ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ehatc...@apache.org
Subject cvs commit: jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant IntrospectionHelper.java Project.java ProjectHelper.java RoleAdapter.java SymbolTable.java TaskAdapter.java
Date Sun, 03 Mar 2002 12:37:13 GMT
ehatcher    02/03/03 04:37:13

  Modified:    proposal/sandbox/antlib/src/main/org/apache/tools/ant/types
                        DataTypeAdapterTask.java
               proposal/sandbox/antlib/src/main/org/apache/tools/ant
                        IntrospectionHelper.java Project.java
                        ProjectHelper.java RoleAdapter.java
                        SymbolTable.java TaskAdapter.java
  Log:
  once again for Jose Alberto - think I got it right this time
  
  Revision  Changes    Path
  1.4       +21 -3     jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java
  
  Index: DataTypeAdapterTask.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- DataTypeAdapterTask.java	2 Mar 2002 22:23:10 -0000	1.3
  +++ DataTypeAdapterTask.java	3 Mar 2002 12:37:13 -0000	1.4
  @@ -66,6 +66,7 @@
   public class DataTypeAdapterTask extends Task implements RoleAdapter {
   
       Object proxy;
  +    String id = null;
       
       /**
        * Checks a class, whether it is suitable to be adapted.
  @@ -83,14 +84,27 @@
        * Do the execution.
        */
       public void execute() throws BuildException {
  +	if (id != null) {
  +	    // Need to re-register this reference
  +	    // The container has register the Adapter instead
  +            project.addReference(id, proxy);	    
  +	}
  +    }
  +
  +    /**
  +     * Propagate configuration of Project
  +     */
  +    public void setProject(Project p) {
  +	super.setProject(p);
  +
   	// Check to see if the DataType has a setProject method to set
   	if (proxy instanceof ProjectComponent) {
  -	    ((ProjectComponent)proxy).setProject(project);
  +	    ((ProjectComponent)proxy).setProject(p);
   	    return;
   	}
   
   	// This may not be needed
  -	// We are trying to set project even it is was not declared
  +	// We are trying to set project even if is was not declared
   	// just like TaskAdapter does for beans, this is not done
   	// by the original code
           Method setProjectM = null;
  @@ -99,7 +113,7 @@
               setProjectM = 
                   c.getMethod( "setProject", new Class[] {Project.class});
               if(setProjectM != null) {
  -                setProjectM.invoke(proxy, new Object[] {project});
  +                setProjectM.invoke(proxy, new Object[] {p});
               }
           } catch (NoSuchMethodException e) {
               // ignore this if the class being used as a task does not have
  @@ -122,4 +136,8 @@
           return this.proxy ;
       }
   
  +    public void setId(String id) {
  +	log("Setting adapter id to: " + id, Project.MSG_DEBUG);
  +	this.id = id;
  +    }
   }
  
  
  
  1.2       +706 -706  jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/IntrospectionHelper.java
  
  Index: IntrospectionHelper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/IntrospectionHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- IntrospectionHelper.java	18 Feb 2002 08:55:16 -0000	1.1
  +++ IntrospectionHelper.java	3 Mar 2002 12:37:13 -0000	1.2
  @@ -1,706 +1,706 @@
  -/*
  - * The Apache Software License, Version 1.1
  - *
  - * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
  - * reserved.
  - *
  - * Redistribution and use in source and binary forms, with or without
  - * modification, are permitted provided that the following conditions
  - * are met:
  - *
  - * 1. Redistributions of source code must retain the above copyright
  - *    notice, this list of conditions and the following disclaimer.
  - *
  - * 2. Redistributions in binary form must reproduce the above copyright
  - *    notice, this list of conditions and the following disclaimer in
  - *    the documentation and/or other materials provided with the
  - *    distribution.
  - *
  - * 3. The end-user documentation included with the redistribution, if
  - *    any, must include the following acknowlegement:
  - *       "This product includes software developed by the
  - *        Apache Software Foundation (http://www.apache.org/)."
  - *    Alternately, this acknowlegement may appear in the software itself,
  - *    if and wherever such third-party acknowlegements normally appear.
  - *
  - * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  - *    Foundation" must not be used to endorse or promote products derived
  - *    from this software without prior written permission. For written
  - *    permission, please contact apache@apache.org.
  - *
  - * 5. Products derived from this software may not be called "Apache"
  - *    nor may "Apache" appear in their names without prior written
  - *    permission of the Apache Group.
  - *
  - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  - * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  - * SUCH DAMAGE.
  - * ====================================================================
  - *
  - * This software consists of voluntary contributions made by many
  - * individuals on behalf of the Apache Software Foundation.  For more
  - * information on the Apache Software Foundation, please see
  - * <http://www.apache.org/>.
  - */
  -
  -package org.apache.tools.ant;
  -
  -import org.apache.tools.ant.types.Path;
  -import org.apache.tools.ant.types.EnumeratedAttribute;
  -
  -import java.lang.reflect.Method;
  -import java.lang.reflect.InvocationTargetException;
  -import java.lang.reflect.Constructor;
  -import java.io.File;
  -import java.util.Enumeration;
  -import java.util.Hashtable;
  -import java.util.Locale;
  -
  -/**
  - * Helper class that collects the methods a task or nested element
  - * holds to set attributes, create nested elements or hold PCDATA
  - * elements.
  - *
  - * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
  - */
  -public class IntrospectionHelper implements BuildListener {
  -
  -    /**
  -     * holds the types of the attributes that could be set.
  -     */
  -    private Hashtable attributeTypes;
  -
  -    /**
  -     * holds the attribute setter methods.
  -     */
  -    private Hashtable attributeSetters;
  -
  -    /**
  -     * Holds the types of nested elements that could be created.
  -     */
  -    private Hashtable nestedTypes;
  -
  -    /**
  -     * Holds methods to create nested elements.
  -     */
  -    private Hashtable nestedCreators;
  -
  -    /**
  -     * Holds methods to store configured nested elements.
  -     */
  -    private Hashtable nestedStorers;
  -
  -    /**
  -     * The method to add PCDATA stuff.
  -     */
  -    private Method addText = null;
  -
  -    /**
  -     * The Class that's been introspected.
  -     */
  -    private Class bean;
  -
  -    /**
  -     * instances we've already created
  -     */
  -    private static Hashtable helpers = new Hashtable();
  -
  -    private IntrospectionHelper(final Class bean) {
  -        attributeTypes = new Hashtable();
  -        attributeSetters = new Hashtable();
  -        nestedTypes = new Hashtable();
  -        nestedCreators = new Hashtable();
  -        nestedStorers = new Hashtable();
  -
  -        this.bean = bean;
  -
  -        Method[] methods = bean.getMethods();
  -        for (int i=0; i<methods.length; i++) {
  -            final Method m = methods[i];
  -            final String name = m.getName();
  -            Class returnType = m.getReturnType();
  -            Class[] args = m.getParameterTypes();
  -
  -            // not really user settable properties on tasks
  -            if (org.apache.tools.ant.Task.class.isAssignableFrom(bean)
  -                && args.length == 1 &&
  -                (
  -                 (
  -                  "setLocation".equals(name) && org.apache.tools.ant.Location.class.equals(args[0])
  -                  ) || (
  -                   "setTaskType".equals(name) && java.lang.String.class.equals(args[0])
  -                  )
  -                 )) {
  -                continue;
  -            }
  -
  -            // hide addTask for TaskContainers
  -//              if (org.apache.tools.ant.TaskContainer.class.isAssignableFrom(bean)
  -//                  && args.length == 1 && "addTask".equals(name)
  -//                  && org.apache.tools.ant.Task.class.equals(args[0])) {
  -//                  continue;
  -//              }
  -
  -
  -            if ("addText".equals(name)
  -                && java.lang.Void.TYPE.equals(returnType)
  -                && args.length == 1
  -                && java.lang.String.class.equals(args[0])) {
  -
  -                addText = methods[i];
  -
  -            } else if (name.startsWith("set")
  -                       && java.lang.Void.TYPE.equals(returnType)
  -                       && args.length == 1
  -                       && !args[0].isArray()) {
  -
  -                String propName = getPropertyName(name, "set");
  -                if (attributeSetters.get(propName) != null) {
  -                    if (java.lang.String.class.equals(args[0])) {
  -                        /*
  -                            Ignore method m, as there is an overloaded
  -                            form of this method that takes in a
  -                            non-string argument, which gains higher
  -                            priority.
  -                        */
  -                        continue;
  -                    }
  -                    /*
  -                        If the argument is not a String, and if there
  -                        is an overloaded form of this method already defined,
  -                        we just override that with the new one.
  -                        This mechanism does not guarantee any specific order
  -                        in which the methods will be selected: so any code
  -                        that depends on the order in which "set" methods have
  -                        been defined, is not guaranteed to be selected in any
  -                        particular order.
  -                    */
  -                }
  -                AttributeSetter as = createAttributeSetter(m, args[0]);
  -                if (as != null) {
  -                    attributeTypes.put(propName, args[0]);
  -                    attributeSetters.put(propName, as);
  -                }
  -
  -            } else if (name.startsWith("create")
  -                       && !returnType.isArray()
  -                       && !returnType.isPrimitive()
  -                       && args.length == 0) {
  -
  -                String propName = getPropertyName(name, "create");
  -                nestedTypes.put(propName, returnType);
  -                nestedCreators.put(propName, new NestedCreator() {
  -
  -                        public Object create(Object parent)
  -                            throws InvocationTargetException,
  -                            IllegalAccessException {
  -
  -                            return m.invoke(parent, new Object[] {});
  -                        }
  -
  -                    });
  -                nestedStorers.remove(propName);
  -
  -            } else if (name.startsWith("addConfigured")
  -                       && java.lang.Void.TYPE.equals(returnType)
  -                       && args.length == 1
  -                       && !java.lang.String.class.equals(args[0])
  -                       && !args[0].isArray()
  -                       && !args[0].isPrimitive()) {
  -
  -                try {
  -                    final Constructor c =
  -                        args[0].getConstructor(new Class[] {});
  -                    String propName = getPropertyName(name, "addConfigured");
  -                    nestedTypes.put(propName, args[0]);
  -                    nestedCreators.put(propName, new NestedCreator() {
  -
  -                            public Object create(Object parent)
  -                                throws InvocationTargetException, 
  -				       IllegalAccessException, 
  -				       InstantiationException {
  -
  -                                Object o = c.newInstance(new Object[] {});
  -                                return o;
  -                            }
  -
  -                        });
  -                    nestedStorers.put(propName, new NestedStorer() {
  -
  -                            public void store(Object parent, Object child)
  -                                throws InvocationTargetException, 
  -				       IllegalAccessException, 
  -				       InstantiationException {
  -
  -                                m.invoke(parent, new Object[] {child});
  -                            }
  -
  -                        });
  -                } catch (NoSuchMethodException nse) {
  -                }
  -            } else if (name.startsWith("add")
  -                       && java.lang.Void.TYPE.equals(returnType)
  -                       && args.length == 1
  -                       && !java.lang.String.class.equals(args[0])
  -                       && !args[0].isArray()
  -                       && !args[0].isPrimitive()) {
  -
  -                try {
  -                    final Constructor c =
  -                        args[0].getConstructor(new Class[] {});
  -                    String propName = getPropertyName(name, "add");
  -                    nestedTypes.put(propName, args[0]);
  -                    nestedCreators.put(propName, new NestedCreator() {
  -
  -                            public Object create(Object parent)
  -                                throws InvocationTargetException, 
  -				       IllegalAccessException, 
  -				       InstantiationException {
  -
  -                                Object o = c.newInstance(new Object[] {});
  -                                m.invoke(parent, new Object[] {o});
  -                                return o;
  -                            }
  -
  -                        });
  -                    nestedStorers.remove(name);
  -                } catch (NoSuchMethodException nse) {
  -                }
  -            }
  -        }
  -    }
  -
  -    /**
  -     * Factory method for helper objects.
  -     */
  -    public static synchronized IntrospectionHelper getHelper(Class c) {
  -        IntrospectionHelper ih = (IntrospectionHelper) helpers.get(c);
  -        if (ih == null) {
  -            ih = new IntrospectionHelper(c);
  -            helpers.put(c, ih);
  -        }
  -        return ih;
  -    }
  -
  -    /**
  -     * Sets the named attribute.
  -     */
  -    public void setAttribute(Project p, Object element, String attributeName,
  -                             String value)
  -        throws BuildException {
  -        AttributeSetter as = (AttributeSetter) attributeSetters.get(attributeName);
  -        if (as == null) {
  -            String msg = getElementName(p, element) +
  -            //String msg = "Class " + element.getClass().getName() +
  -                " doesn't support the \"" + attributeName + "\" attribute.";
  -            throw new BuildException(msg);
  -        }
  -        try {
  -            as.set(p, element, value);
  -        } catch (IllegalAccessException ie) {
  -            // impossible as getMethods should only return public methods
  -            throw new BuildException(ie);
  -        } catch (InvocationTargetException ite) {
  -            Throwable t = ite.getTargetException();
  -            if (t instanceof BuildException) {
  -                throw (BuildException) t;
  -            }
  -            throw new BuildException(t);
  -        }
  -    }
  -
  -    /**
  -     * Adds PCDATA areas.
  -     */
  -    public void addText(Project project, Object element, String text) {
  -        if (addText == null) {
  -            // Element doesn't handle text content
  -            if ( text.trim().length() == 0 ) {
  -                // Only whitespace - ignore
  -                return;
  -            }
  -            else {
  -                // Not whitespace - fail
  -                String msg = getElementName(project, element) +
  -                    " doesn't support nested text data.";
  -                throw new BuildException(msg);
  -            }
  -        }
  -        try {
  -            addText.invoke(element, new String[] {text});
  -        } catch (IllegalAccessException ie) {
  -            // impossible as getMethods should only return public methods
  -            throw new BuildException(ie);
  -        } catch (InvocationTargetException ite) {
  -            Throwable t = ite.getTargetException();
  -            if (t instanceof BuildException) {
  -                throw (BuildException) t;
  -            }
  -            throw new BuildException(t);
  -        }
  -    }
  -
  -    /**
  -     * Creates a named nested element.
  -     */
  -    public Object createElement(Project project, Object element, String elementName)
  -        throws BuildException {
  -	
  -        try {
  -	    // First check if there are any roles supported by this class
  -	    Object nestedElement = project.createInRole(element, elementName);
  -	    if (nestedElement == null) {
  -		NestedCreator nc = 
  -		    (NestedCreator) nestedCreators.get(elementName);
  -		if (nc == null) {
  -		    String msg = getElementName(project, element) +
  -			" doesn't support the nested \"" + elementName + 
  -			"\" element.";
  -		    throw new BuildException(msg);
  -		}
  -		nestedElement = nc.create(element);
  -	    }
  -            if (nestedElement instanceof ProjectComponent) {
  -                ((ProjectComponent) nestedElement).setProject(project);
  -            }
  -            return nestedElement;
  -        } catch (IllegalAccessException ie) {
  -            // impossible as getMethods should only return public methods
  -            throw new BuildException(ie);
  -        } catch (InstantiationException ine) {
  -            // impossible as getMethods should only return public methods
  -            throw new BuildException(ine);
  -        } catch (InvocationTargetException ite) {
  -            Throwable t = ite.getTargetException();
  -            if (t instanceof BuildException) {
  -                throw (BuildException) t;
  -            }
  -            throw new BuildException(t);
  -        }
  -    }
  -
  -    /**
  -     * Creates a named nested element.
  -     */
  -    public void storeElement(Project project, Object element, Object child, String elementName)
  -        throws BuildException {
  -        if (elementName == null) {
  -            return;
  -        }
  -        NestedStorer ns = (NestedStorer)nestedStorers.get(elementName);
  -        if (ns == null) {
  -            return;
  -        }
  -        try {
  -            ns.store(element, child);
  -        } catch (IllegalAccessException ie) {
  -            // impossible as getMethods should only return public methods
  -            throw new BuildException(ie);
  -        } catch (InstantiationException ine) {
  -            // impossible as getMethods should only return public methods
  -            throw new BuildException(ine);
  -        } catch (InvocationTargetException ite) {
  -            Throwable t = ite.getTargetException();
  -            if (t instanceof BuildException) {
  -                throw (BuildException) t;
  -            }
  -            throw new BuildException(t);
  -        }
  -    }
  -
  -    /**
  -     * returns the type of a named nested element.
  -     */
  -    public Class getElementType(String elementName)
  -        throws BuildException {
  -        Class nt = (Class) nestedTypes.get(elementName);
  -        if (nt == null) {
  -            String msg = "Class " + bean.getName() +
  -                " doesn't support the nested \"" + elementName + "\" element.";
  -            throw new BuildException(msg);
  -        }
  -        return nt;
  -    }
  -
  -    /**
  -     * returns the type of a named attribute.
  -     */
  -    public Class getAttributeType(String attributeName)
  -        throws BuildException {
  -        Class at = (Class) attributeTypes.get(attributeName);
  -        if (at == null) {
  -            String msg = "Class " + bean.getName() +
  -                " doesn't support the \"" + attributeName + "\" attribute.";
  -            throw new BuildException(msg);
  -        }
  -        return at;
  -    }
  -
  -    /**
  -     * Does the introspected class support PCDATA?
  -     */
  -    public boolean supportsCharacters() {
  -        return addText != null;
  -    }
  -
  -    /**
  -     * Return all attribues supported by the introspected class.
  -     */
  -    public Enumeration getAttributes() {
  -        return attributeSetters.keys();
  -    }
  -
  -    /**
  -     * Return all nested elements supported by the introspected class.
  -     */
  -    public Enumeration getNestedElements() {
  -        return nestedTypes.keys();
  -    }
  -
  -    /**
  -     * Create a proper implementation of AttributeSetter for the given
  -     * attribute type.
  -     */
  -    private AttributeSetter createAttributeSetter(final Method m,
  -                                                  final Class arg) {
  -
  -        // simplest case - setAttribute expects String
  -        if (java.lang.String.class.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent, new String[] {value});
  -                    }
  -                };
  -
  -        // now for the primitive types, use their wrappers
  -        } else if (java.lang.Character.class.equals(arg)
  -                   || java.lang.Character.TYPE.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent, new Character[] {new Character(value.charAt(0))});
  -                    }
  -
  -                };
  -        } else if (java.lang.Byte.TYPE.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent, new Byte[] {new Byte(value)});
  -                    }
  -
  -                };
  -        } else if (java.lang.Short.TYPE.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent, new Short[] {new Short(value)});
  -                    }
  -
  -                };
  -        } else if (java.lang.Integer.TYPE.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent, new Integer[] {new Integer(value)});
  -                    }
  -
  -                };
  -        } else if (java.lang.Long.TYPE.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent, new Long[] {new Long(value)});
  -                    }
  -
  -                };
  -        } else if (java.lang.Float.TYPE.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent, new Float[] {new Float(value)});
  -                    }
  -
  -                };
  -        } else if (java.lang.Double.TYPE.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent, new Double[] {new Double(value)});
  -                    }
  -
  -                };
  -
  -        // boolean gets an extra treatment, because we have a nice method
  -        // in Project
  -        } else if (java.lang.Boolean.class.equals(arg)
  -                   || java.lang.Boolean.TYPE.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent,
  -                                 new Boolean[] {new Boolean(Project.toBoolean(value))});
  -                    }
  -
  -                };
  -
  -        // Class doesn't have a String constructor but a decent factory method
  -        } else if (java.lang.Class.class.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException, BuildException {
  -                        try {
  -                            m.invoke(parent, new Class[] {Class.forName(value)});
  -                        } catch (ClassNotFoundException ce) {
  -                            throw new BuildException(ce);
  -                        }
  -                    }
  -                };
  -
  -        // resolve relative paths through Project
  -        } else if (java.io.File.class.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent, new File[] {p.resolveFile(value)});
  -                    }
  -
  -                };
  -
  -        // resolve relative paths through Project
  -        } else if (org.apache.tools.ant.types.Path.class.equals(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException {
  -                        m.invoke(parent, new Path[] {new Path(p, value)});
  -                    }
  -
  -                };
  -
  -        // EnumeratedAttributes have their own helper class
  -        } else if (org.apache.tools.ant.types.EnumeratedAttribute.class.isAssignableFrom(arg)) {
  -            return new AttributeSetter() {
  -                    public void set(Project p, Object parent, String value)
  -                        throws InvocationTargetException, IllegalAccessException, BuildException {
  -                        try {
  -                            org.apache.tools.ant.types.EnumeratedAttribute ea = (org.apache.tools.ant.types.EnumeratedAttribute)arg.newInstance();
  -                            ea.setValue(value);
  -                            m.invoke(parent, new EnumeratedAttribute[] {ea});
  -                        } catch (InstantiationException ie) {
  -                            throw new BuildException(ie);
  -                        }
  -                    }
  -                };
  -
  -        // worst case. look for a public String constructor and use it
  -        } else {
  -
  -            try {
  -                final Constructor c =
  -                    arg.getConstructor(new Class[] {java.lang.String.class});
  -
  -                return new AttributeSetter() {
  -                        public void set(Project p, Object parent,
  -                                        String value)
  -                            throws InvocationTargetException, IllegalAccessException, BuildException {
  -                            try {
  -                                Object attribute = c.newInstance(new String[] {value});
  -                                if (attribute instanceof ProjectComponent) {
  -                                    ((ProjectComponent) attribute).setProject(p);
  -                                }
  -                                m.invoke(parent, new Object[] {attribute});
  -                            } catch (InstantiationException ie) {
  -                                throw new BuildException(ie);
  -                            }
  -                        }
  -                    };
  -
  -            } catch (NoSuchMethodException nme) {
  -            }
  -        }
  -
  -        return null;
  -    }
  -
  -    protected String getElementName(Project project, Object element)
  -    {
  -        Hashtable elements = project.getTaskDefinitions();
  -        String typeName = "task";
  -        if (!elements.contains( element.getClass() ))
  -        {
  -            elements = project.getDataTypeDefinitions();
  -            typeName = "data type";
  -            if (!elements.contains( element.getClass() ))
  -            {
  -                elements = null;
  -            }
  -        }
  -
  -        if (elements != null)
  -        {
  -            Enumeration e = elements.keys();
  -            while (e.hasMoreElements())
  -            {
  -                String elementName = (String) e.nextElement();
  -                Class elementClass = (Class) elements.get( elementName );
  -                if ( element.getClass().equals( elementClass ) )
  -                {
  -                    return "The <" + elementName + "> " + typeName;
  -                }
  -            }
  -        }
  -
  -        return "Class " + element.getClass().getName();
  -    }
  -
  -    /**
  -     * extract the name of a property from a method name - subtracting
  -     * a given prefix.
  -     */
  -    private String getPropertyName(String methodName, String prefix) {
  -        int start = prefix.length();
  -        return methodName.substring(start).toLowerCase(Locale.US);
  -    }
  -
  -    private interface NestedCreator {
  -        Object create(Object parent)
  -            throws InvocationTargetException, IllegalAccessException, InstantiationException;
  -    }
  -
  -    private interface NestedStorer {
  -        void store(Object parent, Object child)
  -            throws InvocationTargetException, IllegalAccessException, InstantiationException;
  -    }
  -
  -    private interface AttributeSetter {
  -        void set(Project p, Object parent, String value)
  -            throws InvocationTargetException, IllegalAccessException,
  -                   BuildException;
  -    }
  -
  -    public void buildStarted(BuildEvent event) {}
  -    public void buildFinished(BuildEvent event) {
  -        attributeTypes.clear();
  -        attributeSetters.clear();
  -        nestedTypes.clear();
  -        nestedCreators.clear();
  -        addText = null;
  -        helpers.clear();
  -    }
  -
  -    public void targetStarted(BuildEvent event) {}
  -    public void targetFinished(BuildEvent event) {}
  -    public void taskStarted(BuildEvent event) {}
  -    public void taskFinished(BuildEvent event) {}
  -    public void messageLogged(BuildEvent event) {}
  -}
  +/*
  + * The Apache Software License, Version 1.1
  + *
  + * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
  + * reserved.
  + *
  + * Redistribution and use in source and binary forms, with or without
  + * modification, are permitted provided that the following conditions
  + * are met:
  + *
  + * 1. Redistributions of source code must retain the above copyright
  + *    notice, this list of conditions and the following disclaimer.
  + *
  + * 2. Redistributions in binary form must reproduce the above copyright
  + *    notice, this list of conditions and the following disclaimer in
  + *    the documentation and/or other materials provided with the
  + *    distribution.
  + *
  + * 3. The end-user documentation included with the redistribution, if
  + *    any, must include the following acknowlegement:
  + *       "This product includes software developed by the
  + *        Apache Software Foundation (http://www.apache.org/)."
  + *    Alternately, this acknowlegement may appear in the software itself,
  + *    if and wherever such third-party acknowlegements normally appear.
  + *
  + * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  + *    Foundation" must not be used to endorse or promote products derived
  + *    from this software without prior written permission. For written
  + *    permission, please contact apache@apache.org.
  + *
  + * 5. Products derived from this software may not be called "Apache"
  + *    nor may "Apache" appear in their names without prior written
  + *    permission of the Apache Group.
  + *
  + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  + * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  + * SUCH DAMAGE.
  + * ====================================================================
  + *
  + * This software consists of voluntary contributions made by many
  + * individuals on behalf of the Apache Software Foundation.  For more
  + * information on the Apache Software Foundation, please see
  + * <http://www.apache.org/>.
  + */
  +
  +package org.apache.tools.ant;
  +
  +import org.apache.tools.ant.types.Path;
  +import org.apache.tools.ant.types.EnumeratedAttribute;
  +
  +import java.lang.reflect.Method;
  +import java.lang.reflect.InvocationTargetException;
  +import java.lang.reflect.Constructor;
  +import java.io.File;
  +import java.util.Enumeration;
  +import java.util.Hashtable;
  +import java.util.Locale;
  +
  +/**
  + * Helper class that collects the methods a task or nested element
  + * holds to set attributes, create nested elements or hold PCDATA
  + * elements.
  + *
  + * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
  + */
  +public class IntrospectionHelper implements BuildListener {
  +
  +    /**
  +     * holds the types of the attributes that could be set.
  +     */
  +    private Hashtable attributeTypes;
  +
  +    /**
  +     * holds the attribute setter methods.
  +     */
  +    private Hashtable attributeSetters;
  +
  +    /**
  +     * Holds the types of nested elements that could be created.
  +     */
  +    private Hashtable nestedTypes;
  +
  +    /**
  +     * Holds methods to create nested elements.
  +     */
  +    private Hashtable nestedCreators;
  +
  +    /**
  +     * Holds methods to store configured nested elements.
  +     */
  +    private Hashtable nestedStorers;
  +
  +    /**
  +     * The method to add PCDATA stuff.
  +     */
  +    private Method addText = null;
  +
  +    /**
  +     * The Class that's been introspected.
  +     */
  +    private Class bean;
  +
  +    /**
  +     * instances we've already created
  +     */
  +    private static Hashtable helpers = new Hashtable();
  +
  +    private IntrospectionHelper(final Class bean) {
  +        attributeTypes = new Hashtable();
  +        attributeSetters = new Hashtable();
  +        nestedTypes = new Hashtable();
  +        nestedCreators = new Hashtable();
  +        nestedStorers = new Hashtable();
  +
  +        this.bean = bean;
  +
  +        Method[] methods = bean.getMethods();
  +        for (int i=0; i<methods.length; i++) {
  +            final Method m = methods[i];
  +            final String name = m.getName();
  +            Class returnType = m.getReturnType();
  +            Class[] args = m.getParameterTypes();
  +
  +            // not really user settable properties on tasks
  +            if (org.apache.tools.ant.Task.class.isAssignableFrom(bean)
  +                && args.length == 1 &&
  +                (
  +                 (
  +                  "setLocation".equals(name) && org.apache.tools.ant.Location.class.equals(args[0])
  +                  ) || (
  +                   "setTaskType".equals(name) && java.lang.String.class.equals(args[0])
  +                  )
  +                 )) {
  +                continue;
  +            }
  +
  +            // hide addTask for TaskContainers
  +//              if (org.apache.tools.ant.TaskContainer.class.isAssignableFrom(bean)
  +//                  && args.length == 1 && "addTask".equals(name)
  +//                  && org.apache.tools.ant.Task.class.equals(args[0])) {
  +//                  continue;
  +//              }
  +
  +
  +            if ("addText".equals(name)
  +                && java.lang.Void.TYPE.equals(returnType)
  +                && args.length == 1
  +                && java.lang.String.class.equals(args[0])) {
  +
  +                addText = methods[i];
  +
  +            } else if (name.startsWith("set")
  +                       && java.lang.Void.TYPE.equals(returnType)
  +                       && args.length == 1
  +                       && !args[0].isArray()) {
  +
  +                String propName = getPropertyName(name, "set");
  +                if (attributeSetters.get(propName) != null) {
  +                    if (java.lang.String.class.equals(args[0])) {
  +                        /*
  +                            Ignore method m, as there is an overloaded
  +                            form of this method that takes in a
  +                            non-string argument, which gains higher
  +                            priority.
  +                        */
  +                        continue;
  +                    }
  +                    /*
  +                        If the argument is not a String, and if there
  +                        is an overloaded form of this method already defined,
  +                        we just override that with the new one.
  +                        This mechanism does not guarantee any specific order
  +                        in which the methods will be selected: so any code
  +                        that depends on the order in which "set" methods have
  +                        been defined, is not guaranteed to be selected in any
  +                        particular order.
  +                    */
  +                }
  +                AttributeSetter as = createAttributeSetter(m, args[0]);
  +                if (as != null) {
  +                    attributeTypes.put(propName, args[0]);
  +                    attributeSetters.put(propName, as);
  +                }
  +
  +            } else if (name.startsWith("create")
  +                       && !returnType.isArray()
  +                       && !returnType.isPrimitive()
  +                       && args.length == 0) {
  +
  +                String propName = getPropertyName(name, "create");
  +                nestedTypes.put(propName, returnType);
  +                nestedCreators.put(propName, new NestedCreator() {
  +
  +                        public Object create(Object parent)
  +                            throws InvocationTargetException,
  +                            IllegalAccessException {
  +
  +                            return m.invoke(parent, new Object[] {});
  +                        }
  +
  +                    });
  +                nestedStorers.remove(propName);
  +
  +            } else if (name.startsWith("addConfigured")
  +                       && java.lang.Void.TYPE.equals(returnType)
  +                       && args.length == 1
  +                       && !java.lang.String.class.equals(args[0])
  +                       && !args[0].isArray()
  +                       && !args[0].isPrimitive()) {
  +
  +                try {
  +                    final Constructor c =
  +                        args[0].getConstructor(new Class[] {});
  +                    String propName = getPropertyName(name, "addConfigured");
  +                    nestedTypes.put(propName, args[0]);
  +                    nestedCreators.put(propName, new NestedCreator() {
  +
  +                            public Object create(Object parent)
  +                                throws InvocationTargetException, 
  +				       IllegalAccessException, 
  +				       InstantiationException {
  +
  +                                Object o = c.newInstance(new Object[] {});
  +                                return o;
  +                            }
  +
  +                        });
  +                    nestedStorers.put(propName, new NestedStorer() {
  +
  +                            public void store(Object parent, Object child)
  +                                throws InvocationTargetException, 
  +				       IllegalAccessException, 
  +				       InstantiationException {
  +
  +                                m.invoke(parent, new Object[] {child});
  +                            }
  +
  +                        });
  +                } catch (NoSuchMethodException nse) {
  +                }
  +            } else if (name.startsWith("add")
  +                       && java.lang.Void.TYPE.equals(returnType)
  +                       && args.length == 1
  +                       && !java.lang.String.class.equals(args[0])
  +                       && !args[0].isArray()
  +                       && !args[0].isPrimitive()) {
  +
  +                try {
  +                    final Constructor c =
  +                        args[0].getConstructor(new Class[] {});
  +                    String propName = getPropertyName(name, "add");
  +                    nestedTypes.put(propName, args[0]);
  +                    nestedCreators.put(propName, new NestedCreator() {
  +
  +                            public Object create(Object parent)
  +                                throws InvocationTargetException, 
  +				       IllegalAccessException, 
  +				       InstantiationException {
  +
  +                                Object o = c.newInstance(new Object[] {});
  +                                m.invoke(parent, new Object[] {o});
  +                                return o;
  +                            }
  +
  +                        });
  +                    nestedStorers.remove(name);
  +                } catch (NoSuchMethodException nse) {
  +                }
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Factory method for helper objects.
  +     */
  +    public static synchronized IntrospectionHelper getHelper(Class c) {
  +        IntrospectionHelper ih = (IntrospectionHelper) helpers.get(c);
  +        if (ih == null) {
  +            ih = new IntrospectionHelper(c);
  +            helpers.put(c, ih);
  +        }
  +        return ih;
  +    }
  +
  +    /**
  +     * Sets the named attribute.
  +     */
  +    public void setAttribute(Project p, Object element, String attributeName,
  +                             String value)
  +        throws BuildException {
  +        AttributeSetter as = (AttributeSetter) attributeSetters.get(attributeName);
  +        if (as == null) {
  +            String msg = getElementName(p, element) +
  +            //String msg = "Class " + element.getClass().getName() +
  +                " doesn't support the \"" + attributeName + "\" attribute.";
  +            throw new BuildException(msg);
  +        }
  +        try {
  +            as.set(p, element, value);
  +        } catch (IllegalAccessException ie) {
  +            // impossible as getMethods should only return public methods
  +            throw new BuildException(ie);
  +        } catch (InvocationTargetException ite) {
  +            Throwable t = ite.getTargetException();
  +            if (t instanceof BuildException) {
  +                throw (BuildException) t;
  +            }
  +            throw new BuildException(t);
  +        }
  +    }
  +
  +    /**
  +     * Adds PCDATA areas.
  +     */
  +    public void addText(Project project, Object element, String text) {
  +        if (addText == null) {
  +            // Element doesn't handle text content
  +            if ( text.trim().length() == 0 ) {
  +                // Only whitespace - ignore
  +                return;
  +            }
  +            else {
  +                // Not whitespace - fail
  +                String msg = getElementName(project, element) +
  +                    " doesn't support nested text data.";
  +                throw new BuildException(msg);
  +            }
  +        }
  +        try {
  +            addText.invoke(element, new String[] {text});
  +        } catch (IllegalAccessException ie) {
  +            // impossible as getMethods should only return public methods
  +            throw new BuildException(ie);
  +        } catch (InvocationTargetException ite) {
  +            Throwable t = ite.getTargetException();
  +            if (t instanceof BuildException) {
  +                throw (BuildException) t;
  +            }
  +            throw new BuildException(t);
  +        }
  +    }
  +
  +    /**
  +     * Creates a named nested element.
  +     */
  +    public Object createElement(Project project, Object element, String elementName)
  +        throws BuildException {
  +	
  +        try {
  +	    // First check if there are any roles supported by this class
  +	    Object nestedElement = project.createInRole(element, elementName);
  +	    if (nestedElement == null) {
  +		NestedCreator nc = 
  +		    (NestedCreator) nestedCreators.get(elementName);
  +		if (nc == null) {
  +		    String msg = getElementName(project, element) +
  +			" doesn't support the nested \"" + elementName + 
  +			"\" element.";
  +		    throw new BuildException(msg);
  +		}
  +		nestedElement = nc.create(element);
  +	    }
  +            if (nestedElement instanceof ProjectComponent) {
  +                ((ProjectComponent) nestedElement).setProject(project);
  +            }
  +            return nestedElement;
  +        } catch (IllegalAccessException ie) {
  +            // impossible as getMethods should only return public methods
  +            throw new BuildException(ie);
  +        } catch (InstantiationException ine) {
  +            // impossible as getMethods should only return public methods
  +            throw new BuildException(ine);
  +        } catch (InvocationTargetException ite) {
  +            Throwable t = ite.getTargetException();
  +            if (t instanceof BuildException) {
  +                throw (BuildException) t;
  +            }
  +            throw new BuildException(t);
  +        }
  +    }
  +
  +    /**
  +     * Creates a named nested element.
  +     */
  +    public void storeElement(Project project, Object element, Object child, String elementName)
  +        throws BuildException {
  +        if (elementName == null) {
  +            return;
  +        }
  +        NestedStorer ns = (NestedStorer)nestedStorers.get(elementName);
  +        if (ns == null) {
  +            return;
  +        }
  +        try {
  +            ns.store(element, child);
  +        } catch (IllegalAccessException ie) {
  +            // impossible as getMethods should only return public methods
  +            throw new BuildException(ie);
  +        } catch (InstantiationException ine) {
  +            // impossible as getMethods should only return public methods
  +            throw new BuildException(ine);
  +        } catch (InvocationTargetException ite) {
  +            Throwable t = ite.getTargetException();
  +            if (t instanceof BuildException) {
  +                throw (BuildException) t;
  +            }
  +            throw new BuildException(t);
  +        }
  +    }
  +
  +    /**
  +     * returns the type of a named nested element.
  +     */
  +    public Class getElementType(String elementName)
  +        throws BuildException {
  +        Class nt = (Class) nestedTypes.get(elementName);
  +        if (nt == null) {
  +            String msg = "Class " + bean.getName() +
  +                " doesn't support the nested \"" + elementName + "\" element.";
  +            throw new BuildException(msg);
  +        }
  +        return nt;
  +    }
  +
  +    /**
  +     * returns the type of a named attribute.
  +     */
  +    public Class getAttributeType(String attributeName)
  +        throws BuildException {
  +        Class at = (Class) attributeTypes.get(attributeName);
  +        if (at == null) {
  +            String msg = "Class " + bean.getName() +
  +                " doesn't support the \"" + attributeName + "\" attribute.";
  +            throw new BuildException(msg);
  +        }
  +        return at;
  +    }
  +
  +    /**
  +     * Does the introspected class support PCDATA?
  +     */
  +    public boolean supportsCharacters() {
  +        return addText != null;
  +    }
  +
  +    /**
  +     * Return all attribues supported by the introspected class.
  +     */
  +    public Enumeration getAttributes() {
  +        return attributeSetters.keys();
  +    }
  +
  +    /**
  +     * Return all nested elements supported by the introspected class.
  +     */
  +    public Enumeration getNestedElements() {
  +        return nestedTypes.keys();
  +    }
  +
  +    /**
  +     * Create a proper implementation of AttributeSetter for the given
  +     * attribute type.
  +     */
  +    private AttributeSetter createAttributeSetter(final Method m,
  +                                                  final Class arg) {
  +
  +        // simplest case - setAttribute expects String
  +        if (java.lang.String.class.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent, new String[] {value});
  +                    }
  +                };
  +
  +        // now for the primitive types, use their wrappers
  +        } else if (java.lang.Character.class.equals(arg)
  +                   || java.lang.Character.TYPE.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent, new Character[] {new Character(value.charAt(0))});
  +                    }
  +
  +                };
  +        } else if (java.lang.Byte.TYPE.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent, new Byte[] {new Byte(value)});
  +                    }
  +
  +                };
  +        } else if (java.lang.Short.TYPE.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent, new Short[] {new Short(value)});
  +                    }
  +
  +                };
  +        } else if (java.lang.Integer.TYPE.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent, new Integer[] {new Integer(value)});
  +                    }
  +
  +                };
  +        } else if (java.lang.Long.TYPE.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent, new Long[] {new Long(value)});
  +                    }
  +
  +                };
  +        } else if (java.lang.Float.TYPE.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent, new Float[] {new Float(value)});
  +                    }
  +
  +                };
  +        } else if (java.lang.Double.TYPE.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent, new Double[] {new Double(value)});
  +                    }
  +
  +                };
  +
  +        // boolean gets an extra treatment, because we have a nice method
  +        // in Project
  +        } else if (java.lang.Boolean.class.equals(arg)
  +                   || java.lang.Boolean.TYPE.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent,
  +                                 new Boolean[] {new Boolean(Project.toBoolean(value))});
  +                    }
  +
  +                };
  +
  +        // Class doesn't have a String constructor but a decent factory method
  +        } else if (java.lang.Class.class.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException, BuildException {
  +                        try {
  +                            m.invoke(parent, new Class[] {Class.forName(value)});
  +                        } catch (ClassNotFoundException ce) {
  +                            throw new BuildException(ce);
  +                        }
  +                    }
  +                };
  +
  +        // resolve relative paths through Project
  +        } else if (java.io.File.class.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent, new File[] {p.resolveFile(value)});
  +                    }
  +
  +                };
  +
  +        // resolve relative paths through Project
  +        } else if (org.apache.tools.ant.types.Path.class.equals(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException {
  +                        m.invoke(parent, new Path[] {new Path(p, value)});
  +                    }
  +
  +                };
  +
  +        // EnumeratedAttributes have their own helper class
  +        } else if (org.apache.tools.ant.types.EnumeratedAttribute.class.isAssignableFrom(arg)) {
  +            return new AttributeSetter() {
  +                    public void set(Project p, Object parent, String value)
  +                        throws InvocationTargetException, IllegalAccessException, BuildException {
  +                        try {
  +                            org.apache.tools.ant.types.EnumeratedAttribute ea = (org.apache.tools.ant.types.EnumeratedAttribute)arg.newInstance();
  +                            ea.setValue(value);
  +                            m.invoke(parent, new EnumeratedAttribute[] {ea});
  +                        } catch (InstantiationException ie) {
  +                            throw new BuildException(ie);
  +                        }
  +                    }
  +                };
  +
  +        // worst case. look for a public String constructor and use it
  +        } else {
  +
  +            try {
  +                final Constructor c =
  +                    arg.getConstructor(new Class[] {java.lang.String.class});
  +
  +                return new AttributeSetter() {
  +                        public void set(Project p, Object parent,
  +                                        String value)
  +                            throws InvocationTargetException, IllegalAccessException, BuildException {
  +                            try {
  +                                Object attribute = c.newInstance(new String[] {value});
  +                                if (attribute instanceof ProjectComponent) {
  +                                    ((ProjectComponent) attribute).setProject(p);
  +                                }
  +                                m.invoke(parent, new Object[] {attribute});
  +                            } catch (InstantiationException ie) {
  +                                throw new BuildException(ie);
  +                            }
  +                        }
  +                    };
  +
  +            } catch (NoSuchMethodException nme) {
  +            }
  +        }
  +
  +        return null;
  +    }
  +
  +    protected String getElementName(Project project, Object element)
  +    {
  +        Hashtable elements = project.getTaskDefinitions();
  +        String typeName = "task";
  +        if (!elements.contains( element.getClass() ))
  +        {
  +            elements = project.getDataTypeDefinitions();
  +            typeName = "data type";
  +            if (!elements.contains( element.getClass() ))
  +            {
  +                elements = null;
  +            }
  +        }
  +
  +        if (elements != null)
  +        {
  +            Enumeration e = elements.keys();
  +            while (e.hasMoreElements())
  +            {
  +                String elementName = (String) e.nextElement();
  +                Class elementClass = (Class) elements.get( elementName );
  +                if ( element.getClass().equals( elementClass ) )
  +                {
  +                    return "The <" + elementName + "> " + typeName;
  +                }
  +            }
  +        }
  +
  +        return "Class " + element.getClass().getName();
  +    }
  +
  +    /**
  +     * extract the name of a property from a method name - subtracting
  +     * a given prefix.
  +     */
  +    private String getPropertyName(String methodName, String prefix) {
  +        int start = prefix.length();
  +        return methodName.substring(start).toLowerCase(Locale.US);
  +    }
  +
  +    private interface NestedCreator {
  +        Object create(Object parent)
  +            throws InvocationTargetException, IllegalAccessException, InstantiationException;
  +    }
  +
  +    private interface NestedStorer {
  +        void store(Object parent, Object child)
  +            throws InvocationTargetException, IllegalAccessException, InstantiationException;
  +    }
  +
  +    private interface AttributeSetter {
  +        void set(Project p, Object parent, String value)
  +            throws InvocationTargetException, IllegalAccessException,
  +                   BuildException;
  +    }
  +
  +    public void buildStarted(BuildEvent event) {}
  +    public void buildFinished(BuildEvent event) {
  +        attributeTypes.clear();
  +        attributeSetters.clear();
  +        nestedTypes.clear();
  +        nestedCreators.clear();
  +        addText = null;
  +        helpers.clear();
  +    }
  +
  +    public void targetStarted(BuildEvent event) {}
  +    public void targetFinished(BuildEvent event) {}
  +    public void taskStarted(BuildEvent event) {}
  +    public void taskFinished(BuildEvent event) {}
  +    public void messageLogged(BuildEvent event) {}
  +}
  
  
  
  1.4       +997 -550  jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java
  
  Index: Project.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- Project.java	2 Mar 2002 22:20:43 -0000	1.3
  +++ Project.java	3 Mar 2002 12:37:13 -0000	1.4
  @@ -1,57 +1,56 @@
   /*
  - * The Apache Software License, Version 1.1
  + *  The Apache Software License, Version 1.1
    *
  - * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
  - * reserved.
  + *  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:
  + *  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.
  + *  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.
  + *  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.
  + *  3. The end-user documentation included with the redistribution, if
  + *  any, must include the following acknowlegement:
  + *  "This product includes software developed by the
  + *  Apache Software Foundation (http://www.apache.org/)."
  + *  Alternately, this acknowlegement may appear in the software itself,
  + *  if and wherever such third-party acknowlegements normally appear.
    *
  - * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  - *    Foundation" must not be used to endorse or promote products derived
  - *    from this software without prior written permission. For written
  - *    permission, please contact apache@apache.org.
  + *  4. The names "The Jakarta Project", "Ant", and "Apache Software
  + *  Foundation" must not be used to endorse or promote products derived
  + *  from this software without prior written permission. For written
  + *  permission, please contact apache@apache.org.
    *
  - * 5. Products derived from this software may not be called "Apache"
  - *    nor may "Apache" appear in their names without prior written
  - *    permission of the Apache Group.
  + *  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 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/>.
  + *  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;
  @@ -63,35 +62,67 @@
   import java.util.Enumeration;
   import java.util.Stack;
   import java.lang.reflect.Modifier;
  +import java.lang.reflect.Method;
  +import java.lang.reflect.InvocationTargetException;
   
  -
  -import org.apache.tools.ant.types.DataTypeAdapterTask; 
  -import org.apache.tools.ant.types.FilterSet; 
  -import org.apache.tools.ant.types.FilterSetCollection; 
  -import org.apache.tools.ant.util.FileUtils; 
  +import org.apache.tools.ant.types.DataTypeAdapterTask;
  +import org.apache.tools.ant.types.FileSet;
  +import org.apache.tools.ant.types.FilterSet;
  +import org.apache.tools.ant.types.FilterSetCollection;
  +import org.apache.tools.ant.util.FileUtils;
  +import org.apache.tools.ant.types.Path;
  +import org.apache.tools.ant.taskdefs.Antlib;
   
   /**
  - * Central representation of an Ant project. This class defines a
  - * Ant project with all of it's targets and tasks. It also provides
  - * the mechanism to kick off a build using a particular target name.
  - * <p>
  - * This class also encapsulates methods which allow Files to be refered
  - * to using abstract path names which are translated to native system
  - * file paths at runtime as well as defining various project properties.
  + *  Central representation of an Ant project. This class defines a Ant project
  + *  with all of it's targets and tasks. It also provides the mechanism to kick
  + *  off a build using a particular target name. <p>
    *
  - * @author duncan@x180.com
  + *  This class also encapsulates methods which allow Files to be refered to
  + *  using abstract path names which are translated to native system file paths
  + *  at runtime as well as defining various project properties.
  + *
  + *@author     duncan@x180.com
  + *@author     j_a_fernandez@yahoo.com
  + *@created    February 27, 2002
    */
   
   public class Project {
   
  +    /**
  +     *  Description of the Field
  +     */
       public final static int MSG_ERR = 0;
  +    /**
  +     *  Description of the Field
  +     */
       public final static int MSG_WARN = 1;
  +    /**
  +     *  Description of the Field
  +     */
       public final static int MSG_INFO = 2;
  +    /**
  +     *  Description of the Field
  +     */
       public final static int MSG_VERBOSE = 3;
  +    /**
  +     *  Description of the Field
  +     */
       public final static int MSG_DEBUG = 4;
   
  +    /**
  +     *  LoaderId for the CoreLoader.
  +     */
  +    public final static String CORELOADER_ID = null;
  +
  +    /**
  +     *  Description of the Field
  +     */
       public final static String TASK_ROLE = "task";
  -    public final static String DATATYPE_ROLE = "datatype";
  +    /**
  +     *  Description of the Field
  +     */
  +    public final static String DATATYPE_ROLE = "data-type";
   
       // private set of constants to represent the state
       // of a DFS of the Target dependencies
  @@ -100,15 +131,38 @@
   
       private static String javaVersion;
   
  +    /**
  +     *  Description of the Field
  +     */
       public final static String JAVA_1_0 = "1.0";
  +    /**
  +     *  Description of the Field
  +     */
       public final static String JAVA_1_1 = "1.1";
  +    /**
  +     *  Description of the Field
  +     */
       public final static String JAVA_1_2 = "1.2";
  +    /**
  +     *  Description of the Field
  +     */
       public final static String JAVA_1_3 = "1.3";
  +    /**
  +     *  Description of the Field
  +     */
       public final static String JAVA_1_4 = "1.4";
   
  +    /**
  +     *  Description of the Field
  +     */
       public final static String TOKEN_START = FilterSet.DEFAULT_TOKEN_START;
  +    /**
  +     *  Description of the Field
  +     */
       public final static String TOKEN_END = FilterSet.DEFAULT_TOKEN_END;
   
  +    private final static String CORE_DEFINITIONS = "org/apache/tools/ant/antlib.xml";
  +
       private String name;
       private String description;
   
  @@ -126,13 +180,19 @@
   
       private Vector listeners = new Vector();
   
  -    /** The Ant core classloader - may be null if using system loader */    
  +    /**
  +     *  The Ant core classloader - may be null if using system loader
  +     */
       private ClassLoader coreLoader = null;
   
  -    /** Records the latest task on a thread */ 
  +    /**
  +     *  Records the latest task on a thread
  +     */
       private Hashtable threadTasks = new Hashtable();
   
  -    /** Store symbol tables */
  +    /**
  +     *  Store symbol tables
  +     */
       private SymbolTable symbols;
   
       static {
  @@ -161,179 +221,289 @@
   
       private FileUtils fileUtils;
   
  +
       /**
  -     * create a new ant project
  +     *  <p>
  +     *
  +     *  Description: The only reason for this class is to make the
  +     *  LoadDefinition method visible in this package.</p>
  +     *
  +     *@author     jfernandez
  +     *@created    February 27, 2002
  +     */
  +    private class Corelib extends Antlib {
  +        /**
  +         *  Constructor for the Corelib object
  +         */
  +        Corelib() {
  +            super(Project.this);
  +            setLoaderid(CORELOADER_ID);
  +            getOnerror().setValue("ignore");
  +        }
  +
  +
  +        /**
  +         *  Description of the Method
  +         */
  +        public void loadCoreDefinitions() {
  +            getOnerror().setValue("report");
  +            super.loadDefinitions(CORE_DEFINITIONS);
  +        }
  +    }
  +
  +
  +    /**
  +     *  create a new ant project
        */
       public Project() {
           fileUtils = FileUtils.newFileUtils();
  -	symbols = new SymbolTable();
  -	symbols.setProject(this);
  +        symbols = new SymbolTable();
  +        symbols.setProject(this);
       }
  -    
  +
  +
       /**
  -     * create a new ant project that inherits from caler project
  -     * @param p the calling project
  +     *  create a new ant project that inherits from caller project
  +     *
  +     *@param  p  the calling project
        */
  -    public Project(Project p) {
  +    private Project(Project p) {
           fileUtils = FileUtils.newFileUtils();
  -	symbols = new SymbolTable(p);
  -	symbols.setProject(this);
  +        symbols = new SymbolTable(p.getSymbols());
  +        symbols.setProject(this);
  +        setCoreLoader(p.getCoreLoader());
       }
  -    
  +
  +
       /**
  -     * Initialise the project.
  -     *
  -     * This involves setting the default task definitions and loading the
  -     * system properties.
  +     *  Loads the core definitions into the Root project.
        */
  -    public void init() throws BuildException {
  -        setJavaVersionProperty();
  -        
  -	// Initialize simbol table just in case
  -	symbols.addRole("task", TaskContainer.class, TaskAdapter.class);
  -	symbols.addRole("datatype", TaskContainer.class, 
  -			DataTypeAdapterTask.class);
  +    private void loadDefinitions() {
  +        // Initialize symbol table just in case
  +        symbols.addRole(TASK_ROLE, TaskContainer.class, TaskAdapter.class);
  +        symbols.addRole(DATATYPE_ROLE, TaskContainer.class,
  +                DataTypeAdapterTask.class);
   
  -        String defs = "/org/apache/tools/ant/taskdefs/defaults.properties";
  +        Corelib load = new Corelib();
  +        load.loadDefinitions();
   
  -        try {
  -            Properties props = new Properties();
  -            InputStream in = this.getClass().getResourceAsStream(defs);
  -            if (in == null) { 
  -                throw new BuildException("Can't load default task list");
  -            }
  -            props.load(in);
  -            in.close();
  +        // If the most basic of tasks, "property", is not defined
  +        // then there was no antlib jars from where to load the descriptors
  +        // we should be doing bootstrapping, or ant.jar is not an antlib.
  +        if (!isDefinedOnRole(TASK_ROLE, "property")) {
  +            load.loadCoreDefinitions();
   
  -            Enumeration enum = props.propertyNames();
  -            while (enum.hasMoreElements()) {
  -                String key = (String) enum.nextElement();
  -                String value = props.getProperty(key);
  -                try {
  -                    Class taskClass = Class.forName(value);
  -                    addTaskDefinition(key, taskClass);
  -                } catch (NoClassDefFoundError ncdfe) {
  -                    log("Could not load a dependent class (" + 
  -			ncdfe.getMessage() + ") for task " + key, MSG_DEBUG); 
  -                } catch (ClassNotFoundException cnfe) {
  -                    log("Could not load class (" 
  -			+ value + ") for task " + key, MSG_DEBUG); 
  -                }
  +            if (!isDefinedOnRole(TASK_ROLE, "property")) {
  +                throw new BuildException("Can't load core definitions");
               }
  -        } catch (IOException ioe) {
  -            throw new BuildException("Can't load default task list");
           }
  +        autoLoadDefinitions();
  +    }
   
  -        String dataDefs = "/org/apache/tools/ant/types/defaults.properties";
  +    private void autoLoadDefinitions() {
  +        DirectoryScanner ds = new DirectoryScanner();
  +        ds.setBasedir(new File(getProperty("ant.home"),"autolib"));
  +        ds.scan();
  +        String dirs[] = ds.getIncludedDirectories();
  +        for (int i = 0; i < dirs.length; i++) {
  +            autoLoad(ds.getBasedir(), dirs[i]);
  +        }
  +    }
   
  -        try{
  -            Properties props = new Properties();
  -            InputStream in = this.getClass().getResourceAsStream(dataDefs);
  -            if (in == null) { 
  -                throw new BuildException("Can't load default datatype list");
  -            }
  -            props.load(in);
  -            in.close();
  +    private void autoLoad(File base, String dir) {
  +        FileSet fs = new FileSet();
  +        fs.setProject(this);
  +        fs.setDir(new File(base, dir));
  +        fs.setIncludes("*.jar");
   
  -            Enumeration enum = props.propertyNames();
  -            while (enum.hasMoreElements()) {
  -                String key = (String) enum.nextElement();
  -                String value = props.getProperty(key);
  -                try {
  -                    Class dataClass = Class.forName(value);
  -                    addDataTypeDefinition(key, dataClass);
  -                } catch (NoClassDefFoundError ncdfe) {
  -                    // ignore...
  -                } catch (ClassNotFoundException cnfe) {
  -                    // ignore...
  -                }
  -            }
  -        } catch (IOException ioe) {
  -            throw new BuildException("Can't load default datatype list");
  +        Path cp = new Path(this);
  +        cp.addFileset(fs);
  +        if (cp.size() == 0) {
  +            return;
           }
   
  +        Antlib.FailureAction fa = new Antlib.FailureAction();
  +        fa.setValue("ignore");
  +
  +        Antlib lib = new Antlib(this);
  +        lib.setClasspath(cp);
  +        lib.setLoaderid(dir);
  +        lib.setOnerror(fa);
  +        lib.loadDefinitions();
  +    }
  +
  +    /**
  +     *  Creates a subproject of the current project.
  +     *
  +     *@return    Description of the Return Value
  +     */
  +    public Project createSubProject() {
  +        return new Project(this);
  +    }
  +
  +
  +    /**
  +     *  Initialise the project. This involves setting the default task
  +     *  definitions and loading the system properties.
  +     *
  +     *@exception  BuildException  Description of the Exception
  +     */
  +    public void init() throws BuildException {
  +        setJavaVersionProperty();
           setSystemProperties();
  +        if (!isRoleDefined(TASK_ROLE)) {
  +            // Top project, need to load the core definitions
  +            loadDefinitions();
  +        }
       }
   
  +
  +    /**
  +     *  Sets the CoreLoader to the default of the Project object
  +     */
  +    private void setDefaultCoreLoader() {
  +        coreLoader = this.getClass().getClassLoader();
  +        if (coreLoader == null) {
  +            // This should only happen if ANT is being
  +            // loader by the Bootstrap classloader
  +            // This may be the case in JDK 1.1
  +            coreLoader = ClassLoader.getSystemClassLoader();
  +        }
  +    }
  +
  +
  +    /**
  +     *  Sets the coreLoader attribute of the Project object
  +     *
  +     *@param  coreLoader  The new coreLoader value
  +     */
       public void setCoreLoader(ClassLoader coreLoader) {
  -        this.coreLoader = coreLoader;
  +        if (coreLoader == null) {
  +            setDefaultCoreLoader();
  +        }
  +        else this.coreLoader = coreLoader;
       }
  -    
  +
  +
  +    /**
  +     *  Gets the coreLoader attribute of the Project object
  +     *
  +     *@return    The coreLoader value
  +     */
       public ClassLoader getCoreLoader() {
  +        if (coreLoader == null) {
  +            setDefaultCoreLoader();
  +        }
           return coreLoader;
       }
  -    
  +
  +
  +    /**
  +     *  Adds a feature to the BuildListener attribute of the Project object
  +     *
  +     *@param  listener  The feature to be added to the BuildListener attribute
  +     */
       public void addBuildListener(BuildListener listener) {
           listeners.addElement(listener);
       }
   
  +
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  listener  Description of the Parameter
  +     */
       public void removeBuildListener(BuildListener listener) {
           listeners.removeElement(listener);
       }
   
  +
  +    /**
  +     *  Gets the buildListeners attribute of the Project object
  +     *
  +     *@return    The buildListeners value
  +     */
       public Vector getBuildListeners() {
           return listeners;
       }
   
  +
       /**
  -     * Get the symbols associated with this project.
  +     *  Get the symbols associated with this project.
  +     *
  +     *@return    The symbols value
        */
  -    public SymbolTable getSymbols() {
  -	return symbols;
  +    private SymbolTable getSymbols() {
  +        // Package protected on purpose
  +        return symbols;
       }
   
  +
       /**
  -     * Output a message to the log with the default log level
  -     * of MSG_INFO
  -     * @param msg text to log
  +     *  Output a message to the log with the default log level of MSG_INFO
  +     *
  +     *@param  msg  text to log
        */
  -     
  +
       public void log(String msg) {
           log(msg, MSG_INFO);
       }
   
  +
       /**
  -     * Output a message to the log with the given log level
  -     * and an event scope of project
  -     * @param msg text to log
  -     * @param msgLevel level to log at 
  +     *  Output a message to the log with the given log level and an event scope
  +     *  of project
  +     *
  +     *@param  msg       text to log
  +     *@param  msgLevel  level to log at
        */
       public void log(String msg, int msgLevel) {
           fireMessageLogged(this, msg, msgLevel);
       }
   
  +
       /**
  -     * Output a message to the log with the given log level
  -     * and an event scope of a task
  -     * @param task task to use in the log
  -     * @param msg text to log
  -     * @param msgLevel level to log at 
  +     *  Output a message to the log with the given log level and an event scope
  +     *  of a task
  +     *
  +     *@param  task      task to use in the log
  +     *@param  msg       text to log
  +     *@param  msgLevel  level to log at
        */
       public void log(Task task, String msg, int msgLevel) {
           fireMessageLogged(task, msg, msgLevel);
       }
   
  +
       /**
  -     * Output a message to the log with the given log level
  -     * and an event scope of a target
  -     * @param target target to use in the log
  -     * @param msg text to log
  -     * @param msgLevel level to log at 
  +     *  Output a message to the log with the given log level and an event scope
  +     *  of a target
  +     *
  +     *@param  target    target to use in the log
  +     *@param  msg       text to log
  +     *@param  msgLevel  level to log at
        */
       public void log(Target target, String msg, int msgLevel) {
           fireMessageLogged(target, msg, msgLevel);
       }
   
  -  
  +
  +    /**
  +     *  Gets the globalFilterSet attribute of the Project object
  +     *
  +     *@return    The globalFilterSet value
  +     */
       public FilterSet getGlobalFilterSet() {
           return globalFilterSet;
       }
  -    
  +
  +
       /**
  -     * set a property. Any existing property of the same name 
  -     * is overwritten, unless it is a user property. 
  -     * @param name name of property
  -     * @param value new value of the property
  +     *  set a property. Any existing property of the same name is overwritten,
  +     *  unless it is a user property.
  +     *
  +     *@param  name   name of property
  +     *@param  value  new value of the property
        */
       public void setProperty(String name, String value) {
           // command line properties take precedence
  @@ -343,21 +513,23 @@
           }
   
           if (null != properties.get(name)) {
  -            log("Overriding previous definition of property " + name, 
  -                MSG_VERBOSE);
  +            log("Overriding previous definition of property " + name,
  +                    MSG_VERBOSE);
           }
   
           log("Setting project property: " + name + " -> " +
  -             value, MSG_DEBUG);
  +                value, MSG_DEBUG);
           properties.put(name, value);
       }
   
  +
       /**
  -     * set a property. An existing property of the same name 
  -     * will not be overwritten.
  -     * @param name name of property
  -     * @param value new value of the property
  -     * @since 1.5
  +     *  set a property. An existing property of the same name will not be
  +     *  overwritten.
  +     *
  +     *@param  name   name of property
  +     *@param  value  new value of the property
  +     *@since         1.5
        */
       public void setNewProperty(String name, String value) {
           if (null != properties.get(name)) {
  @@ -365,26 +537,34 @@
               return;
           }
           log("Setting project property: " + name + " -> " +
  -            value, MSG_DEBUG);
  +                value, MSG_DEBUG);
           properties.put(name, value);
       }
   
  +
       /**
  -     * set a user property, which can not be overwritten by
  -     * set/unset property calls
  -     * @see #setProperty(String,String)
  +     *  set a user property, which can not be overwritten by set/unset property
  +     *  calls
  +     *
  +     *@param  name   The new userProperty value
  +     *@param  value  The new userProperty value
  +     *@see           #setProperty(String,String)
        */
       public void setUserProperty(String name, String value) {
           log("Setting ro project property: " + name + " -> " +
  -            value, MSG_DEBUG);
  +                value, MSG_DEBUG);
           userProperties.put(name, value);
           properties.put(name, value);
       }
  -    
  +
  +
       /**
  -     * Allows Project and subclasses to set a property unless its
  -     * already defined as a user property. There are a few cases 
  -     * internally to Project that need to do this currently.
  +     *  Allows Project and subclasses to set a property unless its already
  +     *  defined as a user property. There are a few cases internally to Project
  +     *  that need to do this currently.
  +     *
  +     *@param  name   The new propertyInternal value
  +     *@param  value  The new propertyInternal value
        */
       private void setPropertyInternal(String name, String value) {
           if (null != userProperties.get(name)) {
  @@ -393,182 +573,226 @@
           properties.put(name, value);
       }
   
  +
       /**
  -     * query a property.
  -     * @param name the name of the property
  -     * @return the property value, or null for no match
  +     *  query a property.
  +     *
  +     *@param  name  the name of the property
  +     *@return       the property value, or null for no match
        */
       public String getProperty(String name) {
           if (name == null) {
  -          return null;
  +            return null;
           }
           String property = (String) properties.get(name);
           return property;
       }
   
  +
       /**
  -     * Replace ${} style constructions in the given value with the
  -     * string value of the corresponding data types.
  +     *  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.
  +     *@param  value               the string to be scanned for property
  +     *      references.
  +     *@return                     Description of the Return Value
  +     *@exception  BuildException  Description of the Exception
        */
       public String replaceProperties(String value)
  -        throws BuildException { 
  +             throws BuildException {
           return ProjectHelper.replaceProperties(this, value, properties);
       }
   
  +
       /**
  -     * query a user property.
  -     * @param name the name of the property
  -     * @return the property value, or null for no match
  +     *  query a user property.
  +     *
  +     *@param  name  the name of the property
  +     *@return       the property value, or null for no match
        */
       public String getUserProperty(String name) {
           if (name == null) {
  -          return null;
  +            return null;
           }
           String property = (String) userProperties.get(name);
           return property;
       }
   
  +
       /**
  -     * get a copy of the property hashtable
  -     * @return the hashtable containing all properties, user included
  +     *  get a copy of the property hashtable
  +     *
  +     *@return    the hashtable containing all properties, user included
        */
       public Hashtable getProperties() {
           Hashtable propertiesCopy = new Hashtable();
  -        
  +
           Enumeration e = properties.keys();
           while (e.hasMoreElements()) {
               Object name = e.nextElement();
               Object value = properties.get(name);
               propertiesCopy.put(name, value);
           }
  -        
  +
           return propertiesCopy;
       }
   
  +
       /**
  -     * get a copy of the user property hashtable
  -     * @return the hashtable user properties only
  +     *  get a copy of the user property hashtable
  +     *
  +     *@return    the hashtable user properties only
        */
       public Hashtable getUserProperties() {
           Hashtable propertiesCopy = new Hashtable();
  -        
  +
           Enumeration e = userProperties.keys();
           while (e.hasMoreElements()) {
               Object name = e.nextElement();
               Object value = properties.get(name);
               propertiesCopy.put(name, value);
           }
  -        
  +
           return propertiesCopy;
       }
   
  +
       /**
  -     * set the default target of the project
  -     * @deprecated, use setDefault
  -     * @see #setDefault(String)
  +     *  set the default target of the project
  +     *
  +     *@param  defaultTarget  The new defaultTarget value
  +     *@deprecated,           use setDefault
  +     *@see                   #setDefault(String)
        */
       public void setDefaultTarget(String defaultTarget) {
           this.defaultTarget = defaultTarget;
       }
   
  +
       /**
  -     * get the default target of the project
  -     * @return default target or null
  +     *  get the default target of the project
  +     *
  +     *@return    default target or null
        */
       public String getDefaultTarget() {
           return defaultTarget;
       }
   
  -    
  +
       /**
  -     * set the default target of the project
  -     * XML attribute name.
  +     *  set the default target of the project XML attribute name.
  +     *
  +     *@param  defaultTarget  The new default value
        */
       public void setDefault(String defaultTarget) {
           this.defaultTarget = defaultTarget;
       }
   
  +
       /**
  -     * ant xml property. Set the project name as
  -     * an attribute of this class, and of the property
  -     * ant.project.name
  +     *  ant xml property. Set the project name as an attribute of this class,
  +     *  and of the property ant.project.name
  +     *
  +     *@param  name  The new name value
        */
       public void setName(String name) {
  -        setUserProperty("ant.project.name",  name);
  +        setUserProperty("ant.project.name", name);
           this.name = name;
       }
   
  -    /** get the project name
  -     * @return name string
  +
  +    /**
  +     *  get the project name
  +     *
  +     *@return    name string
        */
       public String getName() {
           return name;
       }
   
  -    /** set the project description
  -     * @param description text
  +
  +    /**
  +     *  set the project description
  +     *
  +     *@param  description  text
        */
       public void setDescription(String description) {
           this.description = description;
       }
   
  -    /** get the project description
  -     * @return description or null if no description has been set
  +
  +    /**
  +     *  get the project description
  +     *
  +     *@return    description or null if no description has been set
        */
       public String getDescription() {
           return description;
       }
   
  -    /** @deprecated */
  +
  +    /**
  +     *@param  token  The feature to be added to the Filter attribute
  +     *@param  value  The feature to be added to the Filter attribute
  +     *@deprecated
  +     */
       public void addFilter(String token, String value) {
           if (token == null) {
               return;
           }
  -        
  +
           globalFilterSet.addFilter(new FilterSet.Filter(token, value));
       }
   
  -    /** @deprecated */
  +
  +    /**
  +     *@return        The filters value
  +     *@deprecated
  +     */
       public Hashtable getFilters() {
           // we need to build the hashtable dynamically
           return globalFilterSet.getFilterHash();
       }
   
  +
       /**
  -     * match basedir attribute in xml
  -     * @param baseD project base directory.
  -     * @throws BuildException if the directory was invalid
  +     *  match basedir attribute in xml
  +     *
  +     *@param  baseD            project base directory.
  +     *@throws  BuildException  if the directory was invalid
        */
       public void setBasedir(String baseD) throws BuildException {
           setBaseDir(new File(baseD));
       }
   
  +
       /**
  -     * set the base directory; XML attribute. 
  -     * checks for the directory existing and being a directory type
  -     * @param baseDir project base directory.
  -     * @throws BuildException if the directory was invalid
  +     *  set the base directory; XML attribute. checks for the directory existing
  +     *  and being a directory type
  +     *
  +     *@param  baseDir          project base directory.
  +     *@throws  BuildException  if the directory was invalid
        */
       public void setBaseDir(File baseDir) throws BuildException {
           baseDir = fileUtils.normalize(baseDir.getAbsolutePath());
  -        if (!baseDir.exists()) { 
  +        if (!baseDir.exists()) {
               throw new BuildException("Basedir " + baseDir.getAbsolutePath() + " does not exist");
           }
  -        if (!baseDir.isDirectory()) { 
  +        if (!baseDir.isDirectory()) {
               throw new BuildException("Basedir " + baseDir.getAbsolutePath() + " is not a directory");
           }
           this.baseDir = baseDir;
  -        setPropertyInternal( "basedir", this.baseDir.getPath());
  +        setPropertyInternal("basedir", this.baseDir.getPath());
           String msg = "Project base dir set to: " + this.baseDir;
           log(msg, MSG_VERBOSE);
       }
   
  +
       /**
  -     * get the base directory of the project as a file object
  -     * @return the base directory. If this is null, then the base
  -     * dir is not valid
  +     *  get the base directory of the project as a file object
  +     *
  +     *@return    the base directory. If this is null, then the base dir is not
  +     *      valid
        */
       public File getBaseDir() {
           if (baseDir == null) {
  @@ -581,18 +805,22 @@
           return baseDir;
       }
   
  +
       /**
  -     * static query of the java version
  -     * @return something like "1.1" or "1.3"
  +     *  static query of the java version
  +     *
  +     *@return    something like "1.1" or "1.3"
        */
       public static String getJavaVersion() {
           return javaVersion;
       }
   
  +
       /**
  -     * set the ant.java.version property, also tests for 
  -     * unsupported JVM versions, prints the verbose log messages
  -     * @throws BuildException if this Java version is not supported
  +     *  set the ant.java.version property, also tests for unsupported JVM
  +     *  versions, prints the verbose log messages
  +     *
  +     *@throws  BuildException  if this Java version is not supported
        */
       public void setJavaVersionProperty() throws BuildException {
           setPropertyInternal("ant.java.version", javaVersion);
  @@ -607,163 +835,202 @@
           log("Detected OS: " + System.getProperty("os.name"), MSG_VERBOSE);
       }
   
  +
  +    /**
  +     *  turn all the system properties into ant properties. user properties
  +     *  still override these values
  +     */
  +    public void setSystemProperties() {
  +        Properties systemP = System.getProperties();
  +        Enumeration e = systemP.keys();
  +        while (e.hasMoreElements()) {
  +            Object name = e.nextElement();
  +            String value = systemP.get(name).toString();
  +            this.setPropertyInternal(name.toString(), value);
  +        }
  +    }
  +
  +
  +    /**
  +     *  Adds a feature to the ToLoader attribute of the Project object
  +     *
  +     *@param  loader  The feature to be added to the ToLoader attribute
  +     *@param  path    The feature to be added to the ToLoader attribute
  +     *@return         Description of the Return Value
  +     */
       public ClassLoader addToLoader(String loader, Path path) {
  -	return symbols.addToLoader(loader, path);
  +        if (loader == CORELOADER_ID) {
  +            // It is not possible to add more libraries to the CoreLoader
  +            // Just return it as is.
  +            return getCoreLoader();
  +        }
  +        return symbols.addToLoader(loader, path);
       }
   
  -    public boolean addRoleDefinition(String role, 
  -				     Class roleClass, Class adapter)
  -    {
  -	return symbols.addRole(role, roleClass, adapter);
  +
  +    /**
  +     *  Adds a feature to the RoleDefinition attribute of the Project object
  +     *
  +     *@param  role       The feature to be added to the RoleDefinition attribute
  +     *@param  roleClass  The feature to be added to the RoleDefinition attribute
  +     *@param  adapter    The feature to be added to the RoleDefinition attribute
  +     *@return            Description of the Return Value
  +     */
  +    public boolean addRoleDefinition(String role,
  +            Class roleClass, Class adapter) {
  +        return symbols.addRole(role, roleClass, adapter);
       }
   
  +
       /**
  -     * test for a role name being in use already
  +     *  test for a role name being in use already
        *
  -     * @param name the name to test
  -     * @return true if it is a task or a datatype
  +     *@param  name  the name to test
  +     *@return       true if it is a task or a datatype
        */
       public boolean isRoleDefined(String name) {
  -	return (symbols.getRole(name) != null);
  +        return (symbols.getRole(name) != null);
       }
   
  +
  +    /**
  +     *  Adds a feature to the DefinitionOnRole attribute of the Project object
  +     *
  +     *@param  role  The feature to be added to the DefinitionOnRole attribute
  +     *@param  type  The feature to be added to the DefinitionOnRole attribute
  +     *@param  clz   The feature to be added to the DefinitionOnRole attribute
  +     */
       public void addDefinitionOnRole(String role,
  -				    String type, Class clz)
  -    {
  -	Class old = symbols.add(role, type, clz);
  -	// Special management for Tasks
  +            String type, Class clz) {
  +        Class old = symbols.add(role, type, clz);
  +        // Special management for Tasks
           if (TASK_ROLE.equals(role) && null != old && !old.equals(clz)) {
  -	    invalidateCreatedTasks(type);
  +            invalidateCreatedTasks(type);
           }
       }
   
  +
       /**
  -     * test for a name being in use already on this role
  +     *  test for a name being in use already on this role
        *
  -     * @param name the name to test
  -     * @return true if it is a task or a datatype
  +     *@param  name  the name to test
  +     *@param  role  Description of the Parameter
  +     *@return       true if it is a task or a datatype
        */
       public boolean isDefinedOnRole(String role, String name) {
  -	return (symbols.get(role, name) != null);
  +        return (symbols.get(role, name) != null);
       }
  -    
  +
  +
       /**
  -     * turn all the system properties into ant properties.
  -     * user properties still override these values
  +     *  add a new task definition, complain if there is an overwrite attempt
  +     *
  +     *@param  taskName         name of the task
  +     *@param  taskClass        full task classname
  +     *@throws  BuildException  and logs as Project.MSG_ERR for conditions, that
  +     *      will cause the task execution to fail.
        */
  -    public void setSystemProperties() {
  -        Properties systemP = System.getProperties();
  -        Enumeration e = systemP.keys();
  -        while (e.hasMoreElements()) {
  -            Object name = e.nextElement();
  -            String value = systemP.get(name).toString();
  -            this.setPropertyInternal(name.toString(), value);
  -        }
  +    public void addTaskDefinition(String taskName, Class taskClass)
  +             throws BuildException {
  +        addDefinitionOnRole(TASK_ROLE, taskName, taskClass);
       }
   
  -    /**
  -     * add a new task definition, complain if there is an overwrite attempt
  -     * @param taskName name of the task
  -     * @param taskClass full task classname
  -     * @throws BuildException and logs as Project.MSG_ERR for
  -     * conditions, that will cause the task execution to fail.
  -     */
  -    public void addTaskDefinition(String taskName, Class taskClass) 
  -	throws BuildException {
  -	Class old = symbols.add("task", taskName, taskClass);
  -        if (null != old && !old.equals(taskClass)) {
  -	    invalidateCreatedTasks(taskName);
  -        }
  -
  -        String msg = 
  -	    " +User task: " + taskName + "     " + taskClass.getName();
  -        log(msg, MSG_DEBUG);
  -        checkTaskClass(taskClass); 
  -    }
   
       /**
  -     * Checks a class, whether it is suitable for serving as ant task.
  -     * @throws BuildException and logs as Project.MSG_ERR for
  -     * conditions, that will cause the task execution to fail.
  +     *  Checks a class, whether it is suitable for serving as ant task.
  +     *
  +     *@param  taskClass        Description of the Parameter
  +     *@throws  BuildException  and logs as Project.MSG_ERR for conditions, that
  +     *      will cause the task execution to fail.
  +     *@deprecated              this is done now when added to SymbolTable
        */
       public void checkTaskClass(final Class taskClass) throws BuildException {
  -        if( !Task.class.isAssignableFrom(taskClass) ) {
  +        if (!Task.class.isAssignableFrom(taskClass)) {
               TaskAdapter.checkTaskClass(taskClass, this);
           }
       }
   
  +
       /**
  -     * get the current task definition hashtable
  +     *  get the current task definition hashtable
  +     *
  +     *@return    The taskDefinitions value
        */
       public Hashtable getTaskDefinitions() {
  -        return symbols.getTaskDefinitions();
  +        return symbols.getDefinitions(TASK_ROLE);
       }
   
  +
       /**
  -     * add a new datatype
  -     * @param typeName name of the datatype
  -     * @param typeClass full datatype classname     
  +     *  add a new datatype
  +     *
  +     *@param  typeName   name of the datatype
  +     *@param  typeClass  full datatype classname
        */
       public void addDataTypeDefinition(String typeName, Class typeClass) {
  -	symbols.add("datatype", typeName, typeClass);
  -
  -        String msg = 
  -	    " +User datatype: " + typeName + "     " + typeClass.getName();
  -        log(msg, MSG_DEBUG);
  +        addDefinitionOnRole(DATATYPE_ROLE, typeName, typeClass);
       }
   
  +
       /**
  -     * get the current task definition hashtable
  +     *  get the current task definition hashtable
  +     *
  +     *@return    The dataTypeDefinitions value
        */
       public Hashtable getDataTypeDefinitions() {
  -        return symbols.getDataTypeDefinitions();
  +        return symbols.getDefinitions(DATATYPE_ROLE);
       }
   
  +
       /**
  -     * This call expects to add a <em>new</em> Target.
  -     * @param target is the Target to be added to the current
  -     * Project.
  -     * @exception BuildException if the Target already exists
  -     * in the project.
  -     * @see Project#addOrReplaceTarget to replace existing Targets.
  +     *  This call expects to add a <em>new</em> Target.
  +     *
  +     *@param  target              is the Target to be added to the current
  +     *      Project.
  +     *@see                        Project#addOrReplaceTarget to replace existing
  +     *      Targets.
        */
       public void addTarget(Target target) {
           String name = target.getName();
           if (targets.get(name) != null) {
  -            throw new BuildException("Duplicate target: `"+name+"'");
  +            throw new BuildException("Duplicate target: `" + name + "'");
           }
           addOrReplaceTarget(name, target);
       }
   
  +
       /**
  -     * This call expects to add a <em>new</em> Target.
  -     * @param target is the Target to be added to the current
  -     * Project.
  -     * @param targetName is the name to use for the Target
  -     * @exception BuildException if the Target already exists
  -     * in the project.
  -     * @see Project#addOrReplaceTarget to replace existing Targets.
  -     */
  -     public void addTarget(String targetName, Target target)
  -         throws BuildException {
  -         if (targets.get(targetName) != null) {
  -             throw new BuildException("Duplicate target: `"+targetName+"'");
  -         }
  -         addOrReplaceTarget(targetName, target);
  -     }
  +     *  This call expects to add a <em>new</em> Target.
  +     *
  +     *@param  target              is the Target to be added to the current
  +     *      Project.
  +     *@param  targetName          is the name to use for the Target
  +     *@exception  BuildException  if the Target already exists in the project.
  +     *@see                        Project#addOrReplaceTarget to replace existing
  +     *      Targets.
  +     */
  +    public void addTarget(String targetName, Target target)
  +             throws BuildException {
  +        if (targets.get(targetName) != null) {
  +            throw new BuildException("Duplicate target: `" + targetName + "'");
  +        }
  +        addOrReplaceTarget(targetName, target);
  +    }
  +
   
       /**
  -     * @param target is the Target to be added or replaced in
  -     * the current Project.
  +     *@param  target  is the Target to be added or replaced in the current
  +     *      Project.
        */
       public void addOrReplaceTarget(Target target) {
           addOrReplaceTarget(target.getName(), target);
       }
   
  +
       /**
  -     * @param target is the Target to be added/replaced in
  -     * the current Project.
  -     * @param targetName is the name to use for the Target
  +     *@param  target      is the Target to be added/replaced in the current
  +     *      Project.
  +     *@param  targetName  is the name to use for the Target
        */
       public void addOrReplaceTarget(String targetName, Target target) {
           String msg = " +Target: " + targetName;
  @@ -772,59 +1039,123 @@
           targets.put(targetName, target);
       }
   
  +
       /**
  -     * get the target hashtable
  -     * @return hashtable, the contents of which can be cast to Target
  +     *  get the target hashtable
  +     *
  +     *@return    hashtable, the contents of which can be cast to Target
        */
       public Hashtable getTargets() {
           return targets;
       }
   
  -    /**
  -     * create a new task instance
  -     * @param taskType name of the task
  -     * @throws BuildException when task creation goes bad
  -     * @return null if the task name is unknown
  -     */
  -    public Task createTask(String taskType) throws BuildException {
  -        Class c = symbols.get("task", taskType);
   
  -        if (c == null) {
  +    /**
  +     *  Create a new element instance on a Role
  +     *
  +     *@param  role  name of the role to use
  +     *@param  type  name of the element to create
  +     *@return       null if element unknown on this role
  +     */
  +    public Object createForRole(String role, String type) {
  +        SymbolTable.Factory f = symbols.get(role, type);
  +        if (f == null) {
               return null;
           }
  -        
  +
           try {
  -            Object o = c.newInstance();
  -            Task task = null;
  -            if( o instanceof Task ) {
  -               task=(Task)o;
  -            } else {
  -                // "Generic" Bean - use the setter pattern
  -                // and an Adapter
  -                TaskAdapter taskA=new TaskAdapter();
  -                taskA.setProxy( o );
  -                task=taskA;
  +            Object o = f.create(this);
  +            // Do special book keeping for ProjectComponents
  +            if (o instanceof ProjectComponent) {
  +                ((ProjectComponent) o).setProject(this);
  +                if (o instanceof Task) {
  +                    Task task = (Task) o;
  +                    task.setTaskType(type);
  +
  +                    // set default value, can be changed by the user
  +                    task.setTaskName(type);
  +                    addCreatedTask(type, task);
  +                }
               }
  -            task.setProject(this);
  -            task.setTaskType(taskType);
  -
  -            // set default value, can be changed by the user
  -            task.setTaskName(taskType);
  -
  -            String msg = "   +Task: " + taskType;
  -            log (msg, MSG_DEBUG);
  -            addCreatedTask(taskType, task);
  -            return task;
  +            String msg = "   +" + role + ": " + type;
  +            log(msg, MSG_DEBUG);
  +            return o;
           } catch (Throwable t) {
  -            String msg = "Could not create task of type: "
  -                 + taskType + " due to " + t;
  +            String msg = "Could not create " + role + " of type: "
  +                     + type + " due to " + t;
               throw new BuildException(msg, t);
           }
       }
   
  +
  +    /**
  +     *@param  container  Description of the Parameter
  +     *@param  type       Description of the Parameter
  +     *@return            Description of the Return Value
  +     */
  +    public Object createInRole(Object container, String type) {
  +        Class clz = container.getClass();
  +        String roles[] = symbols.findRoles(clz);
  +        Object theOne = null;
  +        Method add = null;
  +
  +        for (int i = 0; i < roles.length; i++) {
  +            Object o = createForRole(roles[i], type);
  +            if (o != null) {
  +                if (theOne != null) {
  +                    String msg = "Element " + type +
  +                            " is ambiguous for container " + clz.getName();
  +                    if (theOne instanceof RoleAdapter) {
  +                        theOne = ((RoleAdapter) theOne).getProxy();
  +                    }
  +                    if (o instanceof RoleAdapter) {
  +                        o = ((RoleAdapter) o).getProxy();
  +                    }
  +
  +                    log(msg, MSG_ERR);
  +                    log("cannot distinguish between " +
  +                            theOne.getClass().getName() +
  +                            " and " + o.getClass().getName(), MSG_ERR);
  +                    throw new BuildException(msg);
  +                }
  +                theOne = o;
  +                add = symbols.getRole(roles[i]).getInterfaceMethod();
  +            }
  +        }
  +        if (theOne != null) {
  +            try {
  +                add.invoke(container, new Object[]{theOne});
  +            } catch (InvocationTargetException ite) {
  +                if (ite.getTargetException() instanceof BuildException) {
  +                    throw (BuildException) ite.getTargetException();
  +                }
  +                throw new BuildException(ite.getTargetException());
  +            } catch (Exception e) {
  +                throw new BuildException(e);
  +            }
  +        }
  +        return theOne;
  +    }
  +
  +
  +    /**
  +     *  create a new task instance
  +     *
  +     *@param  taskType         name of the task
  +     *@return                  null if the task name is unknown
  +     *@throws  BuildException  when task creation goes bad
  +     */
  +    public Task createTask(String taskType) throws BuildException {
  +        return (Task) createForRole(TASK_ROLE, taskType);
  +    }
  +
  +
       /**
  -     * Keep a record of all tasks that have been created so that they
  -     * can be invalidated if a taskdef overrides the definition.
  +     *  Keep a record of all tasks that have been created so that they can be
  +     *  invalidated if a taskdef overrides the definition.
  +     *
  +     *@param  type  The feature to be added to the CreatedTask attribute
  +     *@param  task  The feature to be added to the CreatedTask attribute
        */
       private void addCreatedTask(String type, Task task) {
           synchronized (createdTasks) {
  @@ -837,9 +1168,12 @@
           }
       }
   
  +
       /**
  -     * Mark tasks as invalid which no longer are of the correct type
  -     * for a given taskname.
  +     *  Mark tasks as invalid which no longer are of the correct type for a
  +     *  given taskname.
  +     *
  +     *@param  type  Description of the Parameter
        */
       private void invalidateCreatedTasks(String type) {
           synchronized (createdTasks) {
  @@ -856,88 +1190,63 @@
           }
       }
   
  +
       /**
  -     * create a new DataType instance
  -     * @param typeName name of the datatype
  -     * @throws BuildException when datatype creation goes bad
  -     * @return null if the datatype name is unknown
  +     *  create a new DataType instance
  +     *
  +     *@param  typeName         name of the datatype
  +     *@return                  null if the datatype name is unknown
  +     *@throws  BuildException  when datatype creation goes bad
        */
       public Object createDataType(String typeName) throws BuildException {
  -        Class c = symbols.get("datatype", typeName);
  -
  -        if (c == null) {
  -            return null;
  -        }
  -
  -        try {
  -            java.lang.reflect.Constructor ctor = null;
  -            boolean noArg = false;
  -            // DataType can have a "no arg" constructor or take a single 
  -            // Project argument.
  -            try {
  -                ctor = c.getConstructor(new Class[0]);
  -                noArg = true;
  -            } catch (NoSuchMethodException nse) {
  -                ctor = c.getConstructor(new Class[] {Project.class});
  -                noArg = false;
  -            }
  -
  -            Object o = null;
  -            if (noArg) {
  -                 o = ctor.newInstance(new Object[0]);
  -            } else {
  -                 o = ctor.newInstance(new Object[] {this});
  -            }
  -            if (o instanceof ProjectComponent) {
  -                ((ProjectComponent)o).setProject(this);
  -            }
  -            String msg = "   +DataType: " + typeName;
  -            log (msg, MSG_DEBUG);
  -            return o;
  -        } catch (java.lang.reflect.InvocationTargetException ite) {
  -            Throwable t = ite.getTargetException();
  -            String msg = "Could not create datatype of type: "
  -                 + typeName + " due to " + t;
  -            throw new BuildException(msg, t);
  -        } catch (Throwable t) {
  -            String msg = "Could not create datatype of type: "
  -                 + typeName + " due to " + t;
  -            throw new BuildException(msg, t);
  -        }
  +        // This is to make the function backward compatible
  +        // Since we know if it returning an adapter for it
  +        DataTypeAdapterTask dt =
  +                (DataTypeAdapterTask) createForRole(DATATYPE_ROLE, typeName);
  +        return (dt != null ? dt.getProxy() : null);
       }
   
  +
       /**
  -     * execute the sequence of targets, and the targets they depend on
  -     * @param Vector a vector of target name strings
  -     * @throws BuildException if the build failed
  +     *  execute the sequence of targets, and the targets they depend on
  +     *
  +     *@param  targetNames      Description of the Parameter
  +     *@throws  BuildException  if the build failed
        */
       public void executeTargets(Vector targetNames) throws BuildException {
           Throwable error = null;
   
           for (int i = 0; i < targetNames.size(); i++) {
  -            executeTarget((String)targetNames.elementAt(i));
  +            executeTarget((String) targetNames.elementAt(i));
           }
       }
   
  +
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  line     Description of the Parameter
  +     *@param  isError  Description of the Parameter
  +     */
       public void demuxOutput(String line, boolean isError) {
  -        Task task = (Task)threadTasks.get(Thread.currentThread());
  +        Task task = (Task) threadTasks.get(Thread.currentThread());
           if (task == null) {
               fireMessageLogged(this, line, isError ? MSG_ERR : MSG_INFO);
  -        }
  -        else {
  +        } else {
               if (isError) {
                   task.handleErrorOutput(line);
  -            }
  -            else {
  +            } else {
                   task.handleOutput(line);
               }
           }
       }
  -    
  +
  +
       /**
  -     * execute the targets and any targets it depends on
  -     * @param targetName the target to execute
  -     * @throws BuildException if the build failed
  +     *  execute the targets and any targets it depends on
  +     *
  +     *@param  targetName       the target to execute
  +     *@throws  BuildException  if the build failed
        */
       public void executeTarget(String targetName) throws BuildException {
   
  @@ -965,37 +1274,47 @@
           } while (!curtarget.getName().equals(targetName));
       }
   
  +
       /**
  -     * Return the canonical form of fileName as an absolute path.
  +     *  Return the canonical form of fileName as an absolute path. <p>
        *
  -     * <p>If fileName is a relative file name, resolve it relative to
  -     * rootDir.</p>
  +     *  If fileName is a relative file name, resolve it relative to rootDir.</p>
        *
  -     * @deprecated
  +     *@param  fileName  Description of the Parameter
  +     *@param  rootDir   Description of the Parameter
  +     *@return           Description of the Return Value
  +     *@deprecated
        */
       public File resolveFile(String fileName, File rootDir) {
           return fileUtils.resolveFile(rootDir, fileName);
       }
   
  +
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  fileName  Description of the Parameter
  +     *@return           Description of the Return Value
  +     */
       public File resolveFile(String fileName) {
           return fileUtils.resolveFile(baseDir, fileName);
       }
   
  +
       /**
  -     * Translate a path into its native (platform specific) format. 
  -     * <p>
  -     * This method uses the PathTokenizer class to separate the input path
  -     * into its components. This handles DOS style paths in a relatively
  -     * sensible way. The file separators are then converted to their platform
  -     * specific versions.
  -     *
  -     * @param to_process the path to be converted   
  +     *  Translate a path into its native (platform specific) format. <p>
        *
  -     * @return the native version of to_process or 
  -     *         an empty string if to_process is null or empty
  +     *  This method uses the PathTokenizer class to separate the input path into
  +     *  its components. This handles DOS style paths in a relatively sensible
  +     *  way. The file separators are then converted to their platform specific
  +     *  versions.
  +     *
  +     *@param  to_process  the path to be converted
  +     *@return             the native version of to_process or an empty string if
  +     *      to_process is null or empty
        */
       public static String translatePath(String to_process) {
  -        if ( to_process == null || to_process.length() == 0 ) {
  +        if (to_process == null || to_process.length() == 0) {
               return "";
           }
   
  @@ -1010,142 +1329,178 @@
               }
               path.append(pathComponent);
           }
  -        
  +
           return path.toString();
       }
   
  +
       /**
  -     * Convienence method to copy a file from a source to a destination.
  -     * No filtering is performed.
  -     *
  -     * @throws IOException
  +     *  Convienence method to copy a file from a source to a destination. No
  +     *  filtering is performed.
        *
  -     * @deprecated
  +     *@param  sourceFile    Description of the Parameter
  +     *@param  destFile      Description of the Parameter
  +     *@throws  IOException
  +     *@deprecated
        */
       public void copyFile(String sourceFile, String destFile) throws IOException {
           fileUtils.copyFile(sourceFile, destFile);
       }
   
  +
       /**
  -     * Convienence method to copy a file from a source to a destination
  -     * specifying if token filtering must be used.
  -     *
  -     * @throws IOException
  +     *  Convienence method to copy a file from a source to a destination
  +     *  specifying if token filtering must be used.
        *
  -     * @deprecated
  +     *@param  sourceFile    Description of the Parameter
  +     *@param  destFile      Description of the Parameter
  +     *@param  filtering     Description of the Parameter
  +     *@throws  IOException
  +     *@deprecated
        */
       public void copyFile(String sourceFile, String destFile, boolean filtering)
  -        throws IOException {
  +             throws IOException {
           fileUtils.copyFile(sourceFile, destFile, filtering ? globalFilters : null);
       }
   
  +
       /**
  -     * Convienence method to copy a file from a source to a
  -     * destination specifying if token filtering must be used and if
  -     * source files may overwrite newer destination files.
  -     *
  -     * @throws IOException 
  -     *
  -     * @deprecated
  +     *  Convienence method to copy a file from a source to a destination
  +     *  specifying if token filtering must be used and if source files may
  +     *  overwrite newer destination files.
  +     *
  +     *@param  sourceFile    Description of the Parameter
  +     *@param  destFile      Description of the Parameter
  +     *@param  filtering     Description of the Parameter
  +     *@param  overwrite     Description of the Parameter
  +     *@throws  IOException
  +     *@deprecated
        */
       public void copyFile(String sourceFile, String destFile, boolean filtering,
  -                         boolean overwrite) throws IOException {
  +            boolean overwrite) throws IOException {
           fileUtils.copyFile(sourceFile, destFile, filtering ? globalFilters : null, overwrite);
       }
   
  -     /**
  -     * Convienence method to copy a file from a source to a
  -     * destination specifying if token filtering must be used, if
  -     * source files may overwrite newer destination files and the
  -     * last modified time of <code>destFile</code> file should be made equal
  -     * to the last modified time of <code>sourceFile</code>.
  -     *
  -     * @throws IOException 
  -     *
  -     * @deprecated
  +
  +    /**
  +     *  Convienence method to copy a file from a source to a destination
  +     *  specifying if token filtering must be used, if source files may
  +     *  overwrite newer destination files and the last modified time of <code>destFile</code>
  +     *  file should be made equal to the last modified time of <code>sourceFile</code>
  +     *  .
  +     *
  +     *@param  sourceFile            Description of the Parameter
  +     *@param  destFile              Description of the Parameter
  +     *@param  filtering             Description of the Parameter
  +     *@param  overwrite             Description of the Parameter
  +     *@param  preserveLastModified  Description of the Parameter
  +     *@throws  IOException
  +     *@deprecated
        */
       public void copyFile(String sourceFile, String destFile, boolean filtering,
  -                         boolean overwrite, boolean preserveLastModified)
  -        throws IOException {
  -        fileUtils.copyFile(sourceFile, destFile, filtering ? globalFilters : null, 
  -                           overwrite, preserveLastModified);
  +            boolean overwrite, boolean preserveLastModified)
  +             throws IOException {
  +        fileUtils.copyFile(sourceFile, destFile, filtering ? globalFilters : null,
  +                overwrite, preserveLastModified);
       }
   
  +
       /**
  -     * Convienence method to copy a file from a source to a destination.
  -     * No filtering is performed.
  +     *  Convienence method to copy a file from a source to a destination. No
  +     *  filtering is performed.
        *
  -     * @throws IOException
  -     *
  -     * @deprecated
  +     *@param  sourceFile    Description of the Parameter
  +     *@param  destFile      Description of the Parameter
  +     *@throws  IOException
  +     *@deprecated
        */
       public void copyFile(File sourceFile, File destFile) throws IOException {
           fileUtils.copyFile(sourceFile, destFile);
       }
   
  +
       /**
  -     * Convienence method to copy a file from a source to a destination
  -     * specifying if token filtering must be used.
  -     *
  -     * @throws IOException
  +     *  Convienence method to copy a file from a source to a destination
  +     *  specifying if token filtering must be used.
        *
  -     * @deprecated
  +     *@param  sourceFile    Description of the Parameter
  +     *@param  destFile      Description of the Parameter
  +     *@param  filtering     Description of the Parameter
  +     *@throws  IOException
  +     *@deprecated
        */
       public void copyFile(File sourceFile, File destFile, boolean filtering)
  -        throws IOException {
  +             throws IOException {
           fileUtils.copyFile(sourceFile, destFile, filtering ? globalFilters : null);
       }
   
  +
       /**
  -     * Convienence method to copy a file from a source to a
  -     * destination specifying if token filtering must be used and if
  -     * source files may overwrite newer destination files.
  -     *
  -     * @throws IOException 
  -     *
  -     * @deprecated
  +     *  Convienence method to copy a file from a source to a destination
  +     *  specifying if token filtering must be used and if source files may
  +     *  overwrite newer destination files.
  +     *
  +     *@param  sourceFile    Description of the Parameter
  +     *@param  destFile      Description of the Parameter
  +     *@param  filtering     Description of the Parameter
  +     *@param  overwrite     Description of the Parameter
  +     *@throws  IOException
  +     *@deprecated
        */
       public void copyFile(File sourceFile, File destFile, boolean filtering,
  -                         boolean overwrite) throws IOException {
  +            boolean overwrite) throws IOException {
           fileUtils.copyFile(sourceFile, destFile, filtering ? globalFilters : null, overwrite);
       }
   
  +
       /**
  -     * Convienence method to copy a file from a source to a
  -     * destination specifying if token filtering must be used, if
  -     * source files may overwrite newer destination files and the
  -     * last modified time of <code>destFile</code> file should be made equal
  -     * to the last modified time of <code>sourceFile</code>.
  -     *
  -     * @throws IOException 
  -     *
  -     * @deprecated
  +     *  Convienence method to copy a file from a source to a destination
  +     *  specifying if token filtering must be used, if source files may
  +     *  overwrite newer destination files and the last modified time of <code>destFile</code>
  +     *  file should be made equal to the last modified time of <code>sourceFile</code>
  +     *  .
  +     *
  +     *@param  sourceFile            Description of the Parameter
  +     *@param  destFile              Description of the Parameter
  +     *@param  filtering             Description of the Parameter
  +     *@param  overwrite             Description of the Parameter
  +     *@param  preserveLastModified  Description of the Parameter
  +     *@throws  IOException
  +     *@deprecated
        */
       public void copyFile(File sourceFile, File destFile, boolean filtering,
  -                         boolean overwrite, boolean preserveLastModified)
  -        throws IOException {
  -        fileUtils.copyFile(sourceFile, destFile, filtering ? globalFilters : null, 
  -                           overwrite, preserveLastModified);
  +            boolean overwrite, boolean preserveLastModified)
  +             throws IOException {
  +        fileUtils.copyFile(sourceFile, destFile, filtering ? globalFilters : null,
  +                overwrite, preserveLastModified);
       }
   
  +
       /**
  -     * Calls File.setLastModified(long time) in a Java 1.1 compatible way.
  +     *  Calls File.setLastModified(long time) in a Java 1.1 compatible way.
        *
  -     * @deprecated
  +     *@param  file                The new fileLastModified value
  +     *@param  time                The new fileLastModified value
  +     *@exception  BuildException  Description of the Exception
  +     *@deprecated
        */
       public void setFileLastModified(File file, long time) throws BuildException {
           if (getJavaVersion() == JAVA_1_1) {
               log("Cannot change the modification time of " + file
  -                + " in JDK 1.1", Project.MSG_WARN);
  +                     + " in JDK 1.1", Project.MSG_WARN);
               return;
           }
           fileUtils.setFileLastModified(file, time);
           log("Setting modification time for " + file, MSG_VERBOSE);
       }
   
  +
       /**
  -     * returns the boolean equivalent of a string, which is considered true
  -     * if either "on", "true", or "yes" is found, ignoring case.
  +     *  returns the boolean equivalent of a string, which is considered true if
  +     *  either "on", "true", or "yes" is found, ignoring case.
  +     *
  +     *@param  s  Description of the Parameter
  +     *@return    Description of the Return Value
        */
       public static boolean toBoolean(String s) {
           return (s.equalsIgnoreCase("on") ||
  @@ -1153,19 +1508,22 @@
                   s.equalsIgnoreCase("yes"));
       }
   
  +
       /**
  -     * Topologically sort a set of Targets.
  -     * @param root is the (String) name of the root Target. The sort is
  -     * created in such a way that the sequence of Targets uptil the root
  -     * target is the minimum possible such sequence.
  -     * @param targets is a Hashtable representing a "name to Target" mapping
  -     * @return a Vector of Strings with the names of the targets in
  -     * sorted order.
  -     * @exception BuildException if there is a cyclic dependency among the
  -     * Targets, or if a Target does not exist.
  +     *  Topologically sort a set of Targets.
  +     *
  +     *@param  root                is the (String) name of the root Target. The
  +     *      sort is created in such a way that the sequence of Targets uptil the
  +     *      root target is the minimum possible such sequence.
  +     *@param  targets             is a Hashtable representing a "name to Target"
  +     *      mapping
  +     *@return                     a Vector of Strings with the names of the
  +     *      targets in sorted order.
  +     *@exception  BuildException  if there is a cyclic dependency among the
  +     *      Targets, or if a Target does not exist.
        */
       public final Vector topoSort(String root, Hashtable targets)
  -        throws BuildException {
  +             throws BuildException {
           Vector ret = new Vector();
           Hashtable state = new Hashtable();
           Stack visiting = new Stack();
  @@ -1179,21 +1537,21 @@
           // build Target.
   
           tsort(root, targets, state, visiting, ret);
  -        log("Build sequence for target `"+root+"' is "+ret, MSG_VERBOSE);
  -        for (Enumeration en=targets.keys(); en.hasMoreElements();) {
  -            String curTarget = (String)(en.nextElement());
  +        log("Build sequence for target `" + root + "' is " + ret, MSG_VERBOSE);
  +        for (Enumeration en = targets.keys(); en.hasMoreElements(); ) {
  +            String curTarget = (String) (en.nextElement());
               String st = (String) state.get(curTarget);
               if (st == null) {
                   tsort(curTarget, targets, state, visiting, ret);
  -            }
  -            else if (st == VISITING) {
  -                throw new RuntimeException("Unexpected node in visiting state: "+curTarget);
  +            } else if (st == VISITING) {
  +                throw new RuntimeException("Unexpected node in visiting state: " + curTarget);
               }
           }
  -        log("Complete build sequence is "+ret, MSG_VERBOSE);
  +        log("Complete build sequence is " + ret, MSG_VERBOSE);
           return ret;
       }
   
  +
       // one step in a recursive DFS traversal of the Target dependency tree.
       // - The Hashtable "state" contains the state (VISITED or VISITING or null)
       // of all the target names.
  @@ -1211,14 +1569,24 @@
       //   "ret" now contains the sorted sequence of Targets upto the current
       //   Target.
   
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  root                Description of the Parameter
  +     *@param  targets             Description of the Parameter
  +     *@param  state               Description of the Parameter
  +     *@param  visiting            Description of the Parameter
  +     *@param  ret                 Description of the Parameter
  +     *@exception  BuildException  Description of the Exception
  +     */
       private final void tsort(String root, Hashtable targets,
  -                             Hashtable state, Stack visiting,
  -                             Vector ret)
  -        throws BuildException {
  +            Hashtable state, Stack visiting,
  +            Vector ret)
  +             throws BuildException {
           state.put(root, VISITING);
           visiting.push(root);
   
  -        Target target = (Target)(targets.get(root));
  +        Target target = (Target) (targets.get(root));
   
           // Make sure we exist
           if (target == null) {
  @@ -1227,7 +1595,7 @@
               sb.append("' does not exist in this project. ");
               visiting.pop();
               if (!visiting.empty()) {
  -                String parent = (String)visiting.peek();
  +                String parent = (String) visiting.peek();
                   sb.append("It is used from target `");
                   sb.append(parent);
                   sb.append("'.");
  @@ -1236,14 +1604,13 @@
               throw new BuildException(new String(sb));
           }
   
  -        for (Enumeration en=target.getDependencies(); en.hasMoreElements();) {
  +        for (Enumeration en = target.getDependencies(); en.hasMoreElements(); ) {
               String cur = (String) en.nextElement();
  -            String m=(String)state.get(cur);
  +            String m = (String) state.get(cur);
               if (m == null) {
                   // Not been visited
                   tsort(cur, targets, state, visiting, ret);
  -            }
  -            else if (m == VISITING) {
  +            } else if (m == VISITING) {
                   // Currently visiting this node, so have a cycle
                   throw makeCircularException(cur, visiting);
               }
  @@ -1251,46 +1618,73 @@
   
           String p = (String) visiting.pop();
           if (root != p) {
  -            throw new RuntimeException("Unexpected internal error: expected to pop "+root+" but got "+p);
  +            throw new RuntimeException("Unexpected internal error: expected to pop " + root + " but got " + p);
           }
           state.put(root, VISITED);
           ret.addElement(target);
       }
   
  +
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  end  Description of the Parameter
  +     *@param  stk  Description of the Parameter
  +     *@return      Description of the Return Value
  +     */
       private static BuildException makeCircularException(String end, Stack stk) {
           StringBuffer sb = new StringBuffer("Circular dependency: ");
           sb.append(end);
           String c;
           do {
  -            c = (String)stk.pop();
  +            c = (String) stk.pop();
               sb.append(" <- ");
               sb.append(c);
  -        } while(!c.equals(end));
  +        } while (!c.equals(end));
           return new BuildException(new String(sb));
       }
   
  +
  +    /**
  +     *  Adds a feature to the Reference attribute of the Project object
  +     *
  +     *@param  name   The feature to be added to the Reference attribute
  +     *@param  value  The feature to be added to the Reference attribute
  +     */
       public void addReference(String name, Object value) {
  -        if (null != references.get(name)) {
  -            log("Overriding previous definition of reference to " + name, 
  -                MSG_WARN);
  +        Object o = references.get(name);
  +        if (null != o && o != value
  +                 && (!(o instanceof RoleAdapter)
  +                 || ((RoleAdapter) o).getProxy() != value)) {
  +            log("Overriding previous definition of reference to " + name,
  +                    MSG_WARN);
           }
           log("Adding reference: " + name + " -> " + value, MSG_DEBUG);
  -        references.put(name,value);
  +        references.put(name, value);
       }
   
  +
  +    /**
  +     *  Gets the references attribute of the Project object
  +     *
  +     *@return    The references value
  +     */
       public Hashtable getReferences() {
           return references;
       }
   
  +
       /**
  -     * @return The object with the "id" key.
  +     *@param  key  Description of the Parameter
  +     *@return      The object with the "id" key.
        */
       public Object getReference(String key) {
           return references.get(key);
       }
   
  +
       /**
  -     * send build started event to the listeners
  +     *  send build started event to the listeners
        */
       protected void fireBuildStarted() {
           BuildEvent event = new BuildEvent(this);
  @@ -1300,9 +1694,11 @@
           }
       }
   
  +
       /**
  -     * send build finished event to the listeners
  -     * @param exception exception which indicates failure if not null
  +     *  send build finished event to the listeners
  +     *
  +     *@param  exception  exception which indicates failure if not null
        */
       protected void fireBuildFinished(Throwable exception) {
           BuildEvent event = new BuildEvent(this);
  @@ -1313,9 +1709,11 @@
           }
       }
   
  -    
  +
       /**
  -     * send target started event to the listeners
  +     *  send target started event to the listeners
  +     *
  +     *@param  target  Description of the Parameter
        */
       protected void fireTargetStarted(Target target) {
           BuildEvent event = new BuildEvent(target);
  @@ -1325,9 +1723,12 @@
           }
       }
   
  +
       /**
  -     * send build finished event to the listeners
  -     * @param exception exception which indicates failure if not null
  +     *  send build finished event to the listeners
  +     *
  +     *@param  exception  exception which indicates failure if not null
  +     *@param  target     Description of the Parameter
        */
       protected void fireTargetFinished(Target target, Throwable exception) {
           BuildEvent event = new BuildEvent(target);
  @@ -1338,6 +1739,12 @@
           }
       }
   
  +
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  task  Description of the Parameter
  +     */
       protected void fireTaskStarted(Task task) {
           // register this as the current task on the current thread.
           threadTasks.put(Thread.currentThread(), task);
  @@ -1348,6 +1755,13 @@
           }
       }
   
  +
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  task       Description of the Parameter
  +     *@param  exception  Description of the Parameter
  +     */
       protected void fireTaskFinished(Task task, Throwable exception) {
           threadTasks.remove(Thread.currentThread());
           System.out.flush();
  @@ -1360,6 +1774,14 @@
           }
       }
   
  +
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  event     Description of the Parameter
  +     *@param  message   Description of the Parameter
  +     *@param  priority  Description of the Parameter
  +     */
       private void fireMessageLoggedEvent(BuildEvent event, String message, int priority) {
           event.setMessage(message, priority);
           for (int i = 0; i < listeners.size(); i++) {
  @@ -1368,18 +1790,43 @@
           }
       }
   
  +
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  project   Description of the Parameter
  +     *@param  message   Description of the Parameter
  +     *@param  priority  Description of the Parameter
  +     */
       protected void fireMessageLogged(Project project, String message, int priority) {
           BuildEvent event = new BuildEvent(project);
           fireMessageLoggedEvent(event, message, priority);
       }
   
  +
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  target    Description of the Parameter
  +     *@param  message   Description of the Parameter
  +     *@param  priority  Description of the Parameter
  +     */
       protected void fireMessageLogged(Target target, String message, int priority) {
           BuildEvent event = new BuildEvent(target);
           fireMessageLoggedEvent(event, message, priority);
       }
   
  +
  +    /**
  +     *  Description of the Method
  +     *
  +     *@param  task      Description of the Parameter
  +     *@param  message   Description of the Parameter
  +     *@param  priority  Description of the Parameter
  +     */
       protected void fireMessageLogged(Task task, String message, int priority) {
           BuildEvent event = new BuildEvent(task);
           fireMessageLoggedEvent(event, message, priority);
       }
  +
   }
  
  
  
  1.2       +833 -797  jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/ProjectHelper.java
  
  Index: ProjectHelper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/ProjectHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ProjectHelper.java	18 Feb 2002 08:55:16 -0000	1.1
  +++ ProjectHelper.java	3 Mar 2002 12:37:13 -0000	1.2
  @@ -1,797 +1,833 @@
  -/*
  - * 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 apache@apache.org.
  - *
  - * 5. Products derived from this software may not be called "Apache"
  - *    nor may "Apache" appear in their names without prior written
  - *    permission of the Apache Group.
  - *
  - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  - * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  - * SUCH DAMAGE.
  - * ====================================================================
  - *
  - * This software consists of voluntary contributions made by many
  - * individuals on behalf of the Apache Software Foundation.  For more
  - * information on the Apache Software Foundation, please see
  - * <http://www.apache.org/>.
  - */
  -
  -package org.apache.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 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.
  - *
  - * @author duncan@x180.com
  - */
  -
  -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;
  -
  -    /**
  -     * Configures the Project with the contents of the specified XML file.
  -     */
  -    public static void configureProject(Project project, File buildFile) throws BuildException {
  -        new ProjectHelper(project, buildFile).parse();
  -    }
  -
  -    /**
  -     * Constructs a new Ant parser for the specified XML file.
  -     */
  -    private ProjectHelper(Project project, File buildFile) {
  -        this.project = project;
  -        this.buildFile = new File(buildFile.getAbsolutePath());
  -        buildFileParent = new File(this.buildFile.getParent());
  -    }
  -
  -    /**
  -     * Parses the project file.
  -     */
  -    private void parse() throws BuildException {
  -        FileInputStream inputStream = null;
  -        InputSource inputSource = null;
  -        
  -        try {
  -            SAXParser saxParser = getParserFactory().newSAXParser();
  -            parser = saxParser.getParser();
  -
  -            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);
  -            saxParser.parse(inputSource, new RootHandler());
  -        }
  -        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) {
  -            ProjectHelper.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")) {
  -                handleTopTask(name, attrs);
  -            } else if (name.equals("typedef")) {
  -                handleTopTask(name, attrs);
  -            } else if (name.equals("antlib")) {
  -                handleTopTask(name, attrs);
  -            } else if (name.equals("property")) {
  -                handleTopTask(name, attrs);
  -            } else if (name.equals("target")) {
  -                handleTarget(name, attrs);
  -            } else if (project.isDefinedOnRole(Project.DATATYPE_ROLE, name)) {
  -                handleTopTask(name, attrs);
  -            } else {
  -                throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
  -            }
  -        }
  -
  -        private void handleTopTask(String name, AttributeList attrs) 
  -	    throws SAXParseException {
  -	    InmediateTarget target = new InmediateTarget(name);
  -            (new TaskHandler(this, target, null, target)).init(name, attrs);
  -        }
  -
  -        private void handleTarget(String tag, AttributeList attrs) throws SAXParseException {
  -            new TargetHandler(this).init(tag, 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 {
  -	    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 = (Task)project.createInRole(container, 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);
  -		container.addTask(task);
  -            }
  -
  -            task.setLocation(new Location(buildFile.toString(), 
  -					  locator.getLineNumber(), 
  -					  locator.getColumnNumber()));
  -            configureId(task, attrs);
  -
  -	    task.setOwningTarget(target);
  -	    task.init();
  -	    wrapper = task.getRuntimeConfigurableWrapper();
  -	    wrapper.setAttributes(attrs);
  -	    if (parentWrapper != null) {
  -		parentWrapper.addChild(wrapper);
  -	    }
  -        }
  -
  -        protected void finished() {
  -            if (container instanceof InmediateTarget) {
  -                ((InmediateTarget)container).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 RoleAdapter) {
  -                this.parent = ((RoleAdapter) 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);
  -		    // Set this parameters just in case is a Task
  -		    uc.setTaskType(elementName);
  -		    uc.setTaskName(elementName);
  -                    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);
  -            }
  -        }
  -    }
  -
  -    /**
  -     * Special target type for top level Tasks and Datatypes.
  -     * This will allow eliminating special cases.
  -     */
  -    private class InmediateTarget extends Target {
  -	/**
  -	 * Create a target for a top level task or datatype.
  -	 * @param name the name of the task to be run on this target.
  -	 */
  -	InmediateTarget(String name) {
  -	    super();
  -	    setProject(project);
  -	    setName("Top level " + name);
  -	}
  -    }
  -
  -    public static void configure(Object target, AttributeList attrs, 
  -                                 Project project) throws BuildException {
  -        if( target instanceof RoleAdapter ) {
  -            target=((RoleAdapter)target).getProxy();
  -        }
  -
  -        IntrospectionHelper ih = 
  -            IntrospectionHelper.getHelper(target.getClass());
  -
  -        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 RoleAdapter) {
  -            target = ((RoleAdapter) 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));
  -        }
  -    }
  -
  -    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 {@link #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) {
  -	    if( target instanceof RoleAdapter ) {
  -		((RoleAdapter)target).setId(id);
  -	    }
  -            project.addReference(id, target);
  -        }
  -    }
  -
  -}
  +/*
  + * 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 apache@apache.org.
  + *
  + * 5. Products derived from this software may not be called "Apache"
  + *    nor may "Apache" appear in their names without prior written
  + *    permission of the Apache Group.
  + *
  + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  + * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  + * SUCH DAMAGE.
  + * ====================================================================
  + *
  + * This software consists of voluntary contributions made by many
  + * individuals on behalf of the Apache Software Foundation.  For more
  + * information on the Apache Software Foundation, please see
  + * <http://www.apache.org/>.
  + */
  +
  +package org.apache.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 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.
  + *
  + * @author duncan@x180.com
  + */
  +
  +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;
  +
  +    /**
  +     * Configures the Project with the contents of the specified XML file.
  +     */
  +    public static void configureProject(Project project, File buildFile) throws BuildException {
  +        new ProjectHelper(project, buildFile).parse();
  +    }
  +
  +    /**
  +     * Constructs a new Ant parser for the specified XML file.
  +     */
  +    private ProjectHelper(Project project, File buildFile) {
  +        this.project = project;
  +        this.buildFile = new File(buildFile.getAbsolutePath());
  +        buildFileParent = new File(this.buildFile.getParent());
  +    }
  +
  +    /**
  +     * Parses the project file.
  +     */
  +    private void parse() throws BuildException {
  +        FileInputStream inputStream = null;
  +        InputSource inputSource = null;
  +
  +        try {
  +            SAXParser saxParser = getParserFactory().newSAXParser();
  +            parser = saxParser.getParser();
  +
  +            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);
  +            saxParser.parse(inputSource, new RootHandler());
  +        }
  +        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) {
  +            ProjectHelper.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")) {
  +                handleTopTask(name, attrs);
  +            } else if (name.equals("typedef")) {
  +                handleTopTask(name, attrs);
  +            } else if (name.equals("antlib")) {
  +                handleTopTask(name, attrs);
  +            } else if (name.equals("property")) {
  +                handleTopTask(name, attrs);
  +            } else if (name.equals("target")) {
  +                handleTarget(name, attrs);
  +            } else if (name.equals("description")) {
  +                handleDescription(name, attrs);
  +            } else if (project.isDefinedOnRole(Project.DATATYPE_ROLE, name)) {
  +                handleTopTask(name, attrs);
  +            } else {
  +                throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
  +            }
  +        }
  +
  +        private void handleTopTask(String name, AttributeList attrs)
  +            throws SAXParseException {
  +            InmediateTarget target = new InmediateTarget(name);
  +            (new TaskHandler(this, target, null, target)).init(name, attrs);
  +        }
  +
  +        private void handleTarget(String tag, AttributeList attrs) throws SAXParseException {
  +            new TargetHandler(this).init(tag, attrs);
  +        }
  +
  +        private void handleDescription(String tag, AttributeList attrs) throws SAXParseException {
  +            new DescriptionHandler(this).init(tag, 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 (name.equals("description")) {
  +                new DescriptionHandler(this).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 = (Task)project.createInRole(container, 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);
  +                container.addTask(task);
  +            }
  +
  +            task.setLocation(new Location(buildFile.toString(),
  +                                          locator.getLineNumber(),
  +                                          locator.getColumnNumber()));
  +            configureId(task, attrs);
  +
  +            task.setOwningTarget(target);
  +            task.init();
  +            wrapper = task.getRuntimeConfigurableWrapper();
  +            wrapper.setAttributes(attrs);
  +            if (parentWrapper != null) {
  +                parentWrapper.addChild(wrapper);
  +            }
  +        }
  +
  +        protected void finished() {
  +            if (container instanceof InmediateTarget) {
  +                ((InmediateTarget)container).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 RoleAdapter) {
  +                this.parent = ((RoleAdapter) 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);
  +                    // Set this parameters just in case is a Task
  +                    uc.setTaskType(elementName);
  +                    uc.setTaskName(elementName);
  +                    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 to perform appropriate semantics for the special
  +     * <description> element on tasks.
  +     */
  +    private class DescriptionHandler extends AbstractHandler {
  +
  +        public DescriptionHandler(DocumentHandler parent) {
  +            super(parent);
  +        }
  +
  +        public void init(String tag, AttributeList attrs) throws SAXParseException {
  +            if (attrs.getLength() > 0) {
  +                throw new SAXParseException("No attributes allowed on " + tag, locator);
  +            }
  +        }
  +
  +        public void characters(char[] buf, int start, int end) throws SAXParseException {
  +            String desc = project.getDescription();
  +            if (desc == null) {
  +                desc = "";
  +            }
  +            project.setDescription(desc + new String(buf, start, end));
  +        }
  +    }
  +
  +    /**
  +     * Special target type for top level Tasks and Datatypes.
  +     * This will allow eliminating special cases.
  +     */
  +    private class InmediateTarget extends Target {
  +        /**
  +         * Create a target for a top level task or datatype.
  +         * @param name the name of the task to be run on this target.
  +         */
  +        InmediateTarget(String name) {
  +            super();
  +            setProject(project);
  +            setName("Top level " + name);
  +        }
  +    }
  +
  +    public static void configure(Object target, AttributeList attrs,
  +                                 Project project) throws BuildException {
  +        if( target instanceof RoleAdapter ) {
  +            target=((RoleAdapter)target).getProxy();
  +        }
  +
  +        IntrospectionHelper ih =
  +            IntrospectionHelper.getHelper(target.getClass());
  +
  +        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 RoleAdapter) {
  +            target = ((RoleAdapter) 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));
  +        }
  +    }
  +
  +    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 {@link #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) {
  +            if( target instanceof RoleAdapter ) {
  +                ((RoleAdapter)target).setId(id);
  +            }
  +            project.addReference(id, target);
  +        }
  +    }
  +
  +}
  
  
  
  1.4       +5 -0      jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java
  
  Index: RoleAdapter.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- RoleAdapter.java	2 Mar 2002 22:20:43 -0000	1.3
  +++ RoleAdapter.java	3 Mar 2002 12:37:13 -0000	1.4
  @@ -56,6 +56,11 @@
   public interface RoleAdapter {
   
       /**
  +     * Obtain the id in case it is needed.
  +     */
  +    public void setId(String id);
  +
  +    /**
        * Set the object being adapted.
        * @param o the object being adapted
        */
  
  
  
  1.4       +357 -287  jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java
  
  Index: SymbolTable.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- SymbolTable.java	2 Mar 2002 22:20:43 -0000	1.3
  +++ SymbolTable.java	3 Mar 2002 12:37:13 -0000	1.4
  @@ -54,6 +54,7 @@
   package org.apache.tools.ant;
   
   import java.lang.reflect.InvocationTargetException;
  +import java.lang.reflect.Constructor;
   import java.lang.reflect.Method;
   import java.lang.reflect.Modifier;
   import java.util.*;
  @@ -64,7 +65,7 @@
   
       /** Parent symbol table */
       private SymbolTable parentTable;
  -    
  +
       /** Project associated with this symbol table */
       private Project project;
   
  @@ -82,8 +83,8 @@
       /**
        * Parameters for checking adapters.
        */
  -    private static final Class[] CHECK_ADAPTER_PARAMS = 
  -	new Class[]{Class.class, Project.class};
  +    private static final Class[] CHECK_ADAPTER_PARAMS =
  +        new Class[]{Class.class, Project.class};
   
       /**
        * Create a top level Symbol table.
  @@ -96,8 +97,8 @@
        * from that defined in the calling Project.
        * @param p the calling project
        */
  -    public SymbolTable(Project p) {
  -	parentTable = p.getSymbols();
  +    public SymbolTable(SymbolTable st) {
  +        parentTable = st;
       }
   
       /**
  @@ -105,7 +106,55 @@
        * @param p the project for this symbol table
        */
       public void setProject(Project p) {
  -	this.project = p;
  +        this.project = p;
  +    }
  +
  +    /**
  +     * Get the specified loader for the project.
  +     * @param name the name of the loader
  +     * @return the corresponding ANT classloader
  +     */
  +    private AntClassLoader getLoader(String name) {
  +        AntClassLoader cl = (AntClassLoader) loaders.get(name);
  +        if (cl == null && parentTable != null) {
  +            return parentTable.getLoader(name);
  +        }
  +        return cl;
  +    }
  +
  +    /**
  +     * Add the specified class-path to a loader.
  +     * If the loader is defined in an ancestor project then a new
  +     * classloader inheritin from the one already existing
  +     * will be created, otherwise the path willbe added to the existing
  +     * ClassLoader.
  +     * @param name the name of the loader to use.
  +     * @param clspath the path to be added to the classloader
  +     */
  +    public ClassLoader addToLoader(String name, Path clspath) {
  +        // Find if the loader is already defined in the current project
  +        AntClassLoader cl = (AntClassLoader) loaders.get(name);
  +        if (cl == null) {
  +            // Is it inherited from the calling project
  +            if (parentTable != null) {
  +                cl = parentTable.getLoader(name);
  +            }
  +            cl = new AntClassLoader(cl, project, clspath, true);
  +            loaders.put(name, cl);
  +        }
  +        else {
  +            // Add additional path to the existing definition
  +            String[] pathElements = clspath.list();
  +            for (int i = 0; i < pathElements.length; ++i) {
  +                try {
  +                    cl.addPathElement(pathElements[i]);
  +                }
  +                catch (BuildException e) {
  +                    // ignore path elements invalid relative to the project
  +                }
  +            }
  +        }
  +        return cl;
       }
   
       /**
  @@ -115,9 +164,9 @@
        * @return an array of roles supported by the class
        */
       public String[] findRoles(final Class clz) {
  -	Vector list = new Vector();
  -	findRoles(clz, list);
  -	return (String[])list.toArray(new String[list.size()]);
  +        Vector list = new Vector();
  +        findRoles(clz, list);
  +        return (String[])list.toArray(new String[list.size()]);
       }
   
       /**
  @@ -126,27 +175,27 @@
        * @param list the roles collected up to this point
        */
       private void findRoles(final Class clz, Vector list) {
  -	for (Enumeration e = roles.keys(); e.hasMoreElements();) {
  -	    String role = (String) e.nextElement();
  +        for (Enumeration e = roles.keys(); e.hasMoreElements();) {
  +            String role = (String) e.nextElement();
   
  -	    if (((Role) roles.get(role)).isImplementedBy(clz)) {
  -		list.addElement(role);
  -	    }
  -	}
  -	if (parentTable != null) findRoles(clz, list);
  +            if (((Role) roles.get(role)).isImplementedBy(clz)) {
  +                list.addElement(role);
  +            }
  +        }
  +        if (parentTable != null) parentTable.findRoles(clz, list);
       }
  -    
  +
       /**
        * Get the Role definition
        * @param role the name of the role
  -     * @return the method used to support objects on this role
  +     * @return the Role description
        */
       public Role getRole(String role) {
  -	Role r = (Role) roles.get(role);
  -	if (r == null && parentTable != null) {
  -	    return parentTable.getRole(role);
  -	}
  -	return r;
  +        Role r = (Role) roles.get(role);
  +        if (r == null && parentTable != null) {
  +            return parentTable.getRole(role);
  +        }
  +        return r;
       }
   
       /**
  @@ -157,127 +206,20 @@
        * @return whether the role replaced a different definition
        */
       public boolean addRole(String role, Class rclz, Class aclz) {
  -	// Check if role already declared
  -	Role old = getRole(role);
  -	if (old != null && old.isSameAsFor(rclz, aclz)
  -	    ) {
  -	    project.log("Ignoring override for role " + role 
  -			+ ", it is already defined by the same definition.", 
  -			project.MSG_VERBOSE);
  -	    return false;
  -	}
  -	// Role interfaces should only contain one method
  -	roles.put(role, new Role(rclz, aclz));
  -	return (old != null);
  -    }
  -
  -    /** 
  -     * Verify if the interface is valid.
  -     * @param clz the interface to validate
  -     * @return the method defined by the interface
  -     */
  -    private Method validInterface(Class clz) {
  -	Method m[] = clz.getDeclaredMethods();
  -	if (m.length == 1
  -	    && java.lang.Void.TYPE.equals(m[0].getReturnType())) {
  -	    Class args[] = m[0].getParameterTypes();
  -	    if (args.length == 1
  -		&& !java.lang.String.class.equals(args[0])
  -		&& !args[0].isArray()
  -		&& !args[0].isPrimitive()) {
  -		return m[0];
  -	    }
  -	    else {
  -		throw new BuildException("Invalid role interface method in: "
  -					 + clz.getName());
  -	    }
  -	}
  -	else {
  -	    throw new BuildException("More than one method on role interface");
  -	}
  -    }
  -
  -    /** 
  -     * Verify if the adapter is valid with respect to the interface.
  -     * @param clz the class adapter to validate
  -     * @param mtd the method whose only argument must match
  -     * @return the static method to use for validating adaptees
  -     */
  -    private Method validAdapter(Class clz, Method mtd) {
  -	if (clz == null) return null;
  -	
  -	checkClass(clz);
  -	if (!mtd.getParameterTypes()[0].isAssignableFrom(clz)) {
  -	    String msg = "Adapter " + clz.getName() + 
  -		" is incompatible with role interface " + 
  -		mtd.getDeclaringClass().getName();
  -	    throw new BuildException(msg);
  -	}
  -	String msg = "Class " + clz.getName() + " is not an adapter: ";
  -	if (!RoleAdapter.class.isAssignableFrom(clz)) {
  -	    throw new BuildException(msg + "does not implement RoleAdapter");
  -	}
  -	try {
  -	    Method chk = clz.getMethod("checkClass", CHECK_ADAPTER_PARAMS);
  -	    if (!Modifier.isStatic(chk.getModifiers())) {
  -		throw new BuildException(msg + "checkClass() is not static");
  -	    }
  -	    return chk;
  -	}
  -	catch(NoSuchMethodException nme){
  -	    throw new BuildException(msg + "checkClass() not found", nme);
  -	}
  -    }
  -
  -    /**
  -     * Get the specified loader for the project.
  -     * @param name the name of the loader
  -     * @return the corresponding ANT classloader
  -     */
  -    private AntClassLoader getLoader(String name) {
  -	AntClassLoader cl = (AntClassLoader) loaders.get(name);
  -	if (cl == null && parentTable != null) {
  -	    return parentTable.getLoader(name);
  -	}
  -	return cl;
  +        // Check if role already declared
  +        Role old = getRole(role);
  +        if (old != null && old.isSameAsFor(rclz, aclz)) {
  +            project.log("Ignoring override for role " + role
  +                        + ", it is already defined by the same definition.",
  +                        project.MSG_VERBOSE);
  +            return false;
  +        }
  +        // Role interfaces should only contain one method
  +        roles.put(role, new Role(rclz, aclz));
  +        return (old != null);
       }
   
       /**
  -     * Add the specified class-path to a loader.
  -     * If the loader is defined in an ancestor project then a new
  -     * classloader inheritin from the one already existing
  -     * will be created, otherwise the path willbe added to the existing
  -     * ClassLoader.
  -     * @param name the name of the loader to use.
  -     * @param clspath the path to be added to the classloader
  -     */
  -    public ClassLoader addToLoader(String name, Path clspath) {
  -	// Find if the loader is already defined in the current project
  -	AntClassLoader cl = (AntClassLoader) loaders.get(name);
  -	if (cl == null) {
  -	    // Is it inherited from the calling project
  -	    if (parentTable != null) {
  -		cl = parentTable.getLoader(name);
  -	    }
  -	    cl = new AntClassLoader(cl, project, clspath, true);
  -	    loaders.put(name, cl);
  -	}
  -	else {
  -            // Add additional path to the existing definition
  -	    String[] pathElements = clspath.list();
  -            for (int i = 0; i < pathElements.length; ++i) {
  -                try {
  -                    cl.addPathElement(pathElements[i]);
  -                }
  -                catch (BuildException e) {
  -                    // ignore path elements invalid relative to the project
  -                }
  -            }
  -	}
  -	return cl;
  -    }
  -    
  -    /**
        * Add a new type of element to a role.
        * @param role the role for this Class.
        * @param name the name of the element for this Class
  @@ -285,46 +227,53 @@
        * @return the old definition
        */
       public Class add(String role, String name, Class clz) {
  -	// Find the role definition
  -	Role r = getRole(role);
  -	if (r == null) {
  -	    throw new BuildException("Unknown role: " + role);
  -	}
  -	// Check if it is already defined
  -	Class old = get(role, name);
  -	if (old != null) {
  -	    if (old.equals(clz)) {
  -		project.log("Ignoring override for "+ role + " " + name 
  -			    + ", it is already defined by the same class.", 
  -			    project.MSG_VERBOSE);
  -		return old;
  -	    }
  -	    else {
  +        // Find the role definition
  +        Role r = getRole(role);
  +        if (r == null) {
  +            throw new BuildException("Unknown role: " + role);
  +        }
  +        // Check if it is already defined
  +        Factory old = get(role, name);
  +        if (old != null) {
  +            if (old.getOriginalClass().equals(clz)) {
  +                project.log("Ignoring override for "+ role + " " + name
  +                            + ", it is already defined by the same class.",
  +                            project.MSG_VERBOSE);
  +                return old.getOriginalClass();
  +            }
  +            else {
                   project.log("Trying to override old definition of " +
  -			    role + " " + name, 
  -			    project.MSG_WARN);
  -	    }
  -	}
  -	checkClass(clz);
  -	// Check that the Class is compatible with the role definition
  -	r.verifyAdaptability(role, clz);
  -	// Record the new type
  -	Hashtable defTable = (Hashtable)defs.get(role);
  -	if (defTable == null) {
  -	    defTable = new Hashtable();
  -	    defs.put(role, defTable);
  -	}
  -	defTable.put(name, clz);
  -	return old;
  +                            role + " " + name,
  +                            project.MSG_WARN);
  +            }
  +        }
  +        Factory f = checkClass(clz);
  +        // Check that the Class is compatible with the role definition
  +        f = r.verifyAdaptability(role, f);
  +        // Record the new type
  +        Hashtable defTable = (Hashtable)defs.get(role);
  +        if (defTable == null) {
  +            defTable = new Hashtable();
  +            defs.put(role, defTable);
  +        }
  +        defTable.put(name, f);
  +
  +        String msg =
  +            " +User " + role + ": " + name + "     " + clz.getName();
  +        project.log(msg, project.MSG_DEBUG);
  +        return (old != null ? old.getOriginalClass() : null);
       }
   
       /**
        * Checks a class, whether it is suitable for serving in ANT.
  +     * @return the factory to use when instantiating the class
        * @throws BuildException and logs as Project.MSG_ERR for
        * conditions, that will cause execution to fail.
        */
  -    void checkClass(final Class clz) 
  -	throws BuildException {
  +    Factory checkClass(final Class clz) // Package on purpose
  +        throws BuildException {
  +        if (clz == null) return null;
  +
           if(!Modifier.isPublic(clz.getModifiers())) {
               final String message = clz + " is not public";
               project.log(message, Project.MSG_ERR);
  @@ -336,21 +285,53 @@
               throw new BuildException(message);
           }
           try {
  -            // Class can have a "no arg" constructor or take a single 
  +            // Class can have a "no arg" constructor or take a single
               // Project argument.
               // don't have to check for public, since
               // getConstructor finds public constructors only.
               try {
                   clz.getConstructor(new Class[0]);
  +                return new Factory(){
  +                        public Object create(Project p) {
  +                            try {
  +                                return clz.newInstance();
  +                            }
  +                            catch(Exception e) {
  +                                throw new BuildException(e);
  +                            }
  +                        }
  +
  +                        public Class getOriginalClass() {
  +                            return clz;
  +                        }
  +                    };
               } catch (NoSuchMethodException nse) {
  -                clz.getConstructor(new Class[] {Project.class});
  +                final Constructor c =
  +                    clz.getConstructor(new Class[] {Project.class});
  +                return new Factory(){
  +                        public Object create(Project p) {
  +                            try {
  +                                return c.newInstance(new Object[]{p});
  +                            }
  +                            catch(Exception e) {
  +                                throw new BuildException(e);
  +                            }
  +                        }
  +
  +                        public Class getOriginalClass() {
  +                            return clz;
  +                        }
  +                    };
               }
           } catch(NoSuchMethodException e) {
  -            final String message = 
  -		"No valid public constructor in " + clz;
  +            final String message = "No valid public constructor in " + clz;
               project.log(message, Project.MSG_ERR);
               throw new BuildException(message);
           }
  +        catch (NoClassDefFoundError ncdfe) {
  +            final String msg = "Class cannot be loaded: " + ncdfe.getMessage();
  +            throw new BuildException(msg, ncdfe);
  +        }
       }
   
       /**
  @@ -359,32 +340,25 @@
        * @param name the name of the element to sea
        * @return the Class implementation
        */
  -    public Class get(String role, String name) {
  -	Hashtable defTable = (Hashtable)defs.get(role);
  -	if (defTable != null) {
  -	    Class clz = (Class)defTable.get(name);
  -	    if (clz != null) return clz;
  -	}
  -	if (parentTable != null) {
  -	    return parentTable.get(role, name);
  -	}
  -	return null;
  -    }
  -
  -    /**
  -     * Get a Hashtable that is usable for manipulating Tasks,
  -     * @return a Hashtable that delegates to the Symbol table.
  -     */ 
  -    public Hashtable getTaskDefinitions() {
  -	return new SymbolHashtable("task");
  +    public Factory get(String role, String name) {
  +        Hashtable defTable = (Hashtable)defs.get(role);
  +        if (defTable != null) {
  +            Factory f = (Factory)defTable.get(name);
  +            if (f != null) return f;
  +        }
  +        if (parentTable != null) {
  +            return parentTable.get(role, name);
  +        }
  +        return null;
       }
   
       /**
  -     * Get a Hashtable that is usable for manipulating Datatypes,
  +     * Get a Hashtable that is usable for manipulating elements on Role.
  +     * @param role the role of the elements in the table
        * @return a Hashtable that delegates to the Symbol table.
        */
  -    public Hashtable getDataTypeDefinitions() {
  -	return new SymbolHashtable("datatype");
  +    Hashtable getDefinitions(String role) { // package scope on purpose
  +        return new SymbolHashtable(role);
       }
   
       /**
  @@ -392,100 +366,196 @@
        * the search operations to the Symbol table
        */
       private class SymbolHashtable extends Hashtable {
  -	final String role;
  -	SymbolHashtable(String role) {
  -	    this.role = role;
  -	}
  -
  -	public synchronized Object put(Object key, Object value) {
  -	    return SymbolTable.this.add(role, (String) key, (Class) value);
  -	}
  -
  -	public synchronized Object get(Object key) {
  -	    return SymbolTable.this.get(role, (String)key);
  -	}
  +        final String role;
  +        SymbolHashtable(String role) {
  +            this.role = role;
  +        }
  +
  +        public synchronized Object put(Object key, Object value) {
  +            return SymbolTable.this.add(role, (String) key, (Class) value);
  +        }
  +
  +        public synchronized Object get(Object key) {
  +            Factory f = SymbolTable.this.get(role, (String)key);
  +            return (f == null? null : f.getOriginalClass());
  +        }
  +    }
  +
  +    /**
  +     * Factory for creating ANT objects.
  +     * Class objects are not instanciated directly but through a Factory
  +     * which is able to resolve issues such as proxys and such.
  +     */
  +    public static interface Factory {
  +        /**
  +         * Creates an object for the Role
  +         * @param the project in which it is created
  +         * @return the instantiated object with a proxy if necessary
  +         */
  +        public Object create(Project p);
  +
  +        /**
  +         * Creates an object for the Role, adapted if necessary
  +         * for a particular interface.
  +         */
  +//  	public Object adaptFor(Class clz, Project p, Object o);
  +
  +        /**
  +         * The original class of the object without proxy.
  +         */
  +        public Class getOriginalClass();
       }
   
       /**
        * The definition of a role
        */
       public class Role {
  -	private Method interfaceMethod;
  -	private Method adapterVerifier;
  -	
  -	/**
  -	 * Creates a new Role object
  -	 * @param roleClz the class that defines the role
  -	 * @param adapterClz the class for the adapter, or null if none
  -	 */
  -	Role(Class roleClz, Class adapterClz) {
  -	    interfaceMethod = validInterface(roleClz);
  -	    adapterVerifier = validAdapter(adapterClz, interfaceMethod);
  -	}
  -
  -	/**
  -	 * Get the method used to set on interface
  -	 */
  -	public Method getInterfaceMethod() {
  -	    return interfaceMethod;
  -	}
  -
  -	/**
  -	 * Instantiate a new adapter for this role.
  -	 */
  -	public RoleAdapter createAdapter() {
  -	    if (adapterVerifier == null) return null;
  -	    
  -	    try {
  -		return (RoleAdapter) 
  -		    adapterVerifier.getDeclaringClass().newInstance();
  -	    }
  -	    catch(BuildException be) {
  -		throw be;
  -	    }
  -	    catch(Exception e) {
  -		throw new BuildException(e);
  -	    }
  -	}
  -    
  -	/**
  -	 * Verify if the class can be adapted to use by the role
  -	 * @param role the name of the role to verify
  -	 * @param clz the class to verify
  -	 */
  -	public void verifyAdaptability(String role, Class clz) {
  -	    if (interfaceMethod.getParameterTypes()[0].isAssignableFrom(clz)) {
  -		return;
  -	    }
  -	    if (adapterVerifier == null) {
  -		String msg = "Class " + clz.getName() + 
  -		    " incompatible with role: " + role;
  -		throw new BuildException(msg);
  -	    }
  -	    try {
  -		try {
  -		    adapterVerifier.invoke(null, 
  -					   new Object[]{clz, project});
  -		}
  -		catch (InvocationTargetException ite) {
  -		    throw ite.getTargetException();
  -		}
  -	    }
  -	    catch(BuildException be) { throw be; }
  -	    catch(Error err) {throw err; }
  -	    catch(Throwable t) {
  -		throw new BuildException(t);
  -	    }
  -	}
  -	
  -	public boolean isSameAsFor(Class clz, Class pclz) {
  -	    return interfaceMethod.getDeclaringClass().equals(clz) &&
  -		((adapterVerifier == null && pclz == null) ||
  -		 adapterVerifier.getDeclaringClass().equals(pclz));
  -	}
  -
  -	public boolean isImplementedBy(Class clz) {
  -	    return interfaceMethod.getDeclaringClass().isAssignableFrom(clz);
  -	}
  +        private Method interfaceMethod;
  +        private Method adapterVerifier;
  +        private Factory adapterFactory;
  +
  +        /**
  +         * Creates a new Role object
  +         * @param roleClz the class that defines the role
  +         * @param adapterClz the class for the adapter, or null if none
  +         */
  +        Role(Class roleClz, Class adapterClz) {
  +            interfaceMethod = validInterface(roleClz);
  +            adapterFactory = checkClass(adapterClz);
  +            adapterVerifier = validAdapter(adapterClz, interfaceMethod);
  +        }
  +
  +        /**
  +         * Get the method used to set on interface
  +         */
  +        public Method getInterfaceMethod() {
  +            return interfaceMethod;
  +        }
  +
  +        /**
  +         * Instantiate a new adapter for this role.
  +         */
  +        public RoleAdapter createAdapter(Project p) {
  +            if (adapterFactory == null) return null;
  +
  +            try {
  +                return (RoleAdapter) adapterFactory.create(p);
  +            }
  +            catch(BuildException be) {
  +                throw be;
  +            }
  +            catch(Exception e) {
  +                throw new BuildException(e);
  +            }
  +        }
  +
  +        /**
  +         * Verify if the class can be adapted to use by the role
  +         * @param role the name of the role to verify
  +         * @param f the factory for the class to verify
  +         */
  +        public Factory verifyAdaptability(String role, final Factory f) {
  +            final Class clz = f.getOriginalClass();
  +            if (interfaceMethod.getParameterTypes()[0].isAssignableFrom(clz)) {
  +                return f;
  +            }
  +            if (adapterVerifier == null) {
  +                String msg = "Class " + clz.getName() +
  +                    " incompatible with role: " + role;
  +                throw new BuildException(msg);
  +            }
  +            try {
  +                try {
  +                    adapterVerifier.invoke(null, new Object[]{clz, project});
  +                    return new Factory(){
  +                            public Object create(Project p) {
  +                                RoleAdapter ra = createAdapter(p);
  +                                ra.setProxy(f.create(p));
  +                                return ra;
  +                            }
  +
  +                            public Class getOriginalClass() {
  +                                return clz;
  +                            }
  +                        };
  +                }
  +                catch (InvocationTargetException ite) {
  +                    throw ite.getTargetException();
  +                }
  +            }
  +            catch(BuildException be) { throw be; }
  +            catch(Error err) {throw err; }
  +            catch(Throwable t) {
  +                throw new BuildException(t);
  +            }
  +        }
  +
  +        public boolean isSameAsFor(Class clz, Class pclz) {
  +            return interfaceMethod.getDeclaringClass().equals(clz) &&
  +                ((adapterVerifier == null && pclz == null) ||
  +                 adapterVerifier.getDeclaringClass().equals(pclz));
  +        }
  +
  +        public boolean isImplementedBy(Class clz) {
  +            return interfaceMethod.getDeclaringClass().isAssignableFrom(clz);
  +        }
  +
  +        /**
  +         * Verify if the interface is valid.
  +         * @param clz the interface to validate
  +         * @return the method defined by the interface
  +         */
  +        private Method validInterface(Class clz) {
  +            Method m[] = clz.getDeclaredMethods();
  +            if (m.length == 1
  +                && java.lang.Void.TYPE.equals(m[0].getReturnType())) {
  +                Class args[] = m[0].getParameterTypes();
  +                if (args.length == 1
  +                    && !java.lang.String.class.equals(args[0])
  +                    && !args[0].isArray()
  +                    && !args[0].isPrimitive()) {
  +                    return m[0];
  +                }
  +                else {
  +                    throw new BuildException("Invalid role interface method in: "
  +                                             + clz.getName());
  +                }
  +            }
  +            else {
  +                throw new BuildException("More than one method on role interface");
  +            }
  +        }
  +
  +        /**
  +         * Verify if the adapter is valid with respect to the interface.
  +         * @param clz the class adapter to validate
  +         * @param mtd the method whose only argument must match
  +         * @return the static method to use for validating adaptees
  +         */
  +        private Method validAdapter(Class clz, Method mtd) {
  +            if (clz == null) return null;
  +
  +            if (!mtd.getParameterTypes()[0].isAssignableFrom(clz)) {
  +                String msg = "Adapter " + clz.getName() +
  +                    " is incompatible with role interface " +
  +                    mtd.getDeclaringClass().getName();
  +                throw new BuildException(msg);
  +            }
  +            String msg = "Class " + clz.getName() + " is not an adapter: ";
  +            if (!RoleAdapter.class.isAssignableFrom(clz)) {
  +                throw new BuildException(msg + "does not implement RoleAdapter");
  +            }
  +            try {
  +                Method chk = clz.getMethod("checkClass", CHECK_ADAPTER_PARAMS);
  +                if (!Modifier.isStatic(chk.getModifiers())) {
  +                    throw new BuildException(msg + "checkClass() is not static");
  +                }
  +                return chk;
  +            }
  +            catch(NoSuchMethodException nme){
  +                throw new BuildException(msg + "checkClass() not found", nme);
  +            }
  +        }
  +
       }
   }
  
  
  
  1.4       +22 -14    jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java
  
  Index: TaskAdapter.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- TaskAdapter.java	2 Mar 2002 22:20:43 -0000	1.3
  +++ TaskAdapter.java	3 Mar 2002 12:37:13 -0000	1.4
  @@ -1,7 +1,7 @@
   /*
    * The Apache Software License, Version 1.1
    *
  - * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights 
  + * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
    * reserved.
    *
    * Redistribution and use in source and binary forms, with or without
  @@ -9,7 +9,7 @@
    * are met:
    *
    * 1. Redistributions of source code must retain the above copyright
  - *    notice, this list of conditions and the following disclaimer. 
  + *    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
  @@ -17,15 +17,15 @@
    *    distribution.
    *
    * 3. The end-user documentation included with the redistribution, if
  - *    any, must include the following acknowlegement:  
  - *       "This product includes software developed by the 
  + *    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 
  + *    from this software without prior written permission. For written
    *    permission, please contact apache@apache.org.
    *
    * 5. Products derived from this software may not be called "Apache"
  @@ -55,6 +55,7 @@
   package org.apache.tools.ant;
   
   import java.lang.reflect.Method;
  +import java.lang.reflect.InvocationTargetException;
   
   
   
  @@ -68,7 +69,7 @@
   public class TaskAdapter extends Task implements RoleAdapter {
   
       Object proxy;
  -    
  +
       /**
        * Checks a class, whether it is suitable to be adapted by TaskAdapter.
        *
  @@ -81,15 +82,15 @@
        * Logs other suspicious conditions with Project.MSG_WARN.
        */
       public static void checkTaskClass(final Class taskClass, final Project project) {
  -	// This code is for backward compatibility
  -	checkClass(taskClass, project);
  +        // This code is for backward compatibility
  +        checkClass(taskClass, project);
       }
   
       /**
        * Checks a class, whether it is suitable to be adapted.
        *
        * Checks conditions only, which are additionally required for a tasks
  -     * adapted by TaskAdapter. 
  +     * adapted by TaskAdapter.
        *
        * Throws a BuildException and logs as Project.MSG_ERR for
        * conditions, that will cause the task execution to fail.
  @@ -114,7 +115,7 @@
               throw new BuildException(message);
           }
       }
  -    
  +
       /**
        * Do the execution.
        */
  @@ -122,7 +123,7 @@
           Method setProjectM = null;
           try {
               Class c = proxy.getClass();
  -            setProjectM = 
  +            setProjectM =
                   c.getMethod( "setProject", new Class[] {Project.class});
               if(setProjectM != null) {
                   setProjectM.invoke(proxy, new Object[] {project});
  @@ -131,7 +132,7 @@
               // ignore this if the class being used as a task does not have
               // a set project method.
           } catch( Exception ex ) {
  -            log("Error setting project in " + proxy.getClass(), 
  +            log("Error setting project in " + proxy.getClass(),
                   Project.MSG_ERR);
               throw new BuildException( ex );
           }
  @@ -146,14 +147,20 @@
                   throw new BuildException("No public execute() in " + proxy.getClass());
               }
               executeM.invoke(proxy, null);
  -            return; 
  +            return;
  +        } catch( InvocationTargetException ite ) {
  +            Throwable t = ite.getTargetException();
  +            if (t instanceof BuildException) {
  +                throw (BuildException) t;
  +            }
  +            throw new BuildException(t);
           } catch( Exception ex ) {
               log("Error in " + proxy.getClass(), Project.MSG_ERR);
               throw new BuildException( ex );
           }
   
       }
  -    
  +
       /**
        * Set the target object class
        */
  @@ -165,4 +172,5 @@
           return this.proxy ;
       }
   
  +    public void setId(String id) {}
   }
  
  
  

--
To unsubscribe, e-mail:   <mailto:ant-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:ant-dev-help@jakarta.apache.org>


Mime
View raw message