118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu/* 218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Copyright (C) 2012 The Android Open Source Project 318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * 418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License"); 518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * you may not use this file except in compliance with the License. 618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * You may obtain a copy of the License at 718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * 818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * http://www.apache.org/licenses/LICENSE-2.0 918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * 1018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Unless required by applicable law or agreed to in writing, software 1118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS, 1218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * See the License for the specific language governing permissions and 1418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * limitations under the License. 1518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 1618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 1718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhupackage com.android.uiautomator.core; 1818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 1918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.os.Environment; 2018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.os.SystemClock; 2118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.util.Log; 2218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.util.Xml; 2318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo; 2418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 2518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport org.xmlpull.v1.XmlSerializer; 2618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 2718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.io.File; 2818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.io.FileWriter; 2918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.io.IOException; 3018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.io.StringWriter; 3118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 3218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu/** 3318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * 3418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @hide 3518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 3618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhupublic class AccessibilityNodeInfoDumper { 3718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 3818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static final String LOGTAG = AccessibilityNodeInfoDumper.class.getSimpleName(); 3918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static final String[] NAF_EXCLUDED_CLASSES = new String[] { 4018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu android.widget.GridView.class.getName(), android.widget.GridLayout.class.getName(), 4118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu android.widget.ListView.class.getName(), android.widget.TableLayout.class.getName() 4218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu }; 4318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 4418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 4518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Using {@link AccessibilityNodeInfo} this method will walk the layout hierarchy 4618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * and generates an xml dump into the /data/local/window_dump.xml 4718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param root The root accessibility node. 4818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param rotation The rotaion of current display 4918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param width The pixel width of current display 5018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param height The pixel height of current display 5118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 5218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public static void dumpWindowToFile(AccessibilityNodeInfo root, int rotation, 5318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu int width, int height) { 5418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu File baseDir = new File(Environment.getDataDirectory(), "local"); 5518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (!baseDir.exists()) { 5618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu baseDir.mkdir(); 5718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu baseDir.setExecutable(true, false); 5818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu baseDir.setWritable(true, false); 5918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu baseDir.setReadable(true, false); 6018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 6118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu dumpWindowToFile(root, 6218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu new File(new File(Environment.getDataDirectory(), "local"), "window_dump.xml"), 6318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu rotation, width, height); 6418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 6518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 6618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 6718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Using {@link AccessibilityNodeInfo} this method will walk the layout hierarchy 6818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * and generates an xml dump to the location specified by <code>dumpFile</code> 6918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param root The root accessibility node. 7018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param dumpFile The file to dump to. 7118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param rotation The rotaion of current display 7218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param width The pixel width of current display 7318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param height The pixel height of current display 7418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 7518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu public static void dumpWindowToFile(AccessibilityNodeInfo root, File dumpFile, int rotation, 7618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu int width, int height) { 7718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (root == null) { 7818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return; 7918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 8018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu final long startTime = SystemClock.uptimeMillis(); 8118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu try { 8218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu FileWriter writer = new FileWriter(dumpFile); 8318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu XmlSerializer serializer = Xml.newSerializer(); 8418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu StringWriter stringWriter = new StringWriter(); 8518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.setOutput(stringWriter); 8618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.startDocument("UTF-8", true); 8718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.startTag("", "hierarchy"); 8818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "rotation", Integer.toString(rotation)); 8918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu dumpNodeRec(root, serializer, 0, width, height); 9018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.endTag("", "hierarchy"); 9118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.endDocument(); 9218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu writer.write(stringWriter.toString()); 9318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu writer.close(); 9418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } catch (IOException e) { 9518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.e(LOGTAG, "failed to dump window to file", e); 9618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 9718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu final long endTime = SystemClock.uptimeMillis(); 9818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms"); 9918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 10018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 10118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static void dumpNodeRec(AccessibilityNodeInfo node, XmlSerializer serializer,int index, 10218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu int width, int height) throws IOException { 10318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.startTag("", "node"); 10418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (!nafExcludedClass(node) && !nafCheck(node)) 10518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "NAF", Boolean.toString(true)); 10618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "index", Integer.toString(index)); 10718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "text", safeCharSeqToString(node.getText())); 10818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "resource-id", safeCharSeqToString(node.getViewIdResourceName())); 10918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "class", safeCharSeqToString(node.getClassName())); 11018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "package", safeCharSeqToString(node.getPackageName())); 11118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "content-desc", safeCharSeqToString(node.getContentDescription())); 11218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "checkable", Boolean.toString(node.isCheckable())); 11318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "checked", Boolean.toString(node.isChecked())); 11418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "clickable", Boolean.toString(node.isClickable())); 11518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "enabled", Boolean.toString(node.isEnabled())); 11618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "focusable", Boolean.toString(node.isFocusable())); 11718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "focused", Boolean.toString(node.isFocused())); 11818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "scrollable", Boolean.toString(node.isScrollable())); 11918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "long-clickable", Boolean.toString(node.isLongClickable())); 12018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "password", Boolean.toString(node.isPassword())); 12118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "selected", Boolean.toString(node.isSelected())); 12218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.attribute("", "bounds", AccessibilityNodeInfoHelper.getVisibleBoundsInScreen( 12318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu node, width, height).toShortString()); 12418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu int count = node.getChildCount(); 12518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu for (int i = 0; i < count; i++) { 12618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo child = node.getChild(i); 12718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (child != null) { 12818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (child.isVisibleToUser()) { 12918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu dumpNodeRec(child, serializer, i, width, height); 13018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu child.recycle(); 13118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } else { 13218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.i(LOGTAG, String.format("Skipping invisible child: %s", child.toString())); 13318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 13418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } else { 13518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu Log.i(LOGTAG, String.format("Null child %d/%d, parent: %s", 13618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu i, count, node.toString())); 13718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 13818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 13918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu serializer.endTag("", "node"); 14018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 14118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 14218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 14318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * The list of classes to exclude my not be complete. We're attempting to 14418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * only reduce noise from standard layout classes that may be falsely 14518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * configured to accept clicks and are also enabled. 14618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * 14718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param node 14818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return true if node is excluded. 14918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 15018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static boolean nafExcludedClass(AccessibilityNodeInfo node) { 15118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu String className = safeCharSeqToString(node.getClassName()); 15218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu for(String excludedClassName : NAF_EXCLUDED_CLASSES) { 15318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if(className.endsWith(excludedClassName)) 15418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return true; 15518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 15618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return false; 15718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 15818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 15918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 16018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * We're looking for UI controls that are enabled, clickable but have no 16118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * text nor content-description. Such controls configuration indicate an 16218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * interactive control is present in the UI and is most likely not 16318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * accessibility friendly. We refer to such controls here as NAF controls 16418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * (Not Accessibility Friendly) 16518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * 16618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param node 16718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return false if a node fails the check, true if all is OK 16818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 16918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static boolean nafCheck(AccessibilityNodeInfo node) { 17018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu boolean isNaf = node.isClickable() && node.isEnabled() 17118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu && safeCharSeqToString(node.getContentDescription()).isEmpty() 17218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu && safeCharSeqToString(node.getText()).isEmpty(); 17318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 17418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (!isNaf) 17518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return true; 17618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 17718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // check children since sometimes the containing element is clickable 17818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // and NAF but a child's text or description is available. Will assume 17918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu // such layout as fine. 18018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return childNafCheck(node); 18118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 18218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 18318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /** 18418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * This should be used when it's already determined that the node is NAF and 18518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * a further check of its children is in order. A node maybe a container 18618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * such as LinerLayout and may be set to be clickable but have no text or 18718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * content description but it is counting on one of its children to fulfill 18818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * the requirement for being accessibility friendly by having one or more of 18918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * its children fill the text or content-description. Such a combination is 19018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * considered by this dumper as acceptable for accessibility. 19118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * 19218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @param node 19318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @return false if node fails the check. 19418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 19518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static boolean childNafCheck(AccessibilityNodeInfo node) { 19618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu int childCount = node.getChildCount(); 19718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu for (int x = 0; x < childCount; x++) { 19818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu AccessibilityNodeInfo childNode = node.getChild(x); 19918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 20018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (!safeCharSeqToString(childNode.getContentDescription()).isEmpty() 20118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu || !safeCharSeqToString(childNode.getText()).isEmpty()) 20218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return true; 20318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 20418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (childNafCheck(childNode)) 20518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return true; 20618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 20718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return false; 20818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 20918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 21018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static String safeCharSeqToString(CharSequence cs) { 21118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if (cs == null) 21218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return ""; 21318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu else { 21418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return stripInvalidXMLChars(cs); 21518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 21618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 21718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 21818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu private static String stripInvalidXMLChars(CharSequence cs) { 21918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu StringBuffer ret = new StringBuffer(); 22018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu char ch; 22118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu /* http://www.w3.org/TR/xml11/#charsets 22218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu [#x1-#x8], [#xB-#xC], [#xE-#x1F], [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF], 22318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu [#x1FFFE-#x1FFFF], [#x2FFFE-#x2FFFF], [#x3FFFE-#x3FFFF], 22418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu [#x4FFFE-#x4FFFF], [#x5FFFE-#x5FFFF], [#x6FFFE-#x6FFFF], 22518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu [#x7FFFE-#x7FFFF], [#x8FFFE-#x8FFFF], [#x9FFFE-#x9FFFF], 22618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu [#xAFFFE-#xAFFFF], [#xBFFFE-#xBFFFF], [#xCFFFE-#xCFFFF], 22718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu [#xDFFFE-#xDFFFF], [#xEFFFE-#xEFFFF], [#xFFFFE-#xFFFFF], 22818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu [#x10FFFE-#x10FFFF]. 22918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */ 23018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu for (int i = 0; i < cs.length(); i++) { 23118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu ch = cs.charAt(i); 23218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu 23318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu if((ch >= 0x1 && ch <= 0x8) || (ch >= 0xB && ch <= 0xC) || (ch >= 0xE && ch <= 0x1F) || 23418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu (ch >= 0x7F && ch <= 0x84) || (ch >= 0x86 && ch <= 0x9f) || 23518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu (ch >= 0xFDD0 && ch <= 0xFDDF) || (ch >= 0x1FFFE && ch <= 0x1FFFF) || 23618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu (ch >= 0x2FFFE && ch <= 0x2FFFF) || (ch >= 0x3FFFE && ch <= 0x3FFFF) || 23718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu (ch >= 0x4FFFE && ch <= 0x4FFFF) || (ch >= 0x5FFFE && ch <= 0x5FFFF) || 23818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu (ch >= 0x6FFFE && ch <= 0x6FFFF) || (ch >= 0x7FFFE && ch <= 0x7FFFF) || 23918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu (ch >= 0x8FFFE && ch <= 0x8FFFF) || (ch >= 0x9FFFE && ch <= 0x9FFFF) || 24018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu (ch >= 0xAFFFE && ch <= 0xAFFFF) || (ch >= 0xBFFFE && ch <= 0xBFFFF) || 24118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu (ch >= 0xCFFFE && ch <= 0xCFFFF) || (ch >= 0xDFFFE && ch <= 0xDFFFF) || 24218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu (ch >= 0xEFFFE && ch <= 0xEFFFF) || (ch >= 0xFFFFE && ch <= 0xFFFFF) || 24318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu (ch >= 0x10FFFE && ch <= 0x10FFFF)) 24418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu ret.append("."); 24518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu else 24618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu ret.append(ch); 24718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 24818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu return ret.toString(); 24918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu } 25018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu} 251