AccessibilityNodeInfoDumper.java revision 4f68d87218ee618670a71adbaaca83d76a2e2d37
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 196a6ac2e1847fd2d2ce389e61ed723d81d6d2d229Guang Zhuimport android.hardware.display.DisplayManagerGlobal; 20e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.Environment; 21e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.SystemClock; 22e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.Log; 23e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.Xml; 246a6ac2e1847fd2d2ce389e61ed723d81d6d2d229Guang Zhuimport android.view.Display; 25e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo; 26e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu 27e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.io.File; 28e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.io.FileWriter; 29e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.io.IOException; 30e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.io.StringWriter; 31e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu 327f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtazimport org.xmlpull.v1.XmlSerializer; 337f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz 34ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu/** 35ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu * 36ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu * @hide 37ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu */ 38e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class AccessibilityNodeInfoDumper { 39e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu 40e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu private static final String LOGTAG = AccessibilityNodeInfoDumper.class.getSimpleName(); 417f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz private static final String[] NAF_EXCLUDED_CLASSES = new String[] { 426c66df53c880e480c8016ebf846672b49aa10ec8Adam Momtaz android.widget.GridView.class.getName(), android.widget.GridLayout.class.getName(), 436c66df53c880e480c8016ebf846672b49aa10ec8Adam Momtaz android.widget.ListView.class.getName(), android.widget.TableLayout.class.getName() 447f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz }; 45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu 46e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu /** 47e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Using {@link AccessibilityNodeInfo} this method will walk the layout hierarchy 48e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * and generates an xml dump into the /data/local/window_dump.xml 49e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * @param info 50e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */ 51e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu public static void dumpWindowToFile(AccessibilityNodeInfo info) { 52e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu File baseDir = new File(Environment.getDataDirectory(), "local"); 53e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu if (!baseDir.exists()) { 54e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu baseDir.mkdir(); 55e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu baseDir.setExecutable(true, false); 56e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu baseDir.setWritable(true, false); 57e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu baseDir.setReadable(true, false); 58e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 59e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu dumpWindowToFile(info, new File( 60e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu new File(Environment.getDataDirectory(), "local"), "window_dump.xml")); 61e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 62e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu 63e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu /** 64e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Using {@link AccessibilityNodeInfo} this method will walk the layout hierarchy 65e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * and generates an xml dump to the location specified by <code>dumpFile</code> 66e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * @param info 67e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */ 68e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu public static void dumpWindowToFile(AccessibilityNodeInfo root, File dumpFile) { 69e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu if (root == null) { 70e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu return; 71e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 72e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu final long startTime = SystemClock.uptimeMillis(); 73e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu try { 74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu FileWriter writer = new FileWriter(dumpFile); 75e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu XmlSerializer serializer = Xml.newSerializer(); 76e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu StringWriter stringWriter = new StringWriter(); 77e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.setOutput(stringWriter); 78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.startDocument("UTF-8", true); 79e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.startTag("", "hierarchy"); 806a6ac2e1847fd2d2ce389e61ed723d81d6d2d229Guang Zhu serializer.attribute("", "rotation", Integer.toString( 816a6ac2e1847fd2d2ce389e61ed723d81d6d2d229Guang Zhu DisplayManagerGlobal.getInstance().getRealDisplay( 826a6ac2e1847fd2d2ce389e61ed723d81d6d2d229Guang Zhu Display.DEFAULT_DISPLAY).getRotation())); 83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu dumpNodeRec(root, serializer, 0); 84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.endTag("", "hierarchy"); 85e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.endDocument(); 86e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu writer.write(stringWriter.toString()); 87e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu writer.close(); 88e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } catch (IOException e) { 89e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu Log.e(LOGTAG, "failed to dump window to file", e); 90e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 91e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu final long endTime = SystemClock.uptimeMillis(); 92e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms"); 93e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 94e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu 954f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz private static void dumpNodeRec(AccessibilityNodeInfo node, XmlSerializer serializer, 966c66df53c880e480c8016ebf846672b49aa10ec8Adam Momtaz int index) throws IOException { 976c66df53c880e480c8016ebf846672b49aa10ec8Adam Momtaz serializer.startTag("", "node"); 984f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz if (!nafExcludedClass(node) && !nafCheck(node)) 994f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz serializer.attribute("", "NAF", Boolean.toString(true)); 100e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "index", Integer.toString(index)); 1014f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz serializer.attribute("", "text", safeCharSeqToString(node.getText())); 102e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "class", safeCharSeqToString(node.getClassName())); 103e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "package", safeCharSeqToString(node.getPackageName())); 1044f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz serializer.attribute("", "content-desc", safeCharSeqToString(node.getContentDescription())); 105e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "checkable", Boolean.toString(node.isCheckable())); 106e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "checked", Boolean.toString(node.isChecked())); 1074f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz serializer.attribute("", "clickable", Boolean.toString(node.isClickable())); 1084f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz serializer.attribute("", "enabled", Boolean.toString(node.isEnabled())); 109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "focusable", Boolean.toString(node.isFocusable())); 110e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "focused", Boolean.toString(node.isFocused())); 111e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "scrollable", Boolean.toString(node.isScrollable())); 112e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "long-clickable", Boolean.toString(node.isLongClickable())); 113e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "password", Boolean.toString(node.isPassword())); 114e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.attribute("", "selected", Boolean.toString(node.isSelected())); 1157f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz serializer.attribute("", "bounds", 1167f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(node).toShortString()); 117e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu int count = node.getChildCount(); 118e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu for (int i = 0; i < count; i++) { 119e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu AccessibilityNodeInfo child = node.getChild(i); 120e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu if (child != null) { 121e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu if (child.isVisibleToUser()) { 1224f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz dumpNodeRec(child, serializer, i); 1237f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz child.recycle(); 124e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } else { 125e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu Log.i(LOGTAG, String.format("Skipping invisible child: %s", child.toString())); 126e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 127e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } else { 128e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu Log.i(LOGTAG, String.format("Null child %d/%d, parent: %s", 129e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu i, count, node.toString())); 130e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 131e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 132e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu serializer.endTag("", "node"); 133e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 134e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu 135e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu /** 1367f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz * The list of classes to exclude my not be complete. We're attempting to 1377f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz * only reduce noise from standard layout classes that may be falsely 1387f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz * configured to accept clicks and are also enabled. 1397f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz * 140e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * @param n 141e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * @return 142e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */ 1437f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz private static boolean nafExcludedClass(AccessibilityNodeInfo n) { 144e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu String className = safeCharSeqToString(n.getClassName()); 1457f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz for(String excludedClassName : NAF_EXCLUDED_CLASSES) { 146e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu if(className.endsWith(excludedClassName)) 147e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu return true; 148e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 149e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu return false; 150e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 151e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu 1524f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz /** 1534f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * We're looking for UI controls that are enabled, clickable but have no 1544f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * text nor content-description. Such controls configuration indicate an 1554f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * interactive control is present in the UI and is most likely not 1564f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * accessibility friendly. We refer to such controls here as NAF controls 1574f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * (Not Accessibility Friendly) 1584f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * 1594f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * @param node 1604f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * @return false if a node fails the check, true if all is OK 1614f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz */ 1624f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz private static boolean nafCheck(AccessibilityNodeInfo node) { 1634f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz boolean isNaf = node.isClickable() && node.isEnabled() 1644f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz && safeCharSeqToString(node.getContentDescription()).isEmpty() 1654f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz && safeCharSeqToString(node.getText()).isEmpty(); 1664f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz 1674f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz if (!isNaf) 1684f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz return true; 1694f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz 1704f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz // check children since sometimes the containing element is clickable 1714f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz // and NAF but a child's text or description is available. Will assume 1724f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz // such layout as fine. 1734f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz return childNafCheck(node); 1744f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz } 1754f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz 1764f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz /** 1774f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * This should be used when it's already determined that the node is NAF and 1784f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * a further check of its children is in order. A node maybe a container 1794f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * such as LinerLayout and may be set to be clickable but have no text or 1804f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * content description but it is counting on one of its children to fulfill 1814f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * the requirement for being accessibility friendly by having one or more of 1824f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * its children fill the text or content-description. Such a combination is 1834f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * considered by this dumper as acceptable for accessibility. 1844f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * 1854f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * @param node 1864f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz * @return 1874f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz */ 1884f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz private static boolean childNafCheck(AccessibilityNodeInfo node) { 1894f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz int childCount = node.getChildCount(); 1904f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz for (int x = 0; x < childCount; x++) { 1914f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz AccessibilityNodeInfo childNode = node.getChild(x); 1924f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz 1934f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz if (!safeCharSeqToString(childNode.getContentDescription()).isEmpty() 1944f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz || !safeCharSeqToString(childNode.getText()).isEmpty()) 1954f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz return true; 1964f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz 1974f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz return childNafCheck(childNode); 1984f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz } 1994f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz return false; 2004f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz } 2014f68d87218ee618670a71adbaaca83d76a2e2d37Adam Momtaz 202e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu private static String safeCharSeqToString(CharSequence cs) { 203e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu if (cs == null) 204e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu return ""; 205e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu else { 206e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu return stripInvalidXMLChars(cs); 207e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 208e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 209e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu 210e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu private static String stripInvalidXMLChars(CharSequence cs) { 211e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu StringBuffer ret = new StringBuffer(); 212e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu char ch; 213e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu /* http://www.w3.org/TR/xml11/#charsets 214e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu [#x1-#x8], [#xB-#xC], [#xE-#x1F], [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF], 215e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu [#x1FFFE-#x1FFFF], [#x2FFFE-#x2FFFF], [#x3FFFE-#x3FFFF], 216e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu [#x4FFFE-#x4FFFF], [#x5FFFE-#x5FFFF], [#x6FFFE-#x6FFFF], 217e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu [#x7FFFE-#x7FFFF], [#x8FFFE-#x8FFFF], [#x9FFFE-#x9FFFF], 218e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu [#xAFFFE-#xAFFFF], [#xBFFFE-#xBFFFF], [#xCFFFE-#xCFFFF], 219e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu [#xDFFFE-#xDFFFF], [#xEFFFE-#xEFFFF], [#xFFFFE-#xFFFFF], 220e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu [#x10FFFE-#x10FFFF]. 221e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */ 222e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu for (int i = 0; i < cs.length(); i++) { 223e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu ch = cs.charAt(i); 224e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu 225e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu if((ch >= 0x1 && ch <= 0x8) || (ch >= 0xB && ch <= 0xC) || (ch >= 0xE && ch <= 0x1F) || 226e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu (ch >= 0x7F && ch <= 0x84) || (ch >= 0x86 && ch <= 0x9f) || 227e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu (ch >= 0xFDD0 && ch <= 0xFDDF) || (ch >= 0x1FFFE && ch <= 0x1FFFF) || 228e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu (ch >= 0x2FFFE && ch <= 0x2FFFF) || (ch >= 0x3FFFE && ch <= 0x3FFFF) || 229e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu (ch >= 0x4FFFE && ch <= 0x4FFFF) || (ch >= 0x5FFFE && ch <= 0x5FFFF) || 230e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu (ch >= 0x6FFFE && ch <= 0x6FFFF) || (ch >= 0x7FFFE && ch <= 0x7FFFF) || 231e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu (ch >= 0x8FFFE && ch <= 0x8FFFF) || (ch >= 0x9FFFE && ch <= 0x9FFFF) || 232e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu (ch >= 0xAFFFE && ch <= 0xAFFFF) || (ch >= 0xBFFFE && ch <= 0xBFFFF) || 233e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu (ch >= 0xCFFFE && ch <= 0xCFFFF) || (ch >= 0xDFFFE && ch <= 0xDFFFF) || 234e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu (ch >= 0xEFFFE && ch <= 0xEFFFF) || (ch >= 0xFFFFE && ch <= 0xFFFFF) || 235e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu (ch >= 0x10FFFE && ch <= 0x10FFFF)) 236e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu ret.append("."); 237e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu else 238e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu ret.append(ch); 239e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 240e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu return ret.toString(); 241e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu } 242e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu} 243