• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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.layoutopt.uix.xml;
18 
19 import com.sun.org.apache.xerces.internal.parsers.DOMParser;
20 import com.sun.org.apache.xerces.internal.xni.XMLLocator;
21 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
22 import com.sun.org.apache.xerces.internal.xni.Augmentations;
23 import com.sun.org.apache.xerces.internal.xni.XNIException;
24 import com.sun.org.apache.xerces.internal.xni.QName;
25 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
26 
27 import javax.xml.parsers.DocumentBuilder;
28 import javax.xml.parsers.DocumentBuilderFactory;
29 import javax.xml.parsers.ParserConfigurationException;
30 
31 import org.w3c.dom.Node;
32 import org.w3c.dom.Document;
33 import org.xml.sax.SAXNotRecognizedException;
34 import org.xml.sax.SAXNotSupportedException;
35 import org.xml.sax.SAXException;
36 import org.xml.sax.InputSource;
37 
38 import java.io.InputStream;
39 import java.io.IOException;
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.util.LinkedList;
43 
44 /**
45  * Parses XML documents. This class tries to add meta-data in the resulting DOM
46  * trees to indicate the start and end line numbers of each node.
47  */
48 public class XmlDocumentBuilder {
49     /**
50      * Name of the node user data containing the start line number of the node.
51      *
52      * @see Node#getUserData(String)
53      */
54     public static final String NODE_START_LINE = "startLine";
55 
56     /**
57      * Name of the node user data containing the end line number of the node.
58      *
59      * @see Node#getUserData(String)
60      */
61     public static final String NODE_END_LINE = "endLine";
62 
63     private final DocumentBuilder mBuilder;
64     private boolean mHasLineNumbersSupport;
65 
66     /**
67      * Creates a new XML document builder.
68      */
XmlDocumentBuilder()69     public XmlDocumentBuilder() {
70         try {
71             Class.forName("com.sun.org.apache.xerces.internal.parsers.DOMParser");
72             mHasLineNumbersSupport = true;
73         } catch (ClassNotFoundException e) {
74             // Ignore
75         }
76 
77         if (!mHasLineNumbersSupport) {
78             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
79             try {
80                 mBuilder = factory.newDocumentBuilder();
81             } catch (ParserConfigurationException e) {
82                 throw new IllegalStateException("Could not initialize the XML parser");
83             }
84         } else {
85             mBuilder = null;
86         }
87     }
88 
89     /**
90      * Indicates whether the XML documents created by this class are annotated
91      * with line numbers.
92      *
93      * @return True if the parsed documents contain line numbers meta-data,
94      *         false otherwise.
95      *
96      * @see #NODE_START_LINE
97      * @see #NODE_END_LINE
98      */
isHasLineNumbersSupport()99     public boolean isHasLineNumbersSupport() {
100         return mHasLineNumbersSupport;
101     }
102 
parse(InputStream inputStream)103     public Document parse(InputStream inputStream) throws SAXException, IOException {
104         if (!mHasLineNumbersSupport) {
105             return mBuilder.parse(inputStream);
106         } else {
107             DOMParser parser = new LineNumberDOMParser();
108             parser.parse(new InputSource(inputStream));
109             return parser.getDocument();
110         }
111     }
112 
parse(String content)113     public Document parse(String content) throws SAXException, IOException {
114         if (!mHasLineNumbersSupport) {
115             return mBuilder.parse(content);
116         } else {
117             DOMParser parser = new LineNumberDOMParser();
118             parser.parse(content);
119             return parser.getDocument();
120         }
121     }
122 
parse(File file)123     public Document parse(File file) throws SAXException, IOException {
124         return parse(new FileInputStream(file));
125     }
126 
127     private static class LineNumberDOMParser extends DOMParser {
128         private static final String FEATURE_NODE_EXPANSION =
129                 "http://apache.org/xml/features/dom/defer-node-expansion";
130         private static final String CURRENT_NODE =
131                 "http://apache.org/xml/properties/dom/current-element-node";
132 
133         private XMLLocator mLocator;
134         private LinkedList<Node> mStack = new LinkedList<Node>();
135 
LineNumberDOMParser()136         private LineNumberDOMParser() {
137             try {
138                 setFeature(FEATURE_NODE_EXPANSION, false);
139             } catch (SAXNotRecognizedException e) {
140                 e.printStackTrace();
141             } catch (SAXNotSupportedException e) {
142                 e.printStackTrace();
143             }
144         }
145 
146         @Override
startDocument(XMLLocator xmlLocator, String s, NamespaceContext namespaceContext, Augmentations augmentations)147         public void startDocument(XMLLocator xmlLocator, String s,
148                 NamespaceContext namespaceContext, Augmentations augmentations)
149                 throws XNIException {
150             super.startDocument(xmlLocator, s, namespaceContext, augmentations);
151 
152             mLocator = xmlLocator;
153             mStack.add(setNodeLineNumber(NODE_START_LINE));
154         }
155 
setNodeLineNumber(String tag)156         private Node setNodeLineNumber(String tag) {
157             Node node = null;
158             try {
159                 node = (Node) getProperty(CURRENT_NODE);
160             } catch (SAXNotRecognizedException e) {
161                 e.printStackTrace();
162             } catch (SAXNotSupportedException e) {
163                 e.printStackTrace();
164             }
165 
166             if (node != null) {
167                 node.setUserData(tag, mLocator.getLineNumber(), null);
168             }
169 
170             return node;
171         }
172 
173         @Override
startElement(QName qName, XMLAttributes xmlAttributes, Augmentations augmentations)174         public void startElement(QName qName, XMLAttributes xmlAttributes,
175                 Augmentations augmentations) throws XNIException {
176             super.startElement(qName, xmlAttributes, augmentations);
177             mStack.add(setNodeLineNumber(NODE_START_LINE));
178         }
179 
180         @Override
endElement(QName qName, Augmentations augmentations)181         public void endElement(QName qName, Augmentations augmentations) throws XNIException {
182             super.endElement(qName, augmentations);
183             Node node = mStack.removeLast();
184             if (node != null) {
185                 node.setUserData(NODE_END_LINE, mLocator.getLineNumber(), null);
186             }
187         }
188     }
189 }
190