The goal of this patch is to allow a different XML processor
to be plugged in and used.

The current processor has a number of limitations ( lack of 
namespace support, etc ) - but the main benefit for this 
patch is that it allows new ideas to be used without 
affecting the whole thing.

How it works:
- the current parser is used as default.

- a user can set a system property or just include an
alternative xml processor in the classpath, and this 
will be detected and loaded instead of the default.
( same pattern as in jaxp, common-logging, etc to
find a Helper implementation )

- all helpers are supposed to support the original
behavior and be backward compatible. Of course, the user
may ignore this and use a completely different model,
or have the behavior customized based on some namespace,
etc.

Costin

/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2002 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 [EMAIL PROTECTED]
 *
 * 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.tools.ant;

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 java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.xml.sax.AttributeList;


/**
 * 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 <project> 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 [EMAIL PROTECTED]
 */
public /*abstract*/ class ProjectHelper {
    protected Project project;
    protected Object source;
    protected File buildFile;
    protected File buildFileParent;

    /**
     * Configures the Project with the contents of the specified XML file.
     * ( should it be deprecated ? Using getProjectHelper(), parse()
     * is cleaner )
     */
    public static void configureProject(Project project, File buildFile)
        throws BuildException
    {
        ProjectHelper helper=ProjectHelper.getProjectHelper();
        System.out.println("Real helper " + helper.getClass().getName());
        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());
    }

    /**
     * Process an input source for the project.
     *
     * 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 ).
     */
    public /*abstract*/ void parse(Project project, Object source)
        throws BuildException
    {
        throw new BuildException("You must use a real ProjectHelper 
implementation"); 
    }

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

    
    /** Discover a project helper instance.
     */
    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);
            }
        } catch (SecurityException e) {
            // It's ok, we'll try next option
            ;
        }

        // 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 {
                InputStream is=null;
                if (classLoader == null) {
                    is=ClassLoader.getSystemResourceAsStream( SERVICE_ID );
                } else {
                    is=classLoader.getResourceAsStream( SERVICE_ID );
                }

                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, classLoader );
                    }
                }
            } catch( Exception ex ) {
                ;
            }
        }

        // Default
        return new ProjectHelperImpl();
    }

    private static ProjectHelper newHelper(String helperClass,
                                           ClassLoader classLoader)
        throws BuildException
    {

        try {
            Class clazz = null;
            if (classLoader == null) {
                clazz = Class.forName(helperClass);
            } else {
                clazz = classLoader.loadClass(helperClass);
            }
            return ((ProjectHelper) clazz.newInstance());
        } catch (Exception e) {
            throw new BuildException(e);
        }
    }

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

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

    
    /* -------------------- Common utilities and wrappers -------------------- 
*/

    /** Configure a java object using ant's rules.
     */
    public static void configure(Object target, AttributeList attrs, 
                                 Project project) throws BuildException {
        if( target instanceof TaskAdapter ) {
            target=((TaskAdapter)target).getProxy();
        }

        IntrospectionHelper ih = 
            IntrospectionHelper.getHelper(target.getClass());

        // 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() );
            try {
                ih.setAttribute(project, target, 
                                attrs.getName(i).toLowerCase(Locale.US), value);

            } catch (BuildException be) {
                // id attribute must be set externally
                if (!attrs.getName(i).equals("id")) {
                    throw be;
                }
            }
        }
    }

    /**
     * Adds the content of #PCDATA sections to an element.
     */
    public static void addText(Project project, Object target, char[] buf, int 
start, int end)
        throws BuildException {
        addText(project, target, new String(buf, start, end));
    }

    /**
     * Adds the content of #PCDATA sections to an element.
     */
    public static void addText(Project project, Object target, String text)
        throws BuildException {

        if (text == null ) {
            return;
        }

        if(target instanceof TaskAdapter) {
            target = ((TaskAdapter) target).getProxy();
        }

        IntrospectionHelper.getHelper(target.getClass()).addText(project, 
target, text);
    }

    /**
     * Stores a configured child element into its parent object 
     */
    public static void storeChild(Project project, Object parent, Object child, 
String tag) {
        IntrospectionHelper ih = 
IntrospectionHelper.getHelper(parent.getClass());
        ih.storeElement(project, parent, child, tag);
    }

    /**
     * Replace ${} 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.
     * @since 1.5
     */
     public static String replaceProperties(Project project, String value)
            throws BuildException {
         return project.replaceProperties(value);
     }

    /**
     * Replace ${} 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.
     */
     public static String replaceProperties(Project project, String value, 
Hashtable keys)
            throws BuildException {
        if (value == null) {
            return null;
        }

        Vector fragments = new Vector();
        Vector propertyRefs = new Vector();
        parsePropertyString(value, fragments, propertyRefs);

        StringBuffer sb = new StringBuffer();
        Enumeration i = fragments.elements();
        Enumeration j = propertyRefs.elements();
        while (i.hasMoreElements()) {
            String fragment = (String)i.nextElement();
            if (fragment == null) {
                String propertyName = (String)j.nextElement();
                if (!keys.containsKey(propertyName)) {
                    project.log("Property ${" + propertyName + "} has not been 
set", Project.MSG_VERBOSE);
                }
                fragment = (keys.containsKey(propertyName)) ? (String) 
keys.get(propertyName) 
                                                            : "${" + 
propertyName + "}"; 
            }
            sb.append(fragment);
        }                        
        
        return sb.toString();
    }

    /**
     * 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.
     */
    public static void parsePropertyString(String value, Vector fragments, 
Vector propertyRefs) 
        throws BuildException {
        int prev = 0;
        int pos;
        while ((pos = value.indexOf("$", prev)) >= 0) {
            if (pos > 0) {
                fragments.addElement(value.substring(prev, pos));
            }

            if( pos == (value.length() - 1)) {
                fragments.addElement("$");
                prev = pos + 1;
            }
            else if (value.charAt(pos + 1) != '{' ) {
                fragments.addElement(value.substring(pos + 1, pos + 2));
                prev = pos + 2;
            } else {
                int endName = value.indexOf('}', pos);
                if (endName < 0) {
                    throw new BuildException("Syntax error in property: " 
                                                 + value );
                }
                String propertyName = value.substring(pos + 2, endName);
                fragments.addElement(null);
                propertyRefs.addElement(propertyName);
                prev = endName + 1;
            }
        }

        if (prev < value.length()) {
            fragments.addElement(value.substring(prev));
        }
    }

}
/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2002 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 [EMAIL PROTECTED]
 *
 * 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.tools.ant;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
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 javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.ParserConfigurationException;

/**
 * "Original" implementation of the project helper. Or at least
 * what is present in ant1.4.
 *
 * @author [EMAIL PROTECTED]
 */
public class ProjectHelperImpl extends ProjectHelper {
    private static SAXParserFactory parserFactory = null;

    protected Project project;
    protected Object source;
    protected File buildFile;
    protected File buildFileParent;
    private org.xml.sax.Parser parser;
    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 <project> 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 </project>

        @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(parent);
        h.init( tag, attrs );
        return h;
    }

    /**
     * Parses the project file.
     */
    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");
        
        File buildFile=(File)source;
        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 {
                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
                }
            }
        }
    }

    /**
     * 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.
     *
     * Each type of xml element (task, target, etc) in ant will
     * have its own subclass of AbstractHandler.
     *
     * 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 {
        protected DocumentHandler parentHandler;

        public AbstractHandler(DocumentHandler parentHandler) {
            this.parentHandler = parentHandler;

            // Start handling SAX events
            parser.setDocumentHandler(this);
        }

        public void startElement(String tag, AttributeList attrs) throws 
SAXParseException {
            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();

            if (s.length() > 0) {
                throw new SAXParseException("Unexpected text \"" + s + "\"", 
locator);
            }
        }

        /**
         * Called when this element and all elements nested into it have been
         * handled.
         */
        protected void finished() {}

        public void endElement(String name) throws SAXException {

            finished();
            // Let parent resume handling SAX events
            parser.setDocumentHandler(parentHandler);
        }
    }

    /**
     * Handler for the root element. It's only child must be the "project" 
element.
     */
    private class RootHandler extends HandlerBase {

        /**
         * resolve file: URIs as relative to the build file.
         */
        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:");
                }
                
                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);
                }
                
                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;
        }

        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);
            }
        }

        public void setDocumentLocator(Locator locator) {
            ProjectHelperImpl.this.locator = locator;
        }
    }

    /**
     * Handler for the top level "project" element.
     */
    private class ProjectHandler extends AbstractHandler {
        public ProjectHandler(DocumentHandler parentHandler) {
            super(parentHandler);
        }

        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));
                    }
                }
            }

        }

        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);
            }
        }

        private void handleTaskdef(String name, AttributeList attrs) throws 
SAXParseException {
            (new TaskHandler(this, null, null, null)).init(name, attrs);
        }

        private void handleTypedef(String name, AttributeList attrs) throws 
SAXParseException {
            (new TaskHandler(this, null, null, null)).init(name, attrs);
        }

        private void handleProperty(String name, AttributeList attrs) throws 
SAXParseException {
            (new TaskHandler(this, null, null, null)).init(name, attrs);
        }

        private void handleTarget(String tag, AttributeList attrs) throws 
SAXParseException {
            new TargetHandler(this).init(tag, attrs);
        }

        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;

        public TargetHandler(DocumentHandler parentHandler) {
            super(parentHandler);
        }

        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);
            }
        }

        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);
            }
        }
    }

    /**
     * Handler for all task elements.
     */
    private class TaskHandler extends AbstractHandler {
        private Target target;
        private TaskContainer container;
        private Task task;
        private RuntimeConfigurable parentWrapper;
        private RuntimeConfigurable wrapper = null;

        public TaskHandler(DocumentHandler parentHandler, TaskContainer 
container, RuntimeConfigurable parentWrapper, Target target) {
            super(parentHandler);
            this.container = container;
            this.parentWrapper = parentWrapper;
            this.target = target;
        }

        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);
            }
        }

        protected void finished() {
            if (task != null && target == null) {
                task.execute();
            }
        }

        public void characters(char[] buf, int start, int end) throws 
SAXParseException {
            if (wrapper == null) {
                try {
                    addText(project, task, buf, start, end);
                } catch (BuildException exc) {
                    throw new SAXParseException(exc.getMessage(), locator, exc);
                }
            } else {
                wrapper.addText(buf, start, end);
            }
        }

        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);
            }
        }
    }

    /**
     * Handler for all nested properties.
     */
    private class NestedElementHandler extends AbstractHandler {
        private Object parent;
        private Object child;
        private RuntimeConfigurable parentWrapper;
        private RuntimeConfigurable childWrapper = null;
        private Target target;

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

        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);
            }
        }

        public void characters(char[] buf, int start, int end) throws 
SAXParseException {
            if (parentWrapper == null) {
                try {
                    addText(project, child, buf, start, end);
                } catch (BuildException exc) {
                    throw new SAXParseException(exc.getMessage(), locator, exc);
                }
            } else {
                childWrapper.addText(buf, start, end);
            }
        }

        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);
            }
        }
    }

    /**
     * Handler for all data types at global level.
     */
    private class DataTypeHandler extends AbstractHandler {
        private Target target;
        private Object element;
        private RuntimeConfigurable wrapper = null;

        public DataTypeHandler(DocumentHandler parentHandler) {
            this(parentHandler, null);
        }

        public DataTypeHandler(DocumentHandler parentHandler, Target target) {
            super(parentHandler);
            this.target = target;
        }

        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);
            }
        }

        public void characters(char[] buf, int start, int end) throws 
SAXParseException {
            try {
                addText(project, element, buf, start, end);
            } catch (BuildException exc) {
                throw new SAXParseException(exc.getMessage(), locator, exc);
            }
        }

        public void startElement(String name, AttributeList attrs) throws 
SAXParseException {
            new NestedElementHandler(this, element, wrapper, target).init(name, 
attrs);
        }
    }

    private static SAXParserFactory getParserFactory() {
        if (parserFactory == null) {
            parserFactory = SAXParserFactory.newInstance();
        }

        return parserFactory;
    }

    /**
     * Scan AttributeList for the id attribute and maybe add a
     * reference to project.  
     *
     * <p>Moved out of [EMAIL PROTECTED] #configure configure} to make it happen
     * at parser time.</p> 
     */
    private void configureId(Object target, AttributeList attr) {
        String id = attr.getValue("id");
        if (id != null) {
            project.addReference(id, target);
        }
    }

}
Index: ProjectHelper.java
===================================================================
RCS file: 
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/ProjectHelper.java,v
retrieving revision 1.75
diff -u -r1.75 ProjectHelper.java
--- ProjectHelper.java  14 Feb 2002 16:49:39 -0000      1.75
+++ ProjectHelper.java  21 Feb 2002 00:37:08 -0000
@@ -58,49 +58,63 @@
 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 java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
 import org.xml.sax.AttributeList;
-import org.xml.sax.helpers.XMLReaderAdapter;
 
-import javax.xml.parsers.SAXParserFactory;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.ParserConfigurationException;
 
 /**
  * Configures a Project (complete with Targets and Tasks) based on
- * a XML build file.
+ * 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 <project> 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 [EMAIL PROTECTED]
  */
-
-public class ProjectHelper {
-
-    private static SAXParserFactory parserFactory = null;
-
-    private org.xml.sax.Parser parser;
-    private Project project;
-    private File buildFile;
-    private File buildFileParent;
-    private Locator locator;
+public /*abstract*/ class ProjectHelper {
+    protected Project project;
+    protected Object source;
+    protected File buildFile;
+    protected File buildFileParent;
 
     /**
      * Configures the Project with the contents of the specified XML file.
+     * ( should it be deprecated ? Using getProjectHelper(), parse()
+     * is cleaner )
      */
-    public static void configureProject(Project project, File buildFile) 
throws BuildException {
-        new ProjectHelper(project, buildFile).parse();
+    public static void configureProject(Project project, File buildFile)
+        throws BuildException
+    {
+        ProjectHelper helper=ProjectHelper.getProjectHelper();
+        System.out.println("Real helper " + helper.getClass().getName());
+        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;
@@ -109,573 +123,140 @@
     }
 
     /**
-     * Parses the project file.
-     */
-    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
-                }
-            }
-        }
-    }
-
-    /**
-     * 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.
-     *
-     * Each type of xml element (task, target, etc) in ant will
-     * have its own subclass of AbstractHandler.
+     * Process an input source for the project.
      *
-     * 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 {
-        protected DocumentHandler parentHandler;
-
-        public AbstractHandler(DocumentHandler parentHandler) {
-            this.parentHandler = parentHandler;
-
-            // Start handling SAX events
-            parser.setDocumentHandler(this);
-        }
-
-        public void startElement(String tag, AttributeList attrs) throws 
SAXParseException {
-            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();
-
-            if (s.length() > 0) {
-                throw new SAXParseException("Unexpected text \"" + s + "\"", 
locator);
-            }
-        }
-
-        /**
-         * Called when this element and all elements nested into it have been
-         * handled.
-         */
-        protected void finished() {}
-
-        public void endElement(String name) throws SAXException {
-
-            finished();
-            // Let parent resume handling SAX events
-            parser.setDocumentHandler(parentHandler);
-        }
-    }
-
-    /**
-     * Handler for the root element. It's only child must be the "project" 
element.
-     */
-    private class RootHandler extends HandlerBase {
-
-        /**
-         * resolve file: URIs as relative to the build file.
-         */
-        public InputSource resolveEntity(String publicId,
-                                         String systemId) {
-        
-            project.log("resolving systemId: " + systemId, 
Project.MSG_VERBOSE);
+     * 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 ).
+     */
+    public /*abstract*/ void parse(Project project, Object source)
+        throws BuildException
+    {
+        throw new BuildException("You must use a real ProjectHelper 
implementation"); 
+    }
+
+    /* -------------------- 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";
+
+    
+    /** Discover a project helper instance.
+     */
+    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;
         
-            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:");
-                }
-                
-                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);
-                }
-                
-                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;
-        }
-
-        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);
-            }
-        }
-
-        public void setDocumentLocator(Locator locator) {
-            ProjectHelper.this.locator = locator;
+        // First, try the system property
+        try {
+            String helperClass = System.getProperty(HELPER_PROPERTY);
+            if (helperClass != null) {
+                helper = newHelper(helperClass, classLoader);
+            }
+        } catch (SecurityException e) {
+            // It's ok, we'll try next option
+            ;
         }
-    }
 
-    /**
-     * Handler for the top level "project" element.
-     */
-    private class ProjectHandler extends AbstractHandler {
-        public ProjectHandler(DocumentHandler parentHandler) {
-            super(parentHandler);
-        }
-
-        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;
+        // 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 {
+                InputStream is=null;
+                if (classLoader == null) {
+                    is=ClassLoader.getSystemResourceAsStream( SERVICE_ID );
                 } else {
-                    throw new SAXParseException("Unexpected attribute \"" + 
attrs.getName(i) + "\"", locator);
+                    is=classLoader.getResourceAsStream( SERVICE_ID );
                 }
-            }
-
-            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, classLoader );
                     }
                 }
+            } catch( Exception ex ) {
+                ;
             }
-
-        }
-
-        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);
-            }
-        }
-
-        private void handleTaskdef(String name, AttributeList attrs) throws 
SAXParseException {
-            (new TaskHandler(this, null, null, null)).init(name, attrs);
-        }
-
-        private void handleTypedef(String name, AttributeList attrs) throws 
SAXParseException {
-            (new TaskHandler(this, null, null, null)).init(name, attrs);
-        }
-
-        private void handleProperty(String name, AttributeList attrs) throws 
SAXParseException {
-            (new TaskHandler(this, null, null, null)).init(name, attrs);
-        }
-
-        private void handleTarget(String tag, AttributeList attrs) throws 
SAXParseException {
-            new TargetHandler(this).init(tag, attrs);
-        }
-
-        private void handleDataType(String name, AttributeList attrs) throws 
SAXParseException {
-            new DataTypeHandler(this).init(name, attrs);
         }
 
+        // Default
+        return new ProjectHelperImpl();
     }
 
-    /**
-     * Handler for "target" elements.
-     */
-    private class TargetHandler extends AbstractHandler {
-        private Target target;
-
-        public TargetHandler(DocumentHandler parentHandler) {
-            super(parentHandler);
-        }
-
-        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);
-            }
-        }
+    private static ProjectHelper newHelper(String helperClass,
+                                           ClassLoader classLoader)
+        throws BuildException
+    {
 
-        public void startElement(String name, AttributeList attrs) throws 
SAXParseException {
-            if (project.getDataTypeDefinitions().get(name) != null) {
-                new DataTypeHandler(this, target).init(name, attrs);
+        try {
+            Class clazz = null;
+            if (classLoader == null) {
+                clazz = Class.forName(helperClass);
             } else {
-                new TaskHandler(this, target, null, target).init(name, attrs);
+                clazz = classLoader.loadClass(helperClass);
             }
+            return ((ProjectHelper) clazz.newInstance());
+        } catch (Exception e) {
+            throw new BuildException(e);
         }
     }
 
-    /**
-     * Handler for all task elements.
+    /** 
      */
-    private class TaskHandler extends AbstractHandler {
-        private Target target;
-        private TaskContainer container;
-        private Task task;
-        private RuntimeConfigurable parentWrapper;
-        private RuntimeConfigurable wrapper = null;
-
-        public TaskHandler(DocumentHandler parentHandler, TaskContainer 
container, RuntimeConfigurable parentWrapper, Target target) {
-            super(parentHandler);
-            this.container = container;
-            this.parentWrapper = parentWrapper;
-            this.target = target;
-        }
-
-        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);
-            }
-        }
-
-        protected void finished() {
-            if (task != null && target == null) {
-                task.execute();
-            }
+    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; 
         }
 
-        public void characters(char[] buf, int start, int end) throws 
SAXParseException {
-            if (wrapper == null) {
-                try {
-                    addText(project, task, buf, start, end);
-                } catch (BuildException exc) {
-                    throw new SAXParseException(exc.getMessage(), locator, 
exc);
-                }
-            } else {
-                wrapper.addText(buf, start, end);
-            }
+        // 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);
         }
 
-        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);
-            }
-        }
+        // Return the selected class loader
+        return (classLoader);
     }
 
-    /**
-     * Handler for all nested properties.
-     */
-    private class NestedElementHandler extends AbstractHandler {
-        private Object parent;
-        private Object child;
-        private RuntimeConfigurable parentWrapper;
-        private RuntimeConfigurable childWrapper = null;
-        private Target target;
-
-        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;
-        }
-
-        public void init(String propType, AttributeList attrs) throws 
SAXParseException {
-            Class parentClass = parent.getClass();
-            IntrospectionHelper ih = 
-                IntrospectionHelper.getHelper(parentClass);
+    
+    /* -------------------- Common utilities and wrappers -------------------- 
*/
 
-            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);
-            }
-        }
-
-        public void characters(char[] buf, int start, int end) throws 
SAXParseException {
-            if (parentWrapper == null) {
-                try {
-                    addText(project, child, buf, start, end);
-                } catch (BuildException exc) {
-                    throw new SAXParseException(exc.getMessage(), locator, 
exc);
-                }
-            } else {
-                childWrapper.addText(buf, start, end);
-            }
-        }
-
-        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);
-            }
-        }
-    }
-
-    /**
-     * Handler for all data types at global level.
+    /** Configure a java object using ant's rules.
      */
-    private class DataTypeHandler extends AbstractHandler {
-        private Target target;
-        private Object element;
-        private RuntimeConfigurable wrapper = null;
-
-        public DataTypeHandler(DocumentHandler parentHandler) {
-            this(parentHandler, null);
-        }
-
-        public DataTypeHandler(DocumentHandler parentHandler, Target target) {
-            super(parentHandler);
-            this.target = target;
-        }
-
-        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);
-            }
-        }
-
-        public void characters(char[] buf, int start, int end) throws 
SAXParseException {
-            try {
-                addText(project, element, buf, start, end);
-            } catch (BuildException exc) {
-                throw new SAXParseException(exc.getMessage(), locator, exc);
-            }
-        }
-
-        public void startElement(String name, AttributeList attrs) throws 
SAXParseException {
-            new NestedElementHandler(this, element, wrapper, 
target).init(name, attrs);
-        }
-    }
-
     public static void configure(Object target, AttributeList attrs, 
                                  Project project) throws BuildException {
         if( target instanceof TaskAdapter ) {
@@ -685,6 +266,7 @@
         IntrospectionHelper ih = 
             IntrospectionHelper.getHelper(target.getClass());
 
+        // XXX What's that ?
         project.addBuildListener(ih);
 
         for (int i = 0; i < attrs.getLength(); i++) {
@@ -822,28 +404,6 @@
 
         if (prev < value.length()) {
             fragments.addElement(value.substring(prev));
-        }
-    }
-
-    private static SAXParserFactory getParserFactory() {
-        if (parserFactory == null) {
-            parserFactory = SAXParserFactory.newInstance();
-        }
-
-        return parserFactory;
-    }
-
-    /**
-     * Scan AttributeList for the id attribute and maybe add a
-     * reference to project.  
-     *
-     * <p>Moved out of [EMAIL PROTECTED] #configure configure} to make it 
happen
-     * at parser time.</p> 
-     */
-    private void configureId(Object target, AttributeList attr) {
-        String id = attr.getValue("id");
-        if (id != null) {
-            project.addReference(id, target);
         }
     }
--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to