1e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/*
2e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Copyright (C) 2012 The Android Open Source Project
3e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
4e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License");
5e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * you may not use this file except in compliance with the License.
6e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * You may obtain a copy of the License at
7e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
8e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *      http://www.apache.org/licenses/LICENSE-2.0
9e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
10e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Unless required by applicable law or agreed to in writing, software
11e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS,
12e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * See the License for the specific language governing permissions and
14e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * limitations under the License.
15e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
16e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
17e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupackage com.android.uiautomator.core;
18e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
19e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.Environment;
20e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.SystemClock;
21e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.Log;
22e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.Xml;
23e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo;
24e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
2523296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhuimport org.xmlpull.v1.XmlSerializer;
2623296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu
27e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.io.File;
28e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.io.FileWriter;
29e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.io.IOException;
30e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.io.StringWriter;
31e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
32ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu/**
33ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu *
34ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu * @hide
35ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu */
36e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class AccessibilityNodeInfoDumper {
37e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
38e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String LOGTAG = AccessibilityNodeInfoDumper.class.getSimpleName();
397f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz    private static final String[] NAF_EXCLUDED_CLASSES = new String[] {
406c66df53c880e480c8016ebf846672b49aa10ec8Adam Momtaz            android.widget.GridView.class.getName(), android.widget.GridLayout.class.getName(),
416c66df53c880e480c8016ebf846672b49aa10ec8Adam Momtaz            android.widget.ListView.class.getName(), android.widget.TableLayout.class.getName()
427f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz    };
43e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
44e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Using {@link AccessibilityNodeInfo} this method will walk the layout hierarchy
46e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * and generates an xml dump into the /data/local/window_dump.xml
4723296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu     * @param root The root accessibility node.
4823296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu     * @param rotation The rotaion of current display
4923296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu     * @param width The pixel width of current display
5023296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu     * @param height The pixel height of current display
51e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
5223296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu    public static void dumpWindowToFile(AccessibilityNodeInfo root, int rotation,
5323296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu            int width, int height) {
54e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        File baseDir = new File(Environment.getDataDirectory(), "local");
55e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (!baseDir.exists()) {
56e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            baseDir.mkdir();
57e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            baseDir.setExecutable(true, false);
58e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            baseDir.setWritable(true, false);
59e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            baseDir.setReadable(true, false);
60e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
6123296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu        dumpWindowToFile(root,
6223296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu                new File(new File(Environment.getDataDirectory(), "local"), "window_dump.xml"),
6323296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu                rotation, width, height);
64e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
65e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
66e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
67e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Using {@link AccessibilityNodeInfo} this method will walk the layout hierarchy
68e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * and generates an xml dump to the location specified by <code>dumpFile</code>
691893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @param root The root accessibility node.
701893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @param dumpFile The file to dump to.
7123296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu     * @param rotation The rotaion of current display
7223296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu     * @param width The pixel width of current display
7323296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu     * @param height The pixel height of current display
74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
7523296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu    public static void dumpWindowToFile(AccessibilityNodeInfo root, File dumpFile, int rotation,
7623296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu            int width, int height) {
77e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (root == null) {
78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return;
79e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
80e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        final long startTime = SystemClock.uptimeMillis();
81e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        try {
82e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            FileWriter writer = new FileWriter(dumpFile);
83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            XmlSerializer serializer = Xml.newSerializer();
84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            StringWriter stringWriter = new StringWriter();
85e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            serializer.setOutput(stringWriter);
86e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            serializer.startDocument("UTF-8", true);
87e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            serializer.startTag("", "hierarchy");
8823296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu            serializer.attribute("", "rotation", Integer.toString(rotation));
8923296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu            dumpNodeRec(root, serializer, 0, width, height);
90e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            serializer.endTag("", "hierarchy");
91e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            serializer.endDocument();
92e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            writer.write(stringWriter.toString());
93e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            writer.close();
94e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (IOException e) {
95e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            Log.e(LOGTAG, "failed to dump window to file", e);
96e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
97e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        final long endTime = SystemClock.uptimeMillis();
98e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms");
99e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
100e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
10123296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu    private static void dumpNodeRec(AccessibilityNodeInfo node, XmlSerializer serializer,int index,
10223296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu            int width, int height) throws IOException {
1036c66df53c880e480c8016ebf846672b49aa10ec8Adam Momtaz        serializer.startTag("", "node");
1044f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        if (!nafExcludedClass(node) && !nafCheck(node))
1054f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz            serializer.attribute("", "NAF", Boolean.toString(true));
106e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "index", Integer.toString(index));
1074f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        serializer.attribute("", "text", safeCharSeqToString(node.getText()));
10856a7ae5d4fb91af7a8f4fb4ebbb778073be6feb3Svetoslav        serializer.attribute("", "resource-id", safeCharSeqToString(node.getViewIdResourceName()));
109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "class", safeCharSeqToString(node.getClassName()));
110e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "package", safeCharSeqToString(node.getPackageName()));
1114f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        serializer.attribute("", "content-desc", safeCharSeqToString(node.getContentDescription()));
112e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "checkable", Boolean.toString(node.isCheckable()));
113e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "checked", Boolean.toString(node.isChecked()));
1144f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        serializer.attribute("", "clickable", Boolean.toString(node.isClickable()));
1154f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        serializer.attribute("", "enabled", Boolean.toString(node.isEnabled()));
116e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "focusable", Boolean.toString(node.isFocusable()));
117e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "focused", Boolean.toString(node.isFocused()));
118e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "scrollable", Boolean.toString(node.isScrollable()));
119e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "long-clickable", Boolean.toString(node.isLongClickable()));
120e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "password", Boolean.toString(node.isPassword()));
121e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.attribute("", "selected", Boolean.toString(node.isSelected()));
12223296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu        serializer.attribute("", "bounds", AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(
12323296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu                node, width, height).toShortString());
124e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int count = node.getChildCount();
125e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        for (int i = 0; i < count; i++) {
126e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            AccessibilityNodeInfo child = node.getChild(i);
127e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (child != null) {
128e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if (child.isVisibleToUser()) {
12923296fc6448cd265fbb45c1fd9041976ae0da274Guang Zhu                    dumpNodeRec(child, serializer, i, width, height);
1307f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz                    child.recycle();
131e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } else {
132e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    Log.i(LOGTAG, String.format("Skipping invisible child: %s", child.toString()));
133e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
134e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            } else {
135e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                Log.i(LOGTAG, String.format("Null child %d/%d, parent: %s",
136e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                        i, count, node.toString()));
137e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
138e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
139e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        serializer.endTag("", "node");
140e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
141e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
142e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1437f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * The list of classes to exclude my not be complete. We're attempting to
1447f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * only reduce noise from standard layout classes that may be falsely
1457f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * configured to accept clicks and are also enabled.
1467f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     *
1471893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @param node
1481893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @return true if node is excluded.
149e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1501893caed0ad4e73b0676f206282d490c2d345316Thanh Le    private static boolean nafExcludedClass(AccessibilityNodeInfo node) {
1511893caed0ad4e73b0676f206282d490c2d345316Thanh Le        String className = safeCharSeqToString(node.getClassName());
1527f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz        for(String excludedClassName : NAF_EXCLUDED_CLASSES) {
153e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(className.endsWith(excludedClassName))
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return true;
155e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
156e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
157e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
158e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
1594f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz    /**
1604f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * We're looking for UI controls that are enabled, clickable but have no
1614f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * text nor content-description. Such controls configuration indicate an
1624f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * interactive control is present in the UI and is most likely not
1634f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * accessibility friendly. We refer to such controls here as NAF controls
1644f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * (Not Accessibility Friendly)
1654f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     *
1664f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * @param node
1674f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * @return false if a node fails the check, true if all is OK
1684f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     */
1694f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz    private static boolean nafCheck(AccessibilityNodeInfo node) {
1704f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        boolean isNaf = node.isClickable() && node.isEnabled()
1714f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz                && safeCharSeqToString(node.getContentDescription()).isEmpty()
1724f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz                && safeCharSeqToString(node.getText()).isEmpty();
1734f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz
1744f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        if (!isNaf)
1754f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz            return true;
1764f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz
1774f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        // check children since sometimes the containing element is clickable
1784f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        // and NAF but a child's text or description is available. Will assume
1794f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        // such layout as fine.
1804f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        return childNafCheck(node);
1814f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz    }
1824f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz
1834f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz    /**
1844f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * This should be used when it's already determined that the node is NAF and
1854f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * a further check of its children is in order. A node maybe a container
1864f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * such as LinerLayout and may be set to be clickable but have no text or
1874f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * content description but it is counting on one of its children to fulfill
1884f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * the requirement for being accessibility friendly by having one or more of
1894f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * its children fill the text or content-description. Such a combination is
1904f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * considered by this dumper as acceptable for accessibility.
1914f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     *
1924f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     * @param node
1931893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @return false if node fails the check.
1944f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz     */
1954f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz    private static boolean childNafCheck(AccessibilityNodeInfo node) {
1964f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        int childCount = node.getChildCount();
1974f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        for (int x = 0; x < childCount; x++) {
1984f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz            AccessibilityNodeInfo childNode = node.getChild(x);
1994f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz
2004f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz            if (!safeCharSeqToString(childNode.getContentDescription()).isEmpty()
2014f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz                    || !safeCharSeqToString(childNode.getText()).isEmpty())
2024f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz                return true;
2034f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz
204462d28d56f67e61478026daf92e7a46a034ce25bAdam Momtaz            if (childNafCheck(childNode))
205462d28d56f67e61478026daf92e7a46a034ce25bAdam Momtaz                return true;
2064f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        }
2074f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz        return false;
2084f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz    }
2094f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz
210e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static String safeCharSeqToString(CharSequence cs) {
211e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (cs == null)
212e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return "";
213e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        else {
214e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return stripInvalidXMLChars(cs);
215e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
216e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
217e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
218e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static String stripInvalidXMLChars(CharSequence cs) {
219e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        StringBuffer ret = new StringBuffer();
220e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        char ch;
221e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        /* http://www.w3.org/TR/xml11/#charsets
222e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        [#x1-#x8], [#xB-#xC], [#xE-#x1F], [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF],
223e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        [#x1FFFE-#x1FFFF], [#x2FFFE-#x2FFFF], [#x3FFFE-#x3FFFF],
224e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        [#x4FFFE-#x4FFFF], [#x5FFFE-#x5FFFF], [#x6FFFE-#x6FFFF],
225e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        [#x7FFFE-#x7FFFF], [#x8FFFE-#x8FFFF], [#x9FFFE-#x9FFFF],
226e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        [#xAFFFE-#xAFFFF], [#xBFFFE-#xBFFFF], [#xCFFFE-#xCFFFF],
227e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        [#xDFFFE-#xDFFFF], [#xEFFFE-#xEFFFF], [#xFFFFE-#xFFFFF],
228e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        [#x10FFFE-#x10FFFF].
229e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu         */
230e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        for (int i = 0; i < cs.length(); i++) {
231e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            ch = cs.charAt(i);
232e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
233e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if((ch >= 0x1 && ch <= 0x8) || (ch >= 0xB && ch <= 0xC) || (ch >= 0xE && ch <= 0x1F) ||
234e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    (ch >= 0x7F && ch <= 0x84) || (ch >= 0x86 && ch <= 0x9f) ||
235e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    (ch >= 0xFDD0 && ch <= 0xFDDF) || (ch >= 0x1FFFE && ch <= 0x1FFFF) ||
236e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    (ch >= 0x2FFFE && ch <= 0x2FFFF) || (ch >= 0x3FFFE && ch <= 0x3FFFF) ||
237e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    (ch >= 0x4FFFE && ch <= 0x4FFFF) || (ch >= 0x5FFFE && ch <= 0x5FFFF) ||
238e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    (ch >= 0x6FFFE && ch <= 0x6FFFF) || (ch >= 0x7FFFE && ch <= 0x7FFFF) ||
239e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    (ch >= 0x8FFFE && ch <= 0x8FFFF) || (ch >= 0x9FFFE && ch <= 0x9FFFF) ||
240e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    (ch >= 0xAFFFE && ch <= 0xAFFFF) || (ch >= 0xBFFFE && ch <= 0xBFFFF) ||
241e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    (ch >= 0xCFFFE && ch <= 0xCFFFF) || (ch >= 0xDFFFE && ch <= 0xDFFFF) ||
242e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    (ch >= 0xEFFFE && ch <= 0xEFFFF) || (ch >= 0xFFFFE && ch <= 0xFFFFF) ||
243e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    (ch >= 0x10FFFE && ch <= 0x10FFFF))
244e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                ret.append(".");
245e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            else
246e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                ret.append(ch);
247e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
248e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return ret.toString();
249e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
250e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu}
251