blob: aabc271f9efacfed5833f985e87ccf54354e3f07 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Ant", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.ant.core.xml;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import javax.xml.parsers.*;
import org.apache.ant.core.support.*;
import org.apache.ant.core.execution.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
/**
* Parses the TASK-INF/antlib.xml file of an ant library
* component. An Ant library may contains tasks, apsects and
* other ant plug in components
*/
public class AntLibParser {
public static final String TASK_ELEMENT = "taskdef";
public static final String CONVERTER_ELEMENT = "converter";
public static final String ASPECT_ELEMENT = "aspect";
/**
* The factory used to create SAX parsers.
*/
private SAXParserFactory parserFactory;
/**
* Parse the library definition
*
* @param libSource the URL from where the library XML is read.
*
* @throws SAXParseException if there is a problem parsing the task definitions
*/
public AntLibrary parseAntLibrary(URL libSource, ClassLoader componentLoader)
throws ConfigException {
try {
parserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = parserFactory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
AntLibRootHandler rootHandler = new AntLibRootHandler(libSource, xmlReader, componentLoader);
saxParser.parse(libSource.toString(), rootHandler);
return rootHandler.getAntLibrary();
}
catch (SAXParseException e) {
throw new ConfigException(e.getMessage(), e,
new Location(libSource.toString(),
e.getLineNumber(), e.getColumnNumber()));
}
catch (ParserConfigurationException e) {
throw new ConfigException("Unable to parse Ant library component", e,
new Location(libSource.toString()));
}
catch (SAXException e) {
throw new ConfigException("Unable to parse Ant library component", e,
new Location(libSource.toString()));
}
catch (IOException e) {
throw new ConfigException("Unable to parse Ant library component", e,
new Location(libSource.toString()));
}
}
/**
* The root handler handles the antlib element. An ant lib may
* contain a number of different types of elements
* <ul>
* <li>taskdef</li>
* <li>aspect</li>
* <li>converter</li>
* </ul>
*/
private class AntLibRootHandler extends RootHandler {
static private final int STATE_LOOKING_FOR_ROOT = 1;
static private final int STATE_ROOT_SEEN = 2;
static private final int STATE_FINISHED = 3;
private int state = STATE_LOOKING_FOR_ROOT;
/**
* The AntLibrary that will be defined by parsing the library's definition
* file.
*/
private AntLibrary library = null;
private ClassLoader componentLoader = null;
/**
* Create an Ant Library Root Handler.
*
* @param taskdefSource the URL from where the task definitions exist
* @param reader the XML parser.
*/
public AntLibRootHandler(URL taskdefSource, XMLReader reader, ClassLoader componentLoader) {
super(taskdefSource, reader);
this.componentLoader = componentLoader;
}
/**
* Get the library which has been parsed.
*
* @return an AntLibary with the library definitions
*/
public AntLibrary getAntLibrary() {
return library;
}
/**
* Start a new element in the root. This must be a taskdefs element
* All other elements are invalid.
*
* @param uri The Namespace URI.
* @param localName The local name (without prefix).
* @param qualifiedName The qualified name (with prefix)
* @param attributes The attributes attached to the element.
*
* @throws SAXParseException if there is a parsing problem.
*/
public void startElement(String uri, String localName, String qualifiedName,
Attributes attributes) throws SAXParseException {
switch (state) {
case STATE_LOOKING_FOR_ROOT:
if (qualifiedName.equals("antlib")) {
state = STATE_ROOT_SEEN;
library = new AntLibrary();
}
else {
throw new SAXParseException("An Ant library component must start with an " +
"<antlib> element and not with <" +
qualifiedName + ">", getLocator());
}
break;
case STATE_ROOT_SEEN:
if (qualifiedName.equals(TASK_ELEMENT)) {
createTaskDef(attributes);
}
else if (qualifiedName.equals(CONVERTER_ELEMENT)) {
createConverterDef(attributes);
}
else if (qualifiedName.equals(ASPECT_ELEMENT)) {
createAspectHandler(attributes);
}
else {
throw new SAXParseException("Unrecognized element <" +
qualifiedName + "> in Ant library definition", getLocator());
}
break;
}
}
public void createTaskDef(Attributes attributes) throws SAXParseException {
Set validAttributes = new HashSet();
validAttributes.add("name");
validAttributes.add("classname");
Map attributeValues
= AttributeValidator.validateAttributes(TASK_ELEMENT, attributes,
validAttributes, getLocator());
String taskName = (String)attributeValues.get("name");
String className = (String)attributeValues.get("classname");
if (taskName == null) {
throw new SAXParseException("'name' attribute is required in a <"
+ TASK_ELEMENT + "> element",
getLocator());
}
if (className == null) {
throw new SAXParseException("'classname' attribute is required in a " +
"<" + TASK_ELEMENT + "> element", getLocator());
}
TaskDefinition taskdef = new TaskDefinition(getSourceURL(), taskName, className, componentLoader);
library.addTaskDefinition(taskdef);
}
public void createConverterDef(Attributes attributes) throws SAXParseException {
Set validAttributes = new HashSet();
validAttributes.add("target");
validAttributes.add("classname");
Map attributeValues
= AttributeValidator.validateAttributes(CONVERTER_ELEMENT, attributes,
validAttributes, getLocator());
String targetClassName = (String)attributeValues.get("target");
String className = (String)attributeValues.get("classname");
if (targetClassName == null) {
throw new SAXParseException("'target' attribute is required in a <"
+ CONVERTER_ELEMENT + "> element",
getLocator());
}
if (className == null) {
throw new SAXParseException("'classname' attribute is required in a " +
"<" + CONVERTER_ELEMENT + "> element", getLocator());
}
ConverterDefinition converterDef
= new ConverterDefinition(getSourceURL(), className, targetClassName, componentLoader);
library.addConverterDefinition(converterDef);
}
public void createAspectHandler(Attributes attributes) throws SAXParseException {
Set validAttributes = new HashSet();
validAttributes.add("prefix");
validAttributes.add("classname");
Map attributeValues
= AttributeValidator.validateAttributes(ASPECT_ELEMENT, attributes,
validAttributes, getLocator());
String aspectPrefix = (String)attributeValues.get("prefix");
String aspectClassname = (String)attributeValues.get("classname");
if (aspectPrefix == null) {
throw new SAXParseException("'prefix' attribute is required in a <"
+ ASPECT_ELEMENT + "> element",
getLocator());
}
if (aspectClassname == null) {
throw new SAXParseException("'classname' attribute is required in a " +
"<" + ASPECT_ELEMENT + "> element", getLocator());
}
AspectDefinition aspectDef
= new AspectDefinition(getSourceURL(), aspectPrefix, aspectClassname, componentLoader);
library.addAspectDefinition(aspectDef);
}
public void endElement(String namespaceURI, String localName, String qName) {
if (state == STATE_ROOT_SEEN && qName.equals("antlib")) {
state = STATE_FINISHED;
}
}
}
}