1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.uiautomator.tree; 18 19 import org.eclipse.swt.graphics.Rectangle; 20 import org.xml.sax.Attributes; 21 import org.xml.sax.SAXException; 22 import org.xml.sax.helpers.DefaultHandler; 23 24 import java.io.File; 25 import java.io.IOException; 26 import java.util.ArrayList; 27 import java.util.Collections; 28 import java.util.List; 29 30 import javax.xml.parsers.ParserConfigurationException; 31 import javax.xml.parsers.SAXParser; 32 import javax.xml.parsers.SAXParserFactory; 33 34 public class UiHierarchyXmlLoader { 35 36 private BasicTreeNode mRootNode; 37 private List<Rectangle> mNafNodes; 38 39 public UiHierarchyXmlLoader() { 40 } 41 42 /** 43 * Uses a SAX parser to process XML dump 44 * @param xmlPath 45 * @return 46 */ 47 public BasicTreeNode parseXml(String xmlPath) { 48 mRootNode = null; 49 mNafNodes = new ArrayList<Rectangle>(); 50 // standard boilerplate to get a SAX parser 51 SAXParserFactory factory = SAXParserFactory.newInstance(); 52 SAXParser parser = null; 53 try { 54 parser = factory.newSAXParser(); 55 } catch (ParserConfigurationException e) { 56 e.printStackTrace(); 57 return null; 58 } catch (SAXException e) { 59 e.printStackTrace(); 60 return null; 61 } 62 // handler class for SAX parser to receiver standard parsing events: 63 // e.g. on reading "<foo>", startElement is called, on reading "</foo>", 64 // endElement is called 65 DefaultHandler handler = new DefaultHandler(){ 66 BasicTreeNode mParentNode; 67 BasicTreeNode mWorkingNode; 68 @Override 69 public void startElement(String uri, String localName, String qName, 70 Attributes attributes) throws SAXException { 71 boolean nodeCreated = false; 72 // starting an element implies that the element that has not yet been closed 73 // will be the parent of the element that is being started here 74 mParentNode = mWorkingNode; 75 if ("hierarchy".equals(qName)) { 76 mWorkingNode = new RootWindowNode(attributes.getValue("windowName")); 77 nodeCreated = true; 78 } else if ("node".equals(qName)) { 79 UiNode tmpNode = new UiNode(); 80 for (int i = 0; i < attributes.getLength(); i++) { 81 tmpNode.addAtrribute(attributes.getQName(i), attributes.getValue(i)); 82 } 83 mWorkingNode = tmpNode; 84 nodeCreated = true; 85 // check if current node is NAF 86 String naf = tmpNode.getAttribute("NAF"); 87 if ("true".equals(naf)) { 88 mNafNodes.add(new Rectangle(tmpNode.x, tmpNode.y, 89 tmpNode.width, tmpNode.height)); 90 } 91 } 92 // nodeCreated will be false if the element started is neither 93 // "hierarchy" nor "node" 94 if (nodeCreated) { 95 if (mRootNode == null) { 96 // this will only happen once 97 mRootNode = mWorkingNode; 98 } 99 if (mParentNode != null) { 100 mParentNode.addChild(mWorkingNode); 101 } 102 } 103 } 104 105 @Override 106 public void endElement(String uri, String localName, String qName) throws SAXException { 107 //mParentNode should never be null here in a well formed XML 108 if (mParentNode != null) { 109 // closing an element implies that we are back to working on 110 // the parent node of the element just closed, i.e. continue to 111 // parse more child nodes 112 mWorkingNode = mParentNode; 113 mParentNode = mParentNode.getParent(); 114 } 115 } 116 }; 117 try { 118 parser.parse(new File(xmlPath), handler); 119 } catch (SAXException e) { 120 e.printStackTrace(); 121 return null; 122 } catch (IOException e) { 123 e.printStackTrace(); 124 return null; 125 } 126 return mRootNode; 127 } 128 129 /** 130 * Returns the list of "Not Accessibility Friendly" nodes found during parsing. 131 * 132 * Call this function after parsing 133 * 134 * @return 135 */ 136 public List<Rectangle> getNafNodes() { 137 return Collections.unmodifiableList(mNafNodes); 138 } 139 } 140