| /******************************************************************************* |
| * 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; |
| } |
| } |