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