diff --git a/src/main/org/apache/tools/ant/ProjectHelper.java b/src/main/org/apache/tools/ant/ProjectHelper.java index 227be5b6f..d3d5ec0f4 100644 --- a/src/main/org/apache/tools/ant/ProjectHelper.java +++ b/src/main/org/apache/tools/ant/ProjectHelper.java @@ -58,59 +58,40 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; import java.util.Locale; -import org.xml.sax.Locator; -import org.xml.sax.InputSource; -import org.xml.sax.HandlerBase; -import org.xml.sax.SAXParseException; -import org.xml.sax.SAXException; -import org.xml.sax.DocumentHandler; -import org.xml.sax.AttributeList; -import org.xml.sax.helpers.XMLReaderAdapter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.ParserConfigurationException; +import org.xml.sax.AttributeList; +import org.apache.tools.ant.helper.ProjectHelperImpl; /** - * Configures a project (complete with targets and tasks) based on - * an XML build file. + * Configures a Project (complete with Targets and Tasks) based on + * a XML build file. It'll rely on a plugin to do the actual processing + * of the xml file. + * + * This class also provide static wrappers for common introspection. + * + * All helper plugins must provide backward compatiblity with the + * original ant patterns, unless a different behavior is explicitely + * specified. For example, if namespace is used on the tag + * the helper can expect the entire build file to be namespace-enabled. + * Namespaces or helper-specific tags can provide meta-information to + * the helper, allowing it to use new ( or different policies ). + * + * However, if no namespace is used the behavior should be exactly + * identical with the default helper. * * @author duncan@x180.com */ - public class ProjectHelper { - /** - * Parser factory to use to create parsers. - * @see #getParserFactory - */ - private static SAXParserFactory parserFactory = null; - - /** - * SAX 1 style parser used to parse the given file. This may - * in fact be a SAX 2 XMLReader wrapped in an XMLReaderAdapter. - */ - private org.xml.sax.Parser parser; - - /** The project to configure. */ - private Project project; - /** The configuration file to parse. */ - private File buildFile; - /** - * Parent directory of the build file. Used for resolving entities - * and setting the project's base directory. - */ - private File buildFileParent; - /** - * Locator for the configuration file parser. - * Used for giving locations of errors etc. - */ - private Locator locator; - /** * Configures the project with the contents of the specified XML file. * @@ -122,1020 +103,166 @@ public class ProjectHelper { * be read */ public static void configureProject(Project project, File buildFile) throws BuildException { - new ProjectHelper(project, buildFile).parse(); + ProjectHelper helper=ProjectHelper.getProjectHelper(); + helper.parse(project, buildFile); } - /** - * Constructs a new helper for the specified project and XML file. - * - * @param project The project for the resulting ProjectHelper to configure. - * Must not be null. - * @param buildFile An XML file giving the project's configuration. - * Must not be null. - */ - private ProjectHelper(Project project, File buildFile) { - this.project = project; - this.buildFile = new File(buildFile.getAbsolutePath()); - buildFileParent = new File(this.buildFile.getParent()); + public ProjectHelper() { } /** * Parses the project file, configuring the project as it goes. - * + * + * @param project The project for the resulting ProjectHelper to configure. + * Must not be null. + * @param source The source for XML configuration. A helper must support + * at least File, for backward compatibility. Helpers may + * support URL, InputStream, etc or specialized types. + * + * @since Ant1.5 * @exception BuildException if the configuration is invalid or cannot * be read */ - private void parse() throws BuildException { - FileInputStream inputStream = null; - InputSource inputSource = null; - - try { - SAXParser saxParser = getParserFactory().newSAXParser(); - try { - parser = saxParser.getParser(); - } catch (SAXException exc) { - parser = new XMLReaderAdapter(saxParser.getXMLReader()); - } - - String uri = "file:" + buildFile.getAbsolutePath().replace('\\', '/'); - for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) { - uri = uri.substring(0, index) + "%23" + uri.substring(index+1); - } - - inputStream = new FileInputStream(buildFile); - inputSource = new InputSource(inputStream); - inputSource.setSystemId(uri); - project.log("parsing buildfile " + buildFile + " with URI = " + uri, Project.MSG_VERBOSE); - HandlerBase hb = new RootHandler(); - parser.setDocumentHandler(hb); - parser.setEntityResolver(hb); - parser.setErrorHandler(hb); - parser.setDTDHandler(hb); - parser.parse(inputSource); - } - catch(ParserConfigurationException exc) { - throw new BuildException("Parser has not been configured correctly", exc); - } - catch(SAXParseException exc) { - Location location = - new Location(buildFile.toString(), exc.getLineNumber(), exc.getColumnNumber()); - - Throwable t = exc.getException(); - if (t instanceof BuildException) { - BuildException be = (BuildException) t; - if (be.getLocation() == Location.UNKNOWN_LOCATION) { - be.setLocation(location); - } - throw be; - } - - throw new BuildException(exc.getMessage(), t, location); - } - catch(SAXException exc) { - Throwable t = exc.getException(); - if (t instanceof BuildException) { - throw (BuildException) t; - } - throw new BuildException(exc.getMessage(), t); - } - catch(FileNotFoundException exc) { - throw new BuildException(exc); - } - catch(IOException exc) { - throw new BuildException("Error reading project file", exc); - } - finally { - if (inputStream != null) { - try { - inputStream.close(); - } - catch (IOException ioe) { - // ignore this - } - } - } + public void parse(Project project, Object source) throws BuildException { + throw new BuildException("ProjectHelper.parse() must be implemented in a helper plugin " + + this.getClass().getName()); } - /** - * The common superclass for all SAX event handlers used to parse - * the configuration file. Each method just throws an exception, - * so subclasses should override what they can handle. - * - * Each type of XML element (task, target, etc.) in Ant has - * a specific subclass. - * - * In the constructor, this class takes over the handling of SAX - * events from the parent handler and returns - * control back to the parent in the endElement method. - */ - private class AbstractHandler extends HandlerBase { - - /** - * Previous handler for the document. - * When the next element is finished, control returns - * to this handler. - */ - protected DocumentHandler parentHandler; - - /** - * Creates a handler and sets the parser to use it - * for the current element. - * - * @param parentHandler The handler which should be restored to the - * parser at the end of the element. - * Must not be null. - */ - public AbstractHandler(DocumentHandler parentHandler) { - this.parentHandler = parentHandler; + /* -------------------- Helper discovery -------------------- */ + public static final String HELPER_PROPERTY = + "org.apache.tools.ant.ProjectHelper"; + + public static final String SERVICE_ID = + "/META-INF/services/org.apache.tools.ant.ProjectHelper"; - // Start handling SAX events - parser.setDocumentHandler(this); - } + + /** Discover a project helper instance. Uses the same patterns + * as JAXP, commons-logging, etc: a system property, a JDK1.3 + * service discovery, default. + */ + public static ProjectHelper getProjectHelper() + throws BuildException + { + // Identify the class loader we will be using. Ant may be + // in a webapp or embeded in a different app + ProjectHelper helper=null; - /** - * Handles the start of an element. This base implementation just - * throws an exception. - * - * @param tag The name of the element being started. - * Will not be null. - * @param attrs Attributes of the element being started. - * Will not be null. - * - * @exception SAXParseException if this method is not overridden, or in - * case of error in an overridden version - */ - public void startElement(String tag, AttributeList attrs) throws SAXParseException { - throw new SAXParseException("Unexpected element \"" + tag + "\"", locator); - } - - /** - * Handles text within an element. This base implementation just - * throws an exception. - * - * @param buf A character array of the text within the element. - * Will not be null. - * @param start The start element in the array. - * @param count The number of characters to read from the array. - * - * @exception SAXParseException if this method is not overridden, or in - * case of error in an overridden version - */ - public void characters(char[] buf, int start, int count) throws SAXParseException { - String s = new String(buf, start, count).trim(); - - if (s.length() > 0) { - throw new SAXParseException("Unexpected text \"" + s + "\"", locator); + // First, try the system property + try { + String helperClass = System.getProperty(HELPER_PROPERTY); + if (helperClass != null) { + helper = newHelper(helperClass); } + } catch (SecurityException e) { + // It's ok, we'll try next option + ; } - /** - * Called when this element and all elements nested into it have been - * handled. - */ - protected void finished() {} - - /** - * Handles the end of an element. Any required clean-up is performed - * by the finished() method and then the original handler is restored to - * the parser. - * - * @param name The name of the element which is ending. - * Will not be null. - * - * @exception SAXException in case of error (not thrown in - * this implementation) - * - * @see #finished() - */ - public void endElement(String name) throws SAXException { - - finished(); - // Let parent resume handling SAX events - parser.setDocumentHandler(parentHandler); - } - } - - /** - * Handler for the root element. Its only child must be the "project" element. - */ - private class RootHandler extends HandlerBase { - - /** - * Resolves file: URIs relative to the build file. - * - * @param publicId The public identifer, or null - * if none is available. Ignored in this - * implementation. - * @param systemId The system identifier provided in the XML - * document. Will not be null. - */ - public InputSource resolveEntity(String publicId, - String systemId) { - - project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE); - - if (systemId.startsWith("file:")) { - String path = systemId.substring(5); - int index = path.indexOf("file:"); - - // we only have to handle these for backward compatibility - // since they are in the FAQ. - while (index != -1) { - path = path.substring(0, index) + path.substring(index + 5); - index = path.indexOf("file:"); + // A JDK1.3 'service' ( like in JAXP ). That will plug a helper + // automatically if in CLASSPATH, with the right META-INF/services. + if( helper==null ) { + try { + ClassLoader classLoader=getContextClassLoader(); + InputStream is=null; + if (classLoader != null) { + is=classLoader.getResourceAsStream( SERVICE_ID ); } - - String entitySystemId = path; - index = path.indexOf("%23"); - // convert these to # - while (index != -1) { - path = path.substring(0, index) + "#" + path.substring(index + 3); - index = path.indexOf("%23"); - } - - File file = new File(path); - if (!file.isAbsolute()) { - file = new File(buildFileParent, path); + if( is==null ) { + is=ClassLoader.getSystemResourceAsStream( SERVICE_ID ); } - try { - InputSource inputSource = new InputSource(new FileInputStream(file)); - inputSource.setSystemId("file:" + entitySystemId); - return inputSource; - } catch (FileNotFoundException fne) { - project.log(file.getAbsolutePath()+" could not be found", - Project.MSG_WARN); - } - } - // use default if not file or file not found - return null; - } - - /** - * Handles the start of a project element. A project handler is created - * and initialised with the element name and attributes. - * - * @param tag The name of the element being started. - * Will not be null. - * @param attrs Attributes of the element being started. - * Will not be null. - * - * @exception SAXParseException if the tag given is not - * "project" - */ - public void startElement(String tag, AttributeList attrs) throws SAXParseException { - if (tag.equals("project")) { - new ProjectHandler(this).init(tag, attrs); - } else { - throw new SAXParseException("Config file is not of expected XML type", locator); - } - } - - /** - * Sets the locator in the project helper for future reference. - * - * @param locator The locator used by the parser. - * Will not be null. - */ - public void setDocumentLocator(Locator locator) { - ProjectHelper.this.locator = locator; - } - } - - /** - * Handler for the top level "project" element. - */ - private class ProjectHandler extends AbstractHandler { - - /** - * Constructor which just delegates to the superconstructor. - * - * @param parentHandler The handler which should be restored to the - * parser at the end of the element. - * Must not be null. - */ - public ProjectHandler(DocumentHandler parentHandler) { - super(parentHandler); - } - - /** - * Initialisation routine called after handler creation - * with the element name and attributes. The attributes which - * this handler can deal with are: "default", - * "name", "id" and "basedir". - * - * @param tag Name of the element which caused this handler - * to be created. Should not be null. - * Ignored in this implementation. - * @param attrs Attributes of the element which caused this - * handler to be created. Must not be null. - * - * @exception SAXParseException if an unexpected attribute is - * encountered or if the "default" attribute - * is missing. - */ - public void init(String tag, AttributeList attrs) throws SAXParseException { - String def = null; - String name = null; - String id = null; - String baseDir = null; - - for (int i = 0; i < attrs.getLength(); i++) { - String key = attrs.getName(i); - String value = attrs.getValue(i); - - if (key.equals("default")) { - def = value; - } else if (key.equals("name")) { - name = value; - } else if (key.equals("id")) { - id = value; - } else if (key.equals("basedir")) { - baseDir = value; - } else { - throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"", locator); - } - } - - if (def == null) { - throw new SAXParseException("The default attribute of project is required", - locator); - } - - - project.setDefaultTarget(def); - - if (name != null) { - project.setName(name); - project.addReference(name, project); - } - - if (id != null) { - project.addReference(id, project); - } - - if (project.getProperty("basedir") != null) { - project.setBasedir(project.getProperty("basedir")); - } else { - if (baseDir == null) { - project.setBasedir(buildFileParent.getAbsolutePath()); - } else { - // check whether the user has specified an absolute path - if ((new File(baseDir)).isAbsolute()) { - project.setBasedir(baseDir); - } else { - project.setBaseDir(project.resolveFile(baseDir, buildFileParent)); + if( is != null ) { + // This code is needed by EBCDIC and other strange systems. + // It's a fix for bugs reported in xerces + BufferedReader rd; + try { + rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); + } catch (java.io.UnsupportedEncodingException e) { + rd = new BufferedReader(new InputStreamReader(is)); + } + + String helperClassName = rd.readLine(); + rd.close(); + + if (helperClassName != null && + ! "".equals(helperClassName)) { + + helper= newHelper( helperClassName ); } } - } - - } - - /** - * Handles the start of a top-level element within the project. An - * appropriate handler is created and initialised with the details - * of the element. - * - * @param tag The name of the element being started. - * Will not be null. - * @param attrs Attributes of the element being started. - * Will not be null. - * - * @exception SAXParseException if the tag given is not - * "taskdef", "typedef", - * "property", "target" - * or a data type definition - */ - public void startElement(String name, AttributeList attrs) throws SAXParseException { - if (name.equals("taskdef")) { - handleTaskdef(name, attrs); - } else if (name.equals("typedef")) { - handleTypedef(name, attrs); - } else if (name.equals("property")) { - handleProperty(name, attrs); - } else if (name.equals("target")) { - handleTarget(name, attrs); - } else if (project.getDataTypeDefinitions().get(name) != null) { - handleDataType(name, attrs); - } else { - throw new SAXParseException("Unexpected element \"" + name + "\"", locator); - } - } - - /** - * Handles a task defintion element by creating a task handler - * and initialising is with the details of the element. - * - * @param tag The name of the element to be handled. - * Will not be null. - * @param attrs Attributes of the element to be handled. - * Will not be null. - * - * @exception SAXParseException if an error occurs when initialising - * the task handler - * - */ - private void handleTaskdef(String name, AttributeList attrs) throws SAXParseException { - (new TaskHandler(this, null, null, null)).init(name, attrs); - } - - /** - * Handles a type defintion element by creating a task handler - * and initialising is with the details of the element. - * - * @param tag The name of the element to be handled. - * Will not be null. - * @param attrs Attributes of the element to be handled. - * Will not be null. - * - * @exception SAXParseException if an error occurs initialising the - * handler - */ - private void handleTypedef(String name, AttributeList attrs) throws SAXParseException { - (new TaskHandler(this, null, null, null)).init(name, attrs); - } - - /** - * Handles a property defintion element by creating a task handler - * and initialising is with the details of the element. - * - * @param tag The name of the element to be handled. - * Will not be null. - * @param attrs Attributes of the element to be handled. - * Will not be null. - * - * @exception SAXParseException if an error occurs initialising - * the handler - */ - private void handleProperty(String name, AttributeList attrs) throws SAXParseException { - (new TaskHandler(this, null, null, null)).init(name, attrs); - } - - /** - * Handles a target defintion element by creating a target handler - * and initialising is with the details of the element. - * - * @param tag The name of the element to be handled. - * Will not be null. - * @param attrs Attributes of the element to be handled. - * Will not be null. - * - * @exception SAXParseException if an error occurs initialising - * the handler - */ - private void handleTarget(String tag, AttributeList attrs) throws SAXParseException { - new TargetHandler(this).init(tag, attrs); - } - /** - * Handles a data type defintion element by creating a data type - * handler and initialising is with the details of the element. - * - * @param tag The name of the element to be handled. - * Will not be null. - * @param attrs Attributes of the element to be handled. - * Will not be null. - * - * @exception SAXParseException if an error occurs initialising - * the handler - */ - private void handleDataType(String name, AttributeList attrs) throws SAXParseException { - new DataTypeHandler(this).init(name, attrs); - } - - } - - /** - * Handler for "target" elements. - */ - private class TargetHandler extends AbstractHandler { - private Target target; - - /** - * Constructor which just delegates to the superconstructor. - * - * @param parentHandler The handler which should be restored to the - * parser at the end of the element. - * Must not be null. - */ - public TargetHandler(DocumentHandler parentHandler) { - super(parentHandler); - } - - /** - * Initialisation routine called after handler creation - * with the element name and attributes. The attributes which - * this handler can deal with are: "name", - * "depends", "if", - * "unless", "id" and - * "description". - * - * @param tag Name of the element which caused this handler - * to be created. Should not be null. - * Ignored in this implementation. - * @param attrs Attributes of the element which caused this - * handler to be created. Must not be null. - * - * @exception SAXParseException if an unexpected attribute is encountered - * or if the "name" attribute is missing. - */ - public void init(String tag, AttributeList attrs) throws SAXParseException { - String name = null; - String depends = ""; - String ifCond = null; - String unlessCond = null; - String id = null; - String description = null; - - for (int i = 0; i < attrs.getLength(); i++) { - String key = attrs.getName(i); - String value = attrs.getValue(i); - - if (key.equals("name")) { - name = value; - } else if (key.equals("depends")) { - depends = value; - } else if (key.equals("if")) { - ifCond = value; - } else if (key.equals("unless")) { - unlessCond = value; - } else if (key.equals("id")) { - id = value; - } else if (key.equals("description")) { - description = value; - } else { - throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator); - } - } - - if (name == null) { - throw new SAXParseException("target element appears without a name attribute", locator); - } - - target = new Target(); - target.setName(name); - target.setIf(ifCond); - target.setUnless(unlessCond); - target.setDescription(description); - project.addTarget(name, target); - - if (id != null && !id.equals("")) { - project.addReference(id, target); - } - - // take care of dependencies - - if (depends.length() > 0) { - target.setDepends(depends); + } catch( Exception ex ) { + ; } } - /** - * Handles the start of an element within a target. - * - * @param tag The name of the element being started. - * Will not be null. - * @param attrs Attributes of the element being started. - * Will not be null. - * - * @exception SAXParseException if an error occurs when initialising - * the appropriate child handler - */ - public void startElement(String name, AttributeList attrs) throws SAXParseException { - if (project.getDataTypeDefinitions().get(name) != null) { - new DataTypeHandler(this, target).init(name, attrs); - } else { - new TaskHandler(this, target, null, target).init(name, attrs); - } + if( helper!=null ) { + return helper; + } else { + // Default + return new ProjectHelperImpl(); } } - /** - * Handler for all task elements. + /** Create a new helper. It'll first try the thread class loader, + * then Class.forName() will load from the same loader that + * loaded this class. */ - private class TaskHandler extends AbstractHandler { - /** Containing target, if any. */ - private Target target; - /** - * Container for the task, if any. If target is - * non-null, this must be too. - */ - private TaskContainer container; - /** - * Task created by this handler. - */ - private Task task; - /** - * Wrapper for the parent element, if any. The wrapper for this - * element will be added to this wrapper as a child. - */ - private RuntimeConfigurable parentWrapper; - /** - * Wrapper for this element which takes care of actually configuring - * the element, if this element is contained within a target. - * Otherwise the configuration is performed with the configure method. - * @see ProjectHelper#configure(Object,AttributeList,Project) - */ - private RuntimeConfigurable wrapper = null; - - /** - * Constructor. - * - * @param parentHandler The handler which should be restored to the - * parser at the end of the element. - * Must not be null. - * - * @param container Container for the element. - * May be null if the target is - * null as well. If the - * target is null, this parameter - * is effectively ignored. - * - * @param parentWrapper Wrapper for the parent element, if any. - * May be null. If the - * target is null, this parameter - * is effectively ignored. - * - * @param target Target this element is part of. - * May be null. - */ - public TaskHandler(DocumentHandler parentHandler, TaskContainer container, RuntimeConfigurable parentWrapper, Target target) { - super(parentHandler); - this.container = container; - this.parentWrapper = parentWrapper; - this.target = target; - } - - /** - * Initialisation routine called after handler creation - * with the element name and attributes. This configures - * the element with its attributes and sets it up with - * its parent container (if any). Nested elements are then - * added later as the parser encounters them. - * - * @param tag Name of the element which caused this handler - * to be created. Must not be null. - * - * @param attrs Attributes of the element which caused this - * handler to be created. Must not be null. - * - * @exception SAXParseException in case of error (not thrown in - * this implementation) - */ - public void init(String tag, AttributeList attrs) throws SAXParseException { - try { - task = project.createTask(tag); - } catch (BuildException e) { - // swallow here, will be thrown again in - // UnknownElement.maybeConfigure if the problem persists. - } - - if (task == null) { - task = new UnknownElement(tag); - task.setProject(project); - task.setTaskType(tag); - task.setTaskName(tag); - } - - task.setLocation(new Location(buildFile.toString(), locator.getLineNumber(), locator.getColumnNumber())); - configureId(task, attrs); - - // Top level tasks don't have associated targets - if (target != null) { - task.setOwningTarget(target); - container.addTask(task); - task.init(); - wrapper = task.getRuntimeConfigurableWrapper(); - wrapper.setAttributes(attrs); - if (parentWrapper != null) { - parentWrapper.addChild(wrapper); - } - } else { - task.init(); - configure(task, attrs, project); - } - } - - /** - * Executes the task if it is a top-level one. - */ - protected void finished() { - if (task != null && target == null) { - task.execute(); - } - } - - /** - * Adds text to the task, using the wrapper if one is - * available (in other words if the task is within a target) - * or using addText otherwise. - * - * @param buf A character array of the text within the element. - * Will not be null. - * @param start The start element in the array. - * @param count The number of characters to read from the array. - * - * @exception SAXParseException if the element doesn't support text - * - * @see ProjectHelper#addText(Project,Object,char[],int,int) - */ - public void characters(char[] buf, int start, int count) throws SAXParseException { - if (wrapper == null) { + private static ProjectHelper newHelper(String helperClass) + throws BuildException + { + ClassLoader classLoader = getContextClassLoader(); + try { + Class clazz = null; + if (classLoader != null) { try { - addText(project, task, buf, start, count); - } catch (BuildException exc) { - throw new SAXParseException(exc.getMessage(), locator, exc); + clazz = classLoader.loadClass(helperClass); + } catch( ClassNotFoundException ex ) { + // try next method } - } else { - wrapper.addText(buf, start, count); } - } - - /** - * Handles the start of an element within a target. Task containers - * will always use another task handler, and all other tasks - * will always use a nested element handler. - * - * @param tag The name of the element being started. - * Will not be null. - * @param attrs Attributes of the element being started. - * Will not be null. - * - * @exception SAXParseException if an error occurs when initialising - * the appropriate child handler - */ - public void startElement(String name, AttributeList attrs) throws SAXParseException { - if (task instanceof TaskContainer) { - // task can contain other tasks - no other nested elements possible - new TaskHandler(this, (TaskContainer)task, wrapper, target).init(name, attrs); - } - else { - new NestedElementHandler(this, task, wrapper, target).init(name, attrs); + if( clazz==null ) { + clazz = Class.forName(helperClass); } + return ((ProjectHelper) clazz.newInstance()); + } catch (Exception e) { + throw new BuildException(e); } } /** - * Handler for all nested properties. + * JDK1.1 compatible access to the context class loader. + * Cut&paste from Jaxp. */ - private class NestedElementHandler extends AbstractHandler { - /** Parent object (task/data type/etc). */ - private Object parent; - /** The nested element itself. */ - private Object child; - /** - * Wrapper for the parent element, if any. The wrapper for this - * element will be added to this wrapper as a child. - */ - private RuntimeConfigurable parentWrapper; - /** - * Wrapper for this element which takes care of actually configuring - * the element, if a parent wrapper is provided. - * Otherwise the configuration is performed with the configure method. - * @see ProjectHelper#configure(Object,AttributeList,Project) - */ - private RuntimeConfigurable childWrapper = null; - /** Target this element is part of, if any. */ - private Target target; - - /** - * Constructor. - * - * @param parentHandler The handler which should be restored to the - * parser at the end of the element. - * Must not be null. - * - * @param parent Parent of this element (task/data type/etc). - * Must not be null. - * - * @param parentWrapper Wrapper for the parent element, if any. - * May be null. - * - * @param target Target this element is part of. - * May be null. - */ - public NestedElementHandler(DocumentHandler parentHandler, - Object parent, - RuntimeConfigurable parentWrapper, - Target target) { - super(parentHandler); - - if (parent instanceof TaskAdapter) { - this.parent = ((TaskAdapter) parent).getProxy(); - } else { - this.parent = parent; - } - this.parentWrapper = parentWrapper; - this.target = target; - } - - /** - * Initialisation routine called after handler creation - * with the element name and attributes. This configures - * the element with its attributes and sets it up with - * its parent container (if any). Nested elements are then - * added later as the parser encounters them. - * - * @param tag Name of the element which caused this handler - * to be created. Must not be null. - * - * @param attrs Attributes of the element which caused this - * handler to be created. Must not be null. - * - * @exception SAXParseException in case of error, such as a - * BuildException being thrown during configuration. - */ - public void init(String propType, AttributeList attrs) throws SAXParseException { - Class parentClass = parent.getClass(); - IntrospectionHelper ih = - IntrospectionHelper.getHelper(parentClass); - - try { - String elementName = propType.toLowerCase(Locale.US); - if (parent instanceof UnknownElement) { - UnknownElement uc = new UnknownElement(elementName); - uc.setProject(project); - ((UnknownElement) parent).addChild(uc); - child = uc; - } else { - child = ih.createElement(project, parent, elementName); - } - - configureId(child, attrs); - - if (parentWrapper != null) { - childWrapper = new RuntimeConfigurable(child, propType); - childWrapper.setAttributes(attrs); - parentWrapper.addChild(childWrapper); - } else { - configure(child, attrs, project); - ih.storeElement(project, parent, child, elementName); - } - } catch (BuildException exc) { - throw new SAXParseException(exc.getMessage(), locator, exc); - } - } - - /** - * Adds text to the element, using the wrapper if one is - * available or using addText otherwise. - * - * @param buf A character array of the text within the element. - * Will not be null. - * @param start The start element in the array. - * @param count The number of characters to read from the array. - * - * @exception SAXParseException if the element doesn't support text - * - * @see ProjectHelper#addText(Project,Object,char[],int,int) - */ - public void characters(char[] buf, int start, int count) throws SAXParseException { - if (parentWrapper == null) { - try { - addText(project, child, buf, start, count); - } catch (BuildException exc) { - throw new SAXParseException(exc.getMessage(), locator, exc); - } - } else { - childWrapper.addText(buf, start, count); - } + public static ClassLoader getContextClassLoader() + throws BuildException + { + // Are we running on a JDK 1.2 or later system? + Method method = null; + try { + method = Thread.class.getMethod("getContextClassLoader", null); + } catch (NoSuchMethodException e) { + // we are running on JDK 1.1 + return null; } - /** - * Handles the start of an element within this one. Task containers - * will always use a task handler, and all other elements - * will always use another nested element handler. - * - * @param tag The name of the element being started. - * Will not be null. - * @param attrs Attributes of the element being started. - * Will not be null. - * - * @exception SAXParseException if an error occurs when initialising - * the appropriate child handler - */ - public void startElement(String name, AttributeList attrs) throws SAXParseException { - if (child instanceof TaskContainer) { - // taskcontainer nested element can contain other tasks - no other - // nested elements possible - new TaskHandler(this, (TaskContainer)child, childWrapper, target).init(name, attrs); - } - else { - new NestedElementHandler(this, child, childWrapper, target).init(name, attrs); - } - } + // Get the thread context class loader (if there is one) + ClassLoader classLoader = null; + try { + classLoader = (ClassLoader) + method.invoke(Thread.currentThread(), null); + } catch (IllegalAccessException e) { + throw new BuildException + ("Unexpected IllegalAccessException", e); + } catch (InvocationTargetException e) { + throw new BuildException + ("Unexpected InvocationTargetException", e); + } + + // Return the selected class loader + return (classLoader); } - /** - * Handler for all data types directly subordinate to project or target. - */ - private class DataTypeHandler extends AbstractHandler { - /** Parent target, if any. */ - private Target target; - /** The element being configured. */ - private Object element; - /** Wrapper for this element, if it's part of a target. */ - private RuntimeConfigurable wrapper = null; - - /** - * Constructor with no target specified. - * - * @param parentHandler The handler which should be restored to the - * parser at the end of the element. - * Must not be null. - */ - public DataTypeHandler(DocumentHandler parentHandler) { - this(parentHandler, null); - } - - /** - * Constructor with a target specified. - * - * @param parentHandler The handler which should be restored to the - * parser at the end of the element. - * Must not be null. - * - * @param target The parent target of this element. - * May be null. - */ - public DataTypeHandler(DocumentHandler parentHandler, Target target) { - super(parentHandler); - this.target = target; - } - - /** - * Initialisation routine called after handler creation - * with the element name and attributes. This configures - * the element with its attributes and sets it up with - * its parent container (if any). Nested elements are then - * added later as the parser encounters them. - * - * @param tag Name of the element which caused this handler - * to be created. Must not be null. - * - * @param attrs Attributes of the element which caused this - * handler to be created. Must not be null. - * - * @exception SAXParseException in case of error, such as a - * BuildException being thrown during configuration. - */ - public void init(String propType, AttributeList attrs) throws SAXParseException { - try { - element = project.createDataType(propType); - if (element == null) { - throw new BuildException("Unknown data type "+propType); - } - - if (target != null) { - wrapper = new RuntimeConfigurable(element, propType); - wrapper.setAttributes(attrs); - target.addDataType(wrapper); - } else { - configure(element, attrs, project); - configureId(element, attrs); - } - } catch (BuildException exc) { - throw new SAXParseException(exc.getMessage(), locator, exc); - } - } - - // XXX: (Jon Skeet) Any reason why this doesn't use the wrapper - // if one is available, whereas NestedElementHandler.characters does? - /** - * Adds text to the element. - * - * @param buf A character array of the text within the element. - * Will not be null. - * @param start The start element in the array. - * @param count The number of characters to read from the array. - * - * @exception SAXParseException if the element doesn't support text - * - * @see ProjectHelper#addText(Project,Object,char[],int,int) - */ - public void characters(char[] buf, int start, int count) throws SAXParseException { - try { - addText(project, element, buf, start, count); - } catch (BuildException exc) { - throw new SAXParseException(exc.getMessage(), locator, exc); - } - } - - /** - * Handles the start of an element within this one. - * This will always use a nested element handler. - * - * @param tag The name of the element being started. - * Will not be null. - * @param attrs Attributes of the element being started. - * Will not be null. - * - * @exception SAXParseException if an error occurs when initialising - * the child handler - */ - public void startElement(String name, AttributeList attrs) throws SAXParseException { - new NestedElementHandler(this, element, wrapper, target).init(name, attrs); - } - } + // -------------------- Static utils, used by most helpers -------------------- /** * Configures an object using an introspection handler. @@ -1359,36 +486,4 @@ public class ProjectHelper { fragments.addElement(value.substring(prev)); } } - - /** - * Returns the parser factory to use. Only one parser - * factory is ever created by this method (multi-threading - * issues aside) and is then cached for future use. - * - * @return a SAXParserFactory to use within this class - */ - private static SAXParserFactory getParserFactory() { - if (parserFactory == null) { - parserFactory = SAXParserFactory.newInstance(); - } - - return parserFactory; - } - - /** - * Scans an attribute list for the id attribute and - * stores a reference to the target object in the project if an - * id is found. - *

- * This method was moved out of the configure method to allow - * it to be executed at parse time. - * - * @see #configure(Object,AttributeList,Project) - */ - private void configureId(Object target, AttributeList attr) { - String id = attr.getValue("id"); - if (id != null) { - project.addReference(id, target); - } - } }