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