• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
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.ide.eclipse.adt.internal.editors;
18 
19 import org.xml.sax.Attributes;
20 import org.xml.sax.InputSource;
21 import org.xml.sax.SAXException;
22 import org.xml.sax.helpers.DefaultHandler;
23 
24 import java.io.FileNotFoundException;
25 import java.io.FileReader;
26 import java.io.IOException;
27 
28 import javax.xml.parsers.ParserConfigurationException;
29 import javax.xml.parsers.SAXParser;
30 import javax.xml.parsers.SAXParserFactory;
31 
32 /**
33  * Quickly parses a (potential) XML file to extract its first element (i.e. the root element)
34  * and namespace, if any.
35  * <p/>
36  * This is used to determine if a file is an XML document that the XmlEditor can process.
37  * <p/>
38  * TODO use this to remove the hardcoded "android" namespace prefix limitation.
39  */
40 public final class FirstElementParser {
41 
42     private static SAXParserFactory sSaxfactory;
43 
44     /**
45      * Result from the XML parsing. <br/>
46      * Contains the name of the root XML element. <br/>
47      * If an XMLNS URI was specified and found, the XMLNS prefix is recorded. Otherwise it is null.
48      */
49     public static final class Result {
50         private String mElement;
51         private String mXmlnsPrefix;
52         private String mXmlnsUri;
53 
getElement()54         public String getElement() {
55             return mElement;
56         }
57 
getXmlnsPrefix()58         public String getXmlnsPrefix() {
59             return mXmlnsPrefix;
60         }
61 
getXmlnsUri()62         public String getXmlnsUri() {
63             return mXmlnsUri;
64         }
65 
setElement(String element)66         void setElement(String element) {
67             mElement = element;
68         }
69 
setXmlnsPrefix(String xmlnsPrefix)70         void setXmlnsPrefix(String xmlnsPrefix) {
71             mXmlnsPrefix = xmlnsPrefix;
72         }
73 
setXmlnsUri(String xmlnsUri)74         void setXmlnsUri(String xmlnsUri) {
75             mXmlnsUri = xmlnsUri;
76         }
77     }
78 
79     private static class ResultFoundException extends SAXException { }
80 
81     /**
82      * Parses the given filename.
83      *
84      * @param osFilename The file to parse.
85      * @param xmlnsUri An optional URL of which we want to know the prefix.
86      * @return The element details found or null if not found.
87      */
parse(String osFilename, String xmlnsUri)88     public static Result parse(String osFilename, String xmlnsUri) {
89         if (sSaxfactory == null) {
90             // TODO just create a single factory in CommonPlugin and reuse it
91             sSaxfactory = SAXParserFactory.newInstance();
92             sSaxfactory.setNamespaceAware(true);
93         }
94 
95         Result result = new Result();
96         if (xmlnsUri != null && xmlnsUri.length() > 0) {
97             result.setXmlnsUri(xmlnsUri);
98         }
99 
100         try {
101             SAXParser parser = sSaxfactory.newSAXParser();
102             XmlHandler handler = new XmlHandler(result);
103             parser.parse(new InputSource(new FileReader(osFilename)), handler);
104 
105         } catch(ResultFoundException e) {
106             // XML handling was aborted because the required element was found.
107             // Simply return the result.
108             return result;
109         } catch (ParserConfigurationException e) {
110         } catch (SAXException e) {
111         } catch (FileNotFoundException e) {
112         } catch (IOException e) {
113         }
114 
115         return null;
116     }
117 
118     /**
119      * Private constructor. Use the static parse() method instead.
120      */
FirstElementParser()121     private FirstElementParser() {
122         // pass
123     }
124 
125     /**
126      * A specialized SAX handler that captures the arguments of the very first element
127      * (i.e. the root element)
128      */
129     private static class XmlHandler extends DefaultHandler {
130         private final Result mResult;
131 
XmlHandler(Result result)132         public XmlHandler(Result result) {
133             mResult = result;
134         }
135 
136         /**
137          * Processes a namespace prefix mapping.
138          * I.e. for xmlns:android="some-uri", this received prefix="android" and uri="some-uri".
139          * <p/>
140          * The prefix is recorded in the result structure if the URI is the one searched for.
141          * <p/>
142          * This event happens <em>before</em> the corresponding startElement event.
143          */
144         @Override
startPrefixMapping(String prefix, String uri)145         public void startPrefixMapping(String prefix, String uri) {
146             if (uri.equals(mResult.getXmlnsUri())) {
147                 mResult.setXmlnsPrefix(prefix);
148             }
149         }
150 
151         /**
152          * Processes a new element start.
153          * <p/>
154          * This simply records the element name and abort processing by throwing an exception.
155          */
156         @Override
startElement(String uri, String localName, String name, Attributes attributes)157         public void startElement(String uri, String localName, String name, Attributes attributes)
158             throws SAXException {
159             mResult.setElement(localName);
160             throw new ResultFoundException();
161         }
162     }
163 
164 }
165