/******************************************************************************* * Copyright (c) 2004, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Philippe Ombredanne (pombredanne@nexb.com) - bug 125367 *******************************************************************************/ package org.eclipse.ant.internal.core.contentDescriber; import java.io.IOException; import java.io.StringReader; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.eclipse.ant.internal.core.IAntCoreConstants; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; /** * An xml event handler for detecting the project top-level element in an Ant buildfile. Also records whether a default attribute is present for the * project and if any target or some other typical ant elements are present. There are still cases where we could ignore a valid ant buildfile though. * * @since 3.1 */ public final class AntHandler extends DefaultHandler { /** * An exception indicating that the parsing should stop. * * @since 3.1 */ private class StopParsingException extends SAXException { /** * All serializable objects should have a stable serialVersionUID */ private static final long serialVersionUID = 1L; /** * Constructs an instance of <code>StopParsingException</code> with a <code>null</code> detail message. */ public StopParsingException() { super((String) null); } } private static final String PROJECT = "project"; //$NON-NLS-1$ private static final String TARGET = "target"; //$NON-NLS-1$ private static final String MACRODEF = "macrodef"; //$NON-NLS-1$ private static final String TASKDEF = "taskdef"; //$NON-NLS-1$ private static final String TYPEDEF = "typedef"; //$NON-NLS-1$ private static final String PROPERTY = "property"; //$NON-NLS-1$ private static final String CLASSPATH = "classpath"; //$NON-NLS-1$ private static final String PATH = "path"; //$NON-NLS-1$ private static final String IMPORT = "import"; //$NON-NLS-1$ /** * This is the name of the top-level element found in the XML file. This member variable is <code>null</code> unless the file has been parsed * successful to the point of finding the top-level element. */ private String fTopElementFound = null; private SAXParserFactory fFactory; private boolean fDefaultAttributeFound = false; private boolean fTargetFound = false; private boolean fAntElementFound = false; private int fLevel = -1; /** * Creates a new SAX parser for use within this instance. * * @return The newly created parser. * @throws ParserConfigurationException * If a parser of the given configuration cannot be created. * @throws SAXException * If something in general goes wrong when creating the parser. */ private final SAXParser createParser(SAXParserFactory parserFactory) throws ParserConfigurationException, SAXException, SAXNotRecognizedException, SAXNotSupportedException { // Initialize the parser. final SAXParser parser = parserFactory.newSAXParser(); final XMLReader reader = parser.getXMLReader(); // disable DTD validation (bug 63625) try { // be sure validation is "off" or the feature to ignore DTD's will not apply reader.setFeature("http://xml.org/sax/features/validation", false); //$NON-NLS-1$ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); //$NON-NLS-1$ } catch (SAXNotRecognizedException e) { // not a big deal if the parser does not recognize the features } catch (SAXNotSupportedException e) { // not a big deal if the parser does not support the features } return parser; } private SAXParserFactory getFactory() { synchronized (this) { if (fFactory != null) { return fFactory; } fFactory = SAXParserFactory.newInstance(); fFactory.setNamespaceAware(true); } return fFactory; } protected boolean parseContents(InputSource contents) throws IOException, ParserConfigurationException, SAXException { // Parse the file into we have what we need (or an error occurs). try { fFactory = getFactory(); if (fFactory == null) { return false; } final SAXParser parser = createParser(fFactory); // to support external entities specified as relative URIs (see bug 63298) contents.setSystemId("/"); //$NON-NLS-1$ parser.parse(contents, this); } catch (StopParsingException e) { // Abort the parsing normally. Fall through... } return true; } /* * Resolve external entity definitions to an empty string. This is to speed up processing of files with external DTDs. Not resolving the contents * of the DTD is ok, as only the System ID of the DTD declaration is used. * * @see org.xml.sax.helpers.DefaultHandler#resolveEntity(java.lang.String, java.lang.String) */ @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException { return new InputSource(new StringReader(IAntCoreConstants.EMPTY_STRING)); } /* * (non-Javadoc) * * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) */ @Override public final void startElement(final String uri, final String elementName, final String qualifiedName, final Attributes attributes) throws SAXException { fLevel++; if (fTopElementFound == null) { fTopElementFound = elementName; if (!hasRootProjectElement()) { throw new StopParsingException(); } if (attributes != null) { fDefaultAttributeFound = attributes.getValue(IAntCoreConstants.DEFAULT) != null; if (fDefaultAttributeFound) { throw new StopParsingException(); } } } if (fLevel == 1 && TARGET.equals(elementName)) { fTargetFound = true; throw new StopParsingException(); } // top level Ant elements if (fLevel == 1 && (MACRODEF.equals(elementName) || TASKDEF.equals(elementName) || TYPEDEF.equals(elementName) || PROPERTY.equals(elementName) || CLASSPATH.equals(elementName) || PATH.equals(elementName) || IMPORT.equals(elementName))) { fAntElementFound = true; throw new StopParsingException(); } } /* * (non-Javadoc) * * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { super.endElement(uri, localName, qName); fLevel--; } protected boolean hasProjectDefaultAttribute() { return fDefaultAttributeFound; } protected boolean hasRootProjectElement() { return PROJECT.equals(fTopElementFound); } protected boolean hasTargetElement() { return fTargetFound; } protected boolean hasAntElement() { return fAntElementFound; } }