1ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu/* 2ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * Copyright (C) 2012 The Android Open Source Project 3ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * 4ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License"); 5ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * you may not use this file except in compliance with the License. 6ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * You may obtain a copy of the License at 7ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * 8ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * http://www.apache.org/licenses/LICENSE-2.0 9ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * 10ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * Unless required by applicable law or agreed to in writing, software 11ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS, 12ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * See the License for the specific language governing permissions and 14ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * limitations under the License. 15ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu */ 16ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 17ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhupackage com.android.uiautomator.core; 18ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 19ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhuimport android.os.Environment; 20ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhuimport android.os.SystemClock; 21ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhuimport android.util.Log; 22ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhuimport android.util.Xml; 23ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo; 24ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 2547d54d40309083809bf04ba07a5b7191fbca9351Guang Zhuimport org.xmlpull.v1.XmlSerializer; 2647d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu 27ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhuimport java.io.File; 28ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhuimport java.io.FileWriter; 29ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhuimport java.io.IOException; 30ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhuimport java.io.StringWriter; 31ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 325a5e8ca8a086bd87b7753cbd3c46ff1ca72292a2Guang Zhu/** 335a5e8ca8a086bd87b7753cbd3c46ff1ca72292a2Guang Zhu * 345a5e8ca8a086bd87b7753cbd3c46ff1ca72292a2Guang Zhu * @hide 355a5e8ca8a086bd87b7753cbd3c46ff1ca72292a2Guang Zhu */ 36ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhupublic class AccessibilityNodeInfoDumper { 37ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 38ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu private static final String LOGTAG = AccessibilityNodeInfoDumper.class.getSimpleName(); 3973da7f69437c911dc60cc3b9ad7ad876677c8f14Adam Momtaz private static final String[] NAF_EXCLUDED_CLASSES = new String[] { 40171367865fbf1bb9e0abae552da998e47c4cc817Adam Momtaz android.widget.GridView.class.getName(), android.widget.GridLayout.class.getName(), 41171367865fbf1bb9e0abae552da998e47c4cc817Adam Momtaz android.widget.ListView.class.getName(), android.widget.TableLayout.class.getName() 4273da7f69437c911dc60cc3b9ad7ad876677c8f14Adam Momtaz }; 43ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 44ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu /** 45ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * Using {@link AccessibilityNodeInfo} this method will walk the layout hierarchy 46ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu * and generates an xml dump to the location specified by <code>dumpFile</code> 475478840ad89ace2474b9b4d3ab14b92d9e06bf47Thanh Le * @param root The root accessibility node. 485478840ad89ace2474b9b4d3ab14b92d9e06bf47Thanh Le * @param dumpFile The file to dump to. 4947d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu * @param rotation The rotaion of current display 5047d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu * @param width The pixel width of current display 5147d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu * @param height The pixel height of current display 52ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu */ 5347d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu public static void dumpWindowToFile(AccessibilityNodeInfo root, File dumpFile, int rotation, 5447d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu int width, int height) { 55ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu if (root == null) { 56ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu return; 57ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 58ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu final long startTime = SystemClock.uptimeMillis(); 59ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu try { 60ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu FileWriter writer = new FileWriter(dumpFile); 61ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu XmlSerializer serializer = Xml.newSerializer(); 62ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu StringWriter stringWriter = new StringWriter(); 63ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.setOutput(stringWriter); 64ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.startDocument("UTF-8", true); 65ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.startTag("", "hierarchy"); 6647d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu serializer.attribute("", "rotation", Integer.toString(rotation)); 6747d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu dumpNodeRec(root, serializer, 0, width, height); 68ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.endTag("", "hierarchy"); 69ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.endDocument(); 70ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu writer.write(stringWriter.toString()); 71ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu writer.close(); 72ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } catch (IOException e) { 73ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu Log.e(LOGTAG, "failed to dump window to file", e); 74ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 75ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu final long endTime = SystemClock.uptimeMillis(); 76ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms"); 77ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 78ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 7947d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu private static void dumpNodeRec(AccessibilityNodeInfo node, XmlSerializer serializer,int index, 8047d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu int width, int height) throws IOException { 81171367865fbf1bb9e0abae552da998e47c4cc817Adam Momtaz serializer.startTag("", "node"); 8292b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz if (!nafExcludedClass(node) && !nafCheck(node)) 8392b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz serializer.attribute("", "NAF", Boolean.toString(true)); 84ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "index", Integer.toString(index)); 8592b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz serializer.attribute("", "text", safeCharSeqToString(node.getText())); 86f006ec43aebace3eb45a8ea94c2bdedd29cc5416Svetoslav serializer.attribute("", "resource-id", safeCharSeqToString(node.getViewIdResourceName())); 87ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "class", safeCharSeqToString(node.getClassName())); 88ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "package", safeCharSeqToString(node.getPackageName())); 8992b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz serializer.attribute("", "content-desc", safeCharSeqToString(node.getContentDescription())); 90ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "checkable", Boolean.toString(node.isCheckable())); 91ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "checked", Boolean.toString(node.isChecked())); 9292b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz serializer.attribute("", "clickable", Boolean.toString(node.isClickable())); 9392b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz serializer.attribute("", "enabled", Boolean.toString(node.isEnabled())); 94ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "focusable", Boolean.toString(node.isFocusable())); 95ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "focused", Boolean.toString(node.isFocused())); 96ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "scrollable", Boolean.toString(node.isScrollable())); 97ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "long-clickable", Boolean.toString(node.isLongClickable())); 98ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "password", Boolean.toString(node.isPassword())); 99ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.attribute("", "selected", Boolean.toString(node.isSelected())); 10047d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu serializer.attribute("", "bounds", AccessibilityNodeInfoHelper.getVisibleBoundsInScreen( 10147d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu node, width, height).toShortString()); 102ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu int count = node.getChildCount(); 103ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu for (int i = 0; i < count; i++) { 104ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu AccessibilityNodeInfo child = node.getChild(i); 105ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu if (child != null) { 106ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu if (child.isVisibleToUser()) { 10747d54d40309083809bf04ba07a5b7191fbca9351Guang Zhu dumpNodeRec(child, serializer, i, width, height); 10873da7f69437c911dc60cc3b9ad7ad876677c8f14Adam Momtaz child.recycle(); 109ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } else { 110ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu Log.i(LOGTAG, String.format("Skipping invisible child: %s", child.toString())); 111ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 112ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } else { 113ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu Log.i(LOGTAG, String.format("Null child %d/%d, parent: %s", 114ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu i, count, node.toString())); 115ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 116ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 117ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu serializer.endTag("", "node"); 118ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 119ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 120ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu /** 12173da7f69437c911dc60cc3b9ad7ad876677c8f14Adam Momtaz * The list of classes to exclude my not be complete. We're attempting to 12273da7f69437c911dc60cc3b9ad7ad876677c8f14Adam Momtaz * only reduce noise from standard layout classes that may be falsely 12373da7f69437c911dc60cc3b9ad7ad876677c8f14Adam Momtaz * configured to accept clicks and are also enabled. 12473da7f69437c911dc60cc3b9ad7ad876677c8f14Adam Momtaz * 1255478840ad89ace2474b9b4d3ab14b92d9e06bf47Thanh Le * @param node 1265478840ad89ace2474b9b4d3ab14b92d9e06bf47Thanh Le * @return true if node is excluded. 127ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu */ 1285478840ad89ace2474b9b4d3ab14b92d9e06bf47Thanh Le private static boolean nafExcludedClass(AccessibilityNodeInfo node) { 1295478840ad89ace2474b9b4d3ab14b92d9e06bf47Thanh Le String className = safeCharSeqToString(node.getClassName()); 13073da7f69437c911dc60cc3b9ad7ad876677c8f14Adam Momtaz for(String excludedClassName : NAF_EXCLUDED_CLASSES) { 131ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu if(className.endsWith(excludedClassName)) 132ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu return true; 133ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 134ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu return false; 135ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 136ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 13792b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz /** 13892b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * We're looking for UI controls that are enabled, clickable but have no 13992b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * text nor content-description. Such controls configuration indicate an 14092b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * interactive control is present in the UI and is most likely not 14192b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * accessibility friendly. We refer to such controls here as NAF controls 14292b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * (Not Accessibility Friendly) 14392b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * 14492b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * @param node 14592b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * @return false if a node fails the check, true if all is OK 14692b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz */ 14792b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz private static boolean nafCheck(AccessibilityNodeInfo node) { 14892b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz boolean isNaf = node.isClickable() && node.isEnabled() 14992b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz && safeCharSeqToString(node.getContentDescription()).isEmpty() 15092b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz && safeCharSeqToString(node.getText()).isEmpty(); 15192b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz 15292b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz if (!isNaf) 15392b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz return true; 15492b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz 15592b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz // check children since sometimes the containing element is clickable 15692b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz // and NAF but a child's text or description is available. Will assume 15792b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz // such layout as fine. 15892b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz return childNafCheck(node); 15992b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz } 16092b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz 16192b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz /** 16292b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * This should be used when it's already determined that the node is NAF and 16392b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * a further check of its children is in order. A node maybe a container 16492b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * such as LinerLayout and may be set to be clickable but have no text or 16592b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * content description but it is counting on one of its children to fulfill 16692b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * the requirement for being accessibility friendly by having one or more of 16792b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * its children fill the text or content-description. Such a combination is 16892b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * considered by this dumper as acceptable for accessibility. 16992b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * 17092b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz * @param node 1715478840ad89ace2474b9b4d3ab14b92d9e06bf47Thanh Le * @return false if node fails the check. 17292b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz */ 17392b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz private static boolean childNafCheck(AccessibilityNodeInfo node) { 17492b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz int childCount = node.getChildCount(); 17592b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz for (int x = 0; x < childCount; x++) { 17692b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz AccessibilityNodeInfo childNode = node.getChild(x); 17792b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz 17892b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz if (!safeCharSeqToString(childNode.getContentDescription()).isEmpty() 17992b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz || !safeCharSeqToString(childNode.getText()).isEmpty()) 18092b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz return true; 18192b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz 182125a7bcd4966779ab02f21a4ef6ddf88646d8bf6Adam Momtaz if (childNafCheck(childNode)) 183125a7bcd4966779ab02f21a4ef6ddf88646d8bf6Adam Momtaz return true; 18492b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz } 18592b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz return false; 18692b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz } 18792b8580fa210008883791bad05e9b912d73a98a6Adam Momtaz 188ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu private static String safeCharSeqToString(CharSequence cs) { 189ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu if (cs == null) 190ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu return ""; 191ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu else { 192ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu return stripInvalidXMLChars(cs); 193ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 194ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 195ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 196ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu private static String stripInvalidXMLChars(CharSequence cs) { 197ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu StringBuffer ret = new StringBuffer(); 198ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu char ch; 199ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu /* http://www.w3.org/TR/xml11/#charsets 200ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu [#x1-#x8], [#xB-#xC], [#xE-#x1F], [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF], 201ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu [#x1FFFE-#x1FFFF], [#x2FFFE-#x2FFFF], [#x3FFFE-#x3FFFF], 202ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu [#x4FFFE-#x4FFFF], [#x5FFFE-#x5FFFF], [#x6FFFE-#x6FFFF], 203ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu [#x7FFFE-#x7FFFF], [#x8FFFE-#x8FFFF], [#x9FFFE-#x9FFFF], 204ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu [#xAFFFE-#xAFFFF], [#xBFFFE-#xBFFFF], [#xCFFFE-#xCFFFF], 205ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu [#xDFFFE-#xDFFFF], [#xEFFFE-#xEFFFF], [#xFFFFE-#xFFFFF], 206ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu [#x10FFFE-#x10FFFF]. 207ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu */ 208ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu for (int i = 0; i < cs.length(); i++) { 209ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu ch = cs.charAt(i); 210ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu 211ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu if((ch >= 0x1 && ch <= 0x8) || (ch >= 0xB && ch <= 0xC) || (ch >= 0xE && ch <= 0x1F) || 212ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu (ch >= 0x7F && ch <= 0x84) || (ch >= 0x86 && ch <= 0x9f) || 213ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu (ch >= 0xFDD0 && ch <= 0xFDDF) || (ch >= 0x1FFFE && ch <= 0x1FFFF) || 214ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu (ch >= 0x2FFFE && ch <= 0x2FFFF) || (ch >= 0x3FFFE && ch <= 0x3FFFF) || 215ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu (ch >= 0x4FFFE && ch <= 0x4FFFF) || (ch >= 0x5FFFE && ch <= 0x5FFFF) || 216ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu (ch >= 0x6FFFE && ch <= 0x6FFFF) || (ch >= 0x7FFFE && ch <= 0x7FFFF) || 217ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu (ch >= 0x8FFFE && ch <= 0x8FFFF) || (ch >= 0x9FFFE && ch <= 0x9FFFF) || 218ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu (ch >= 0xAFFFE && ch <= 0xAFFFF) || (ch >= 0xBFFFE && ch <= 0xBFFFF) || 219ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu (ch >= 0xCFFFE && ch <= 0xCFFFF) || (ch >= 0xDFFFE && ch <= 0xDFFFF) || 220ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu (ch >= 0xEFFFE && ch <= 0xEFFFF) || (ch >= 0xFFFFE && ch <= 0xFFFFF) || 221ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu (ch >= 0x10FFFE && ch <= 0x10FFFF)) 222ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu ret.append("."); 223ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu else 224ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu ret.append(ch); 225ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 226ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu return ret.toString(); 227ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu } 228ff763316cdb1986d8668ca3011cbb892b43aab93Guang Zhu} 229