From df867b66e89d6dcfa962efed57cbbdf543d8d7bd Mon Sep 17 00:00:00 2001 From: Costin Manolache Date: Tue, 5 Mar 2002 05:50:09 +0000 Subject: [PATCH] Updated ProjectHelper. I used the current sources ( with the new comments), and reimplemented all changes. The current version doesn't have anything related with the other proposals, and is functionally equivalent with the old ProjectHelper - except it's cleaner ( all private and SAX code moved to the helper imple ). I created a new package, o.a.t.ant.helper to keep the core cleaner - other helpers may be commited. The SAX2 based helper is easy to write, but depends on few improvements on RoleFactory ( createTask with namespace support, etc ). I'm working on a new implementation of the task factory, with support for data types and other roles - but that's another story. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271735 13f79535-47bb-0310-9956-ffa450edef68 --- proposal/sandbox/embed/ProjectHelper.java | 221 +++-- proposal/sandbox/embed/ProjectHelperImpl.java | 774 +++++++++++++----- 2 files changed, 731 insertions(+), 264 deletions(-) diff --git a/proposal/sandbox/embed/ProjectHelper.java b/proposal/sandbox/embed/ProjectHelper.java index 66243c20e..92eec91c9 100644 --- a/proposal/sandbox/embed/ProjectHelper.java +++ b/proposal/sandbox/embed/ProjectHelper.java @@ -69,7 +69,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.xml.sax.AttributeList; - +import org.apache.tools.ant.helper.ProjectHelperImpl; /** * Configures a Project (complete with Targets and Tasks) based on @@ -90,48 +90,42 @@ import org.xml.sax.AttributeList; * * @author duncan@x180.com */ -public /*abstract*/ class ProjectHelper { +public class ProjectHelper { /** - * Configures the Project with the contents of the specified XML file. - * ( should it be deprecated ? Using getProjectHelper(), parse() - * is cleaner ) + * Configures the project with the contents of the specified XML file. + * + * @param project The project to configure. Must not be null. + * @param buildFile An XML file giving the project's configuration. + * Must not be null. + * + * @exception BuildException if the configuration is invalid or cannot + * be read */ - public static void configureProject(Project project, File buildFile) - throws BuildException - { + public static void configureProject(Project project, File buildFile) throws BuildException { ProjectHelper helper=ProjectHelper.getProjectHelper(); helper.parse(project, buildFile); } public ProjectHelper() { } - - /** - * Constructs a new Ant parser for the specified XML file. - * @deprecated Use the plugin mechanism instead. - */ - private ProjectHelper(Project project, File buildFile) { - // this.project = project; - // this.buildFile = new File(buildFile.getAbsolutePath()); - // buildFileParent = new File(this.buildFile.getParent()); - } - public Project createProject(ClassLoader coreLoader) { - return new Project(); - } - /** - * Process an input source for the project. + * 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. * - * All processors must support at least File sources. It is usefull to also support - * InputSource - this allows the input to come from a non-filesystem source - * (like input stream of a POST, or a soap body ). + * @since Ant1.5 + * @exception BuildException if the configuration is invalid or cannot + * be read */ - public /*abstract*/ void parse(Project project, Object source) - throws BuildException - { - throw new BuildException("You must use a real ProjectHelper implementation"); + public void parse(Project project, Object source) throws BuildException { + throw new BuildException("ProjectHelper.parse() must be implemented in a helper plugin " + + this.getClass().getName()); } /* -------------------- Helper discovery -------------------- */ @@ -142,21 +136,22 @@ public /*abstract*/ class ProjectHelper { "/META-INF/services/org.apache.tools.ant.ProjectHelper"; - /** Discover a project helper instance. + /** 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 - ClassLoader classLoader = getContextClassLoader(); ProjectHelper helper=null; // First, try the system property try { String helperClass = System.getProperty(HELPER_PROPERTY); if (helperClass != null) { - helper = newHelper(helperClass, classLoader); + helper = newHelper(helperClass); } } catch (SecurityException e) { // It's ok, we'll try next option @@ -167,13 +162,15 @@ public /*abstract*/ class ProjectHelper { // 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.getSystemResourceAsStream( SERVICE_ID ); - } else { + if (classLoader != null) { is=classLoader.getResourceAsStream( SERVICE_ID ); } - + if( is==null ) { + is=ClassLoader.getSystemResourceAsStream( SERVICE_ID ); + } + if( is != null ) { // This code is needed by EBCDIC and other strange systems. // It's a fix for bugs reported in xerces @@ -190,7 +187,7 @@ public /*abstract*/ class ProjectHelper { if (helperClassName != null && ! "".equals(helperClassName)) { - helper= newHelper( helperClassName, classLoader ); + helper= newHelper( helperClassName ); } } } catch( Exception ex ) { @@ -202,17 +199,25 @@ public /*abstract*/ class ProjectHelper { return new ProjectHelperImpl(); } - private static ProjectHelper newHelper(String helperClass, - ClassLoader classLoader) + /** 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 static ProjectHelper newHelper(String helperClass) throws BuildException { - + ClassLoader classLoader = getContextClassLoader(); try { Class clazz = null; - if (classLoader == null) { + if (classLoader != null) { + try { + clazz = classLoader.loadClass(helperClass); + } catch( ClassNotFoundException ex ) { + // try next method + } + } + if( clazz==null ) { clazz = Class.forName(helperClass); - } else { - clazz = classLoader.loadClass(helperClass); } return ((ProjectHelper) clazz.newInstance()); } catch (Exception e) { @@ -220,7 +225,9 @@ public /*abstract*/ class ProjectHelper { } } - /** + /** + * JDK1.1 compatible access to the context class loader. + * Cut&paste from Jaxp. */ public static ClassLoader getContextClassLoader() throws BuildException @@ -251,42 +258,42 @@ public /*abstract*/ class ProjectHelper { return (classLoader); } - - /* -------------------- Common utilities and wrappers -------------------- */ + // -------------------- Static utils, used by most helpers -------------------- - /** Configure a java object using ant's rules. + /** + * Configures an object using an introspection handler. + * + * @param target The target object to be configured. + * Must not be null. + * @param attrs A list of attributes to configure within the target. + * Must not be null. + * @param project The project containing the target. + * Must not be null. + * + * @exception BuildException if any of the attributes can't be handled by + * the target */ public static void configure(Object target, AttributeList attrs, Project project) throws BuildException { - TaskAdapter adapter=null; if( target instanceof TaskAdapter ) { - adapter=(TaskAdapter)target; - target=adapter.getProxy(); + target=((TaskAdapter)target).getProxy(); } IntrospectionHelper ih = IntrospectionHelper.getHelper(target.getClass()); - if( adapter != null ) - adapter.setIntrospectionHelper( ih ); - // XXX What's that ? project.addBuildListener(ih); for (int i = 0; i < attrs.getLength(); i++) { // reflect these into the target String value=replaceProperties(project, attrs.getValue(i), project.getProperties() ); - String name=attrs.getName(i).toLowerCase(Locale.US); try { - if (adapter!=null ) { - adapter.setAttribute( name, value ); - } else { - ih.setAttribute(project, target, - name, value); - } + ih.setAttribute(project, target, + attrs.getName(i).toLowerCase(Locale.US), value); + } catch (BuildException be) { // id attribute must be set externally - // XXX Shuldn't it be 'name' ( i.e. lower-cased ) ? if (!attrs.getName(i).equals("id")) { throw be; } @@ -296,14 +303,35 @@ public /*abstract*/ class ProjectHelper { /** * Adds the content of #PCDATA sections to an element. + * + * @param project The project containing the target. + * Must not be null. + * @param target The target object to be configured. + * Must not be null. + * @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 BuildException if the target object doesn't accept text */ - public static void addText(Project project, Object target, char[] buf, int start, int end) + public static void addText(Project project, Object target, char[] buf, int start, int count) throws BuildException { - addText(project, target, new String(buf, start, end)); + addText(project, target, new String(buf, start, count)); } /** * Adds the content of #PCDATA sections to an element. + * + * @param project The project containing the target. + * Must not be null. + * @param target The target object to be configured. + * Must not be null. + * @param text Text to add to the target. + * May be null, in which case this + * method call is a no-op. + * + * @exception BuildException if the target object doesn't accept text */ public static void addText(Project project, Object target, String text) throws BuildException { @@ -320,7 +348,17 @@ public /*abstract*/ class ProjectHelper { } /** - * Stores a configured child element into its parent object + * Stores a configured child element within its parent object. + * + * @param project Project containing the objects. + * May be null. + * @param parent Parent object to add child to. + * Must not be null. + * @param child Child object to store in parent. + * Should not be null. + * @param tag Name of element which generated the child. + * May be null, in which case + * the child is not stored. */ public static void storeChild(Project project, Object parent, Object child, String tag) { IntrospectionHelper ih = IntrospectionHelper.getHelper(parent.getClass()); @@ -328,10 +366,18 @@ public /*abstract*/ class ProjectHelper { } /** - * Replace ${} style constructions in the given value with the string value of - * the corresponding data types. + * Replaces ${xxx} style constructions in the given value with + * the string value of the corresponding properties. + * + * @param value The string to be scanned for property references. + * May be null. * - * @param value the string to be scanned for property references. + * @exception BuildException if the string contains an opening + * ${ without a closing + * } + * @return the original string with the properties replaced, or + * null if the original string is null. + * * @since 1.5 */ public static String replaceProperties(Project project, String value) @@ -340,10 +386,22 @@ public /*abstract*/ class ProjectHelper { } /** - * Replace ${} style constructions in the given value with the string value of - * the corresponding data types. + * Replaces ${xxx} style constructions in the given value + * with the string value of the corresponding data types. * - * @param value the string to be scanned for property references. + * @param project The container project. This is used solely for + * logging purposes. Must not be null. + * @param value The string to be scanned for property references. + * May be null, in which case this + * method returns immediately with no effect. + * @param keys Mapping (String to String) of property names to their + * values. Must not be null. + * + * @exception BuildException if the string contains an opening + * ${ without a closing + * } + * @return the original string with the properties replaced, or + * null if the original string is null. */ public static String replaceProperties(Project project, String value, Hashtable keys) throws BuildException { @@ -375,11 +433,21 @@ public /*abstract*/ class ProjectHelper { } /** - * This method will parse a string containing ${value} style - * property values into two lists. The first list is a collection - * of text fragments, while the other is a set of string property names - * null entries in the first list indicate a property reference from the - * second list. + * Parses a string containing ${xxx} style property + * references into two lists. The first list is a collection + * of text fragments, while the other is a set of string property names. + * null entries in the first list indicate a property + * reference from the second list. + * + * @param value Text to parse. Must not be null. + * @param fragments List to add text fragments to. + * Must not be null. + * @param propertyRefs List to add property names to. + * Must not be null. + * + * @exception BuildException if the string contains an opening + * ${ without a closing + * } */ public static void parsePropertyString(String value, Vector fragments, Vector propertyRefs) throws BuildException { @@ -414,5 +482,4 @@ public /*abstract*/ class ProjectHelper { fragments.addElement(value.substring(prev)); } } - } diff --git a/proposal/sandbox/embed/ProjectHelperImpl.java b/proposal/sandbox/embed/ProjectHelperImpl.java index a825a687a..221a5a3df 100644 --- a/proposal/sandbox/embed/ProjectHelperImpl.java +++ b/proposal/sandbox/embed/ProjectHelperImpl.java @@ -52,7 +52,9 @@ * . */ -package org.apache.tools.ant; +package org.apache.tools.ant.helper; + +import org.apache.tools.ant.*; import java.io.File; import java.io.FileInputStream; @@ -75,68 +77,57 @@ import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParser; import javax.xml.parsers.ParserConfigurationException; -// Note: Use of local classes can create problems with various compilers -// like gcc or even jikes. In addition it makes the code harder to read -// for many beginners ( at least for me - Costin ). -// I changed the code to avoid the tricks that the compiler does, now -// jikes works ( for me ). Note that declaring the classes 'private' -// is probably overriden by the compiler - a feature of the internal class impl. - /** - * "Original" implementation of the project helper. Or at least - * what is present in ant1.4. + * Original helper. * * @author duncan@x180.com */ public class ProjectHelperImpl extends ProjectHelper { + + /** + * Parser factory to use to create parsers. + * @see #getParserFactory + */ private static SAXParserFactory parserFactory = null; - protected Project project; - protected Object source; - protected File buildFile; - protected File buildFileParent; + /** + * 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; - /** Return a handler for project. This can be used by xml helpers to fallback to - the original behavior ( non-namespace aware ). - When the callback is received, if no namespaces are used ( or no - new attributes, etc ) then the easiest way to achieve backward compatibility - is to use the original. - - A helper needs to call this method, which will switch the HandlerBase in - the SAX parser and return it to the original on - - @experimental This is likely to change - */ - public HandlerBase defaultProjectHandler( Project project, - org.xml.sax.Parser parser, - String tag, AttributeList attrs, - DocumentHandler parent ) - throws SAXParseException - { - this.project=project; - this.parser=parser; - ProjectHandler h=new ProjectHandler(this, parent); - h.init( tag, attrs ); - return h; - } - /** - * Parses the project file. + * Parses the project file, configuring the project as it goes. + * + * @exception BuildException if the configuration is invalid or cannot + * be read */ public void parse(Project project, Object source) throws BuildException { if( ! (source instanceof File) ) - throw new BuildException( "Only File source is supported by the default helper"); - + throw new BuildException( "Only File source supported by default plugin" ); File buildFile=(File)source; + FileInputStream inputStream = null; + InputSource inputSource = null; + this.project = project; this.buildFile = new File(buildFile.getAbsolutePath()); buildFileParent = new File(this.buildFile.getParent()); - FileInputStream inputStream = null; - InputSource inputSource = null; - try { SAXParser saxParser = getParserFactory().newSAXParser(); try { @@ -154,8 +145,7 @@ public class ProjectHelperImpl extends ProjectHelper { inputSource = new InputSource(inputStream); inputSource.setSystemId(uri); project.log("parsing buildfile " + buildFile + " with URI = " + uri, Project.MSG_VERBOSE); - - HandlerBase hb = new RootHandler(this); + HandlerBase hb = new RootHandler(); parser.setDocumentHandler(hb); parser.setEntityResolver(hb); parser.setErrorHandler(hb); @@ -206,38 +196,74 @@ public class ProjectHelperImpl extends ProjectHelper { } /** - * The common superclass for all sax event handlers in Ant. Basically - * throws an exception in each method, so subclasses should override - * what they can handle. + * 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 will - * have its own subclass of AbstractHandler. + * 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 + * 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 static class AbstractHandler extends HandlerBase { + private class AbstractHandler extends HandlerBase { + + /** + * Previous handler for the document. + * When the next element is finished, control returns + * to this handler. + */ protected DocumentHandler parentHandler; - protected ProjectHelperImpl helper; - - public AbstractHandler(ProjectHelperImpl helper, 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; - this.helper=helper; // Start handling SAX events - helper.parser.setDocumentHandler(this); + parser.setDocumentHandler(this); } - + + /** + * 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 + "\"", helper.locator); + throw new SAXParseException("Unexpected element \"" + tag + "\"", locator); } - public void characters(char[] buf, int start, int end) throws SAXParseException { - String s = new String(buf, start, end).trim(); + /** + * 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 + "\"", helper.locator); + throw new SAXParseException("Unexpected text \"" + s + "\"", locator); } } @@ -247,31 +273,45 @@ public class ProjectHelperImpl extends ProjectHelper { */ 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 - helper.parser.setDocumentHandler(parentHandler); + parser.setDocumentHandler(parentHandler); } } /** - * Handler for the root element. It's only child must be the "project" element. + * Handler for the root element. Its only child must be the "project" element. */ - private static class RootHandler extends HandlerBase { - private ProjectHelperImpl helper; + private class RootHandler extends HandlerBase { - public RootHandler( ProjectHelperImpl helper ) { - this.helper=helper; - } - /** - * resolve file: URIs as relative to the build file. + * 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) { - helper.project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE); + project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE); if (systemId.startsWith("file:")) { String path = systemId.substring(5); @@ -294,7 +334,7 @@ public class ProjectHelperImpl extends ProjectHelper { File file = new File(path); if (!file.isAbsolute()) { - file = new File(helper.buildFileParent, path); + file = new File(buildFileParent, path); } try { @@ -302,7 +342,7 @@ public class ProjectHelperImpl extends ProjectHelper { inputSource.setSystemId("file:" + entitySystemId); return inputSource; } catch (FileNotFoundException fne) { - helper.project.log(file.getAbsolutePath()+" could not be found", + project.log(file.getAbsolutePath()+" could not be found", Project.MSG_WARN); } } @@ -310,27 +350,69 @@ public class ProjectHelperImpl extends ProjectHelper { 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(helper, this).init(tag, attrs); + new ProjectHandler(this).init(tag, attrs); } else { - throw new SAXParseException("Config file is not of expected XML type", helper.locator); + 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) { - helper.locator = locator; + ProjectHelperImpl.this.locator = locator; } } /** * Handler for the top level "project" element. */ - private static class ProjectHandler extends AbstractHandler { - public ProjectHandler(ProjectHelperImpl helper, DocumentHandler parentHandler) { - super(helper, parentHandler); + 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; @@ -350,44 +432,59 @@ public class ProjectHelperImpl extends ProjectHelper { } else if (key.equals("basedir")) { baseDir = value; } else { - throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"", helper.locator); + throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"", locator); } } if (def == null) { throw new SAXParseException("The default attribute of project is required", - helper.locator); + locator); } - helper.project.setDefaultTarget(def); + project.setDefaultTarget(def); if (name != null) { - helper.project.setName(name); - helper.project.addReference(name, helper.project); + project.setName(name); + project.addReference(name, project); } if (id != null) { - helper.project.addReference(id, helper.project); + project.addReference(id, project); } - if (helper.project.getProperty("basedir") != null) { - helper.project.setBasedir(helper.project.getProperty("basedir")); + if (project.getProperty("basedir") != null) { + project.setBasedir(project.getProperty("basedir")); } else { if (baseDir == null) { - helper.project.setBasedir(helper.buildFileParent.getAbsolutePath()); + project.setBasedir(buildFileParent.getAbsolutePath()); } else { // check whether the user has specified an absolute path if ((new File(baseDir)).isAbsolute()) { - helper.project.setBasedir(baseDir); + project.setBasedir(baseDir); } else { - helper.project.setBaseDir(helper.project.resolveFile(baseDir, helper.buildFileParent)); + project.setBaseDir(project.resolveFile(baseDir, buildFileParent)); } } } } + /** + * 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); @@ -397,31 +494,91 @@ public class ProjectHelperImpl extends ProjectHelper { handleProperty(name, attrs); } else if (name.equals("target")) { handleTarget(name, attrs); - } else if (helper.project.getDataTypeDefinitions().get(name) != null) { + } else if (project.getDataTypeDefinitions().get(name) != null) { handleDataType(name, attrs); } else { - throw new SAXParseException("Unexpected element \"" + name + "\"", helper.locator); + 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(helper, this, null, null, null)).init(name, attrs); + (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(helper, this, null, null, null)).init(name, attrs); + (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(helper, this, null, null, null)).init(name, attrs); + (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(helper, this).init(tag, attrs); + 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(helper, this).init(name, attrs); + new DataTypeHandler(this).init(name, attrs); } } @@ -429,13 +586,37 @@ public class ProjectHelperImpl extends ProjectHelper { /** * Handler for "target" elements. */ - private static class TargetHandler extends AbstractHandler { + private class TargetHandler extends AbstractHandler { private Target target; - public TargetHandler(ProjectHelperImpl helper, DocumentHandler parentHandler) { - super(helper, parentHandler); + /** + * 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 = ""; @@ -461,12 +642,12 @@ public class ProjectHelperImpl extends ProjectHelper { } else if (key.equals("description")) { description = value; } else { - throw new SAXParseException("Unexpected attribute \"" + key + "\"", helper.locator); + throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator); } } if (name == null) { - throw new SAXParseException("target element appears without a name attribute", helper.locator); + throw new SAXParseException("target element appears without a name attribute", locator); } target = new Target(); @@ -474,10 +655,10 @@ public class ProjectHelperImpl extends ProjectHelper { target.setIf(ifCond); target.setUnless(unlessCond); target.setDescription(description); - helper.project.addTarget(name, target); + project.addTarget(name, target); if (id != null && !id.equals("")) { - helper.project.addReference(id, target); + project.addReference(id, target); } // take care of dependencies @@ -487,11 +668,22 @@ public class ProjectHelperImpl extends ProjectHelper { } } + /** + * 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 (helper.project.getDataTypeDefinitions().get(name) != null) { - new DataTypeHandler(helper, this, target).init(name, attrs); + if (project.getDataTypeDefinitions().get(name) != null) { + new DataTypeHandler(this, target).init(name, attrs); } else { - new TaskHandler(helper, this, target, null, target).init(name, attrs); + new TaskHandler(this, target, null, target).init(name, attrs); } } } @@ -499,28 +691,78 @@ public class ProjectHelperImpl extends ProjectHelper { /** * Handler for all task elements. */ - private static class TaskHandler extends AbstractHandler { + 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; - - public TaskHandler(ProjectHelperImpl helper, - DocumentHandler parentHandler, - TaskContainer container, - RuntimeConfigurable parentWrapper, - Target target) - { - super(helper, parentHandler); + + /** + * 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 = helper.project.createTask(tag); + task = project.createTask(tag); } catch (BuildException e) { // swallow here, will be thrown again in // UnknownElement.maybeConfigure if the problem persists. @@ -528,15 +770,13 @@ public class ProjectHelperImpl extends ProjectHelper { if (task == null) { task = new UnknownElement(tag); - task.setProject(helper.project); - task.setTaskType(tag); + task.setProject(project); + //XXX task.setTaskType(tag); task.setTaskName(tag); } - task.setLocation(new Location(helper.buildFile.toString(), - helper.locator.getLineNumber(), - helper.locator.getColumnNumber())); - helper.configureId(task, attrs); + task.setLocation(new Location(buildFile.toString(), locator.getLineNumber(), locator.getColumnNumber())); + configureId(task, attrs); // Top level tasks don't have associated targets if (target != null) { @@ -550,35 +790,65 @@ public class ProjectHelperImpl extends ProjectHelper { } } else { task.init(); - configure(task, attrs, helper.project); + configure(task, attrs, project); } } + /** + * Executes the task if it is a top-level one. + */ protected void finished() { if (task != null && target == null) { task.execute(); } } - public void characters(char[] buf, int start, int end) throws SAXParseException { + /** + * 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) { try { - addText(helper.project, task, buf, start, end); + addText(project, task, buf, start, count); } catch (BuildException exc) { - throw new SAXParseException(exc.getMessage(), helper.locator, exc); + throw new SAXParseException(exc.getMessage(), locator, exc); } } else { - wrapper.addText(buf, start, end); + 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(helper, this, (TaskContainer)task, wrapper, target).init(name, attrs); + new TaskHandler(this, (TaskContainer)task, wrapper, target).init(name, attrs); } else { - new NestedElementHandler(helper, this, task, wrapper, target).init(name, attrs); + new NestedElementHandler(this, task, wrapper, target).init(name, attrs); } } } @@ -586,24 +856,50 @@ public class ProjectHelperImpl extends ProjectHelper { /** * Handler for all nested properties. */ - private static class NestedElementHandler extends AbstractHandler { + 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; - private TaskAdapter adapter=null; + /** Target this element is part of, if any. */ private Target target; - public NestedElementHandler(ProjectHelperImpl helper, - DocumentHandler parentHandler, + /** + * 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(helper, parentHandler); + super(parentHandler); if (parent instanceof TaskAdapter) { - this.adapter= (TaskAdapter)parent; - this.parent = adapter.getProxy(); + this.parent = ((TaskAdapter) parent).getProxy(); } else { this.parent = parent; } @@ -611,87 +907,159 @@ public class ProjectHelperImpl extends ProjectHelper { 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); - if( adapter!=null ) { - adapter.setIntrospectionHelper( ih ); - } - + try { String elementName = propType.toLowerCase(Locale.US); if (parent instanceof UnknownElement) { UnknownElement uc = new UnknownElement(elementName); - uc.setProject(helper.project); + uc.setProject(project); ((UnknownElement) parent).addChild(uc); child = uc; } else { - child = ih.createElement(helper.project, parent, elementName); + child = ih.createElement(project, parent, elementName); } - helper.configureId(child, attrs); + configureId(child, attrs); if (parentWrapper != null) { childWrapper = new RuntimeConfigurable(child, propType); childWrapper.setAttributes(attrs); parentWrapper.addChild(childWrapper); } else { - configure(child, attrs, helper.project); - ih.storeElement(helper.project, parent, child, elementName); + configure(child, attrs, project); + ih.storeElement(project, parent, child, elementName); } } catch (BuildException exc) { - throw new SAXParseException(exc.getMessage(), helper.locator, exc); + throw new SAXParseException(exc.getMessage(), locator, exc); } } - public void characters(char[] buf, int start, int end) throws SAXParseException { + /** + * 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(helper.project, child, buf, start, end); + addText(project, child, buf, start, count); } catch (BuildException exc) { - throw new SAXParseException(exc.getMessage(), helper.locator, exc); + throw new SAXParseException(exc.getMessage(), locator, exc); } } else { - childWrapper.addText(buf, start, end); + childWrapper.addText(buf, start, count); } } + /** + * 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(helper, this, (TaskContainer)child, childWrapper, target).init(name, attrs); + new TaskHandler(this, (TaskContainer)child, childWrapper, target).init(name, attrs); } else { - new NestedElementHandler(helper, this, child, childWrapper, target).init(name, attrs); + new NestedElementHandler(this, child, childWrapper, target).init(name, attrs); } } } /** - * Handler for all data types at global level. + * Handler for all data types directly subordinate to project or target. */ - private static class DataTypeHandler extends AbstractHandler { + 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; - - public DataTypeHandler(ProjectHelperImpl helper, DocumentHandler parentHandler) { - this(helper, parentHandler, 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); } - public DataTypeHandler(ProjectHelperImpl helper, - DocumentHandler parentHandler, - Target target) - { - super(helper, parentHandler); + /** + * 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 = helper.project.createDataType(propType); + element = project.createDataType(propType); if (element == null) { throw new BuildException("Unknown data type "+propType); } @@ -701,30 +1069,60 @@ public class ProjectHelperImpl extends ProjectHelper { wrapper.setAttributes(attrs); target.addDataType(wrapper); } else { - configure(element, attrs, helper.project); - helper.configureId(element, attrs); + configure(element, attrs, project); + configureId(element, attrs); } } catch (BuildException exc) { - throw new SAXParseException(exc.getMessage(), helper.locator, exc); + throw new SAXParseException(exc.getMessage(), locator, exc); } } - public void characters(char[] buf, int start, int end) throws SAXParseException { + // 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(helper.project, element, buf, start, end); + addText(project, element, buf, start, count); } catch (BuildException exc) { - throw new SAXParseException(exc.getMessage(), helper.locator, 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(helper, this, element, wrapper, target).init(name, attrs); + new NestedElementHandler(this, element, wrapper, target).init(name, attrs); } } - public ProjectHelperImpl() { - } - + /** + * 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(); @@ -734,11 +1132,14 @@ public class ProjectHelperImpl extends ProjectHelper { } /** - * Scan AttributeList for the id attribute and maybe add a - * reference to project. - * - *

Moved out of {@link #configure configure} to make it happen - * at parser time.

+ * 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"); @@ -746,5 +1147,4 @@ public class ProjectHelperImpl extends ProjectHelper { project.addReference(id, target); } } - }