blob: e48b321a2776ce163e527b296c34e6e74b9985cf [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.eclipse.adt.internal.editors;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
/**
* Quickly parses a (potential) XML file to extract its first element (i.e. the root element)
* and namespace, if any.
* <p/>
* This is used to determine if a file is an XML document that the XmlEditor can process.
* <p/>
* TODO use this to remove the hardcoded "android" namespace prefix limitation.
*/
public final class FirstElementParser {
private static SAXParserFactory sSaxfactory;
/**
* Result from the XML parsing. <br/>
* Contains the name of the root XML element. <br/>
* If an XMLNS URI was specified and found, the XMLNS prefix is recorded. Otherwise it is null.
*/
public static final class Result {
private String mElement;
private String mXmlnsPrefix;
private String mXmlnsUri;
public String getElement() {
return mElement;
}
public String getXmlnsPrefix() {
return mXmlnsPrefix;
}
public String getXmlnsUri() {
return mXmlnsUri;
}
void setElement(String element) {
mElement = element;
}
void setXmlnsPrefix(String xmlnsPrefix) {
mXmlnsPrefix = xmlnsPrefix;
}
void setXmlnsUri(String xmlnsUri) {
mXmlnsUri = xmlnsUri;
}
}
private static class ResultFoundException extends SAXException { }
/**
* Parses the given filename.
*
* @param osFilename The file to parse.
* @param xmlnsUri An optional URL of which we want to know the prefix.
* @return The element details found or null if not found.
*/
public static Result parse(String osFilename, String xmlnsUri) {
if (sSaxfactory == null) {
// TODO just create a single factory in CommonPlugin and reuse it
sSaxfactory = SAXParserFactory.newInstance();
sSaxfactory.setNamespaceAware(true);
}
Result result = new Result();
if (xmlnsUri != null && xmlnsUri.length() > 0) {
result.setXmlnsUri(xmlnsUri);
}
try {
SAXParser parser = sSaxfactory.newSAXParser();
XmlHandler handler = new XmlHandler(result);
parser.parse(new InputSource(new FileReader(osFilename)), handler);
} catch(ResultFoundException e) {
// XML handling was aborted because the required element was found.
// Simply return the result.
return result;
} catch (ParserConfigurationException e) {
} catch (SAXException e) {
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
return null;
}
/**
* Private constructor. Use the static parse() method instead.
*/
private FirstElementParser() {
// pass
}
/**
* A specialized SAX handler that captures the arguments of the very first element
* (i.e. the root element)
*/
private static class XmlHandler extends DefaultHandler {
private final Result mResult;
public XmlHandler(Result result) {
mResult = result;
}
/**
* Processes a namespace prefix mapping.
* I.e. for xmlns:android="some-uri", this received prefix="android" and uri="some-uri".
* <p/>
* The prefix is recorded in the result structure if the URI is the one searched for.
* <p/>
* This event happens <em>before</em> the corresponding startElement event.
*/
@Override
public void startPrefixMapping(String prefix, String uri) {
if (uri.equals(mResult.getXmlnsUri())) {
mResult.setXmlnsPrefix(prefix);
}
}
/**
* Processes a new element start.
* <p/>
* This simply records the element name and abort processing by throwing an exception.
*/
@Override
public void startElement(String uri, String localName, String name, Attributes attributes)
throws SAXException {
mResult.setElement(localName);
throw new ResultFoundException();
}
}
}