ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bode...@apache.org
Subject cvs commit: jakarta-ant/proposal/sandbox/antlib/src/testcases/org/apache/ant/contrib Case.java
Date Mon, 18 Feb 2002 08:55:17 GMT
bodewig     02/02/18 00:55:17

  Modified:    proposal/sandbox/antlib build.xml
               proposal/sandbox/antlib/src/main/org/apache/tools/ant
                        Project.java RoleAdapter.java SymbolTable.java
                        TaskAdapter.java
               proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs
                        Ant.java Antlib.java
               proposal/sandbox/antlib/src/main/org/apache/tools/ant/types
                        DataTypeAdapterTask.java
  Added:       proposal/sandbox/antlib .cvsignore
               proposal/sandbox/antlib/src/main/org/apache/tools/ant
                        IntrospectionHelper.java ProjectHelper.java
               proposal/sandbox/antlib/src/testcases build.xml
                        case-antlib.xml case.xml
               proposal/sandbox/antlib/src/testcases/org/apache/ant/contrib
                        Case.java
  Log:
  Antlib proposal Second installment
  
  Submitted by:	Jose Alberto Fernandez <j_a_fernandez@yahoo.com>
  
  Revision  Changes    Path
  1.2       +9 -0      jakarta-ant/proposal/sandbox/antlib/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/sandbox/antlib/build.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- build.xml	11 Feb 2002 03:39:00 -0000	1.1
  +++ build.xml	18 Feb 2002 08:55:16 -0000	1.2
  @@ -6,6 +6,7 @@
     <property name='build' location='build' />
     <property name='dist' location='dist' />
     <property name='classes' location='${build}/classes' />
  +  <property name='testcases' location='src/testcases' />
   
     <property name="debug" value="true" />
     <property name="deprecation" value="false" />
  @@ -19,6 +20,8 @@
         <fileset dir='${orig-classes}'>
           <include name='**' />
   	<exclude name='org/apache/tools/ant/Project.class' />
  +	<exclude name='org/apache/tools/ant/ProjectHelper.class' />
  +	<exclude name='org/apache/tools/ant/IntrospectionHelper.class' />
   	<exclude name='org/apache/tools/ant/TaskAdapter.class' />
   	<exclude name='org/apache/tools/ant/taskdefs/Ant.class' />
         </fileset>
  @@ -52,6 +55,12 @@
   
     <target name='clean'>
       <delete dir='${build}' />
  +  </target>
  +
  +  <target name='test'>
  +    <ant dir='${testcases}' inheritAll='false'/>
  +    <ant dir='${testcases}' 
  +         antfile='${testcases}/case.xml' inheritAll='false'/>
     </target>
   
     <target name='cleanall' depends='clean'>
  
  
  
  1.1                  jakarta-ant/proposal/sandbox/antlib/.cvsignore
  
  Index: .cvsignore
  ===================================================================
  build
  dist
  
  
  
  1.2       +169 -105  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.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Project.java	11 Feb 2002 03:39:00 -0000	1.1
  +++ Project.java	18 Feb 2002 08:55:16 -0000	1.2
  @@ -63,12 +63,15 @@
   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.Path;
   
   /**
    * Central representation of an Ant project. This class defines a
  @@ -90,6 +93,9 @@
       public final static int MSG_VERBOSE = 3;
       public final static int MSG_DEBUG = 4;
   
  +    public final static String TASK_ROLE = "task";
  +    public final static String DATATYPE_ROLE = "datatype";
  +
       // private set of constants to represent the state
       // of a DFS of the Target dependencies
       private final static String VISITING = "VISITING";
  @@ -165,30 +171,26 @@
           fileUtils = FileUtils.newFileUtils();
   	symbols = new SymbolTable();
   	symbols.setProject(this);
  +	loadDefinitions();
       }
       
       /**
        * create a new ant project that inherits from caler project
        * @param p the calling project
        */
  -    public Project(Project p) {
  +    private Project(Project p) {
           fileUtils = FileUtils.newFileUtils();
  -	symbols = new SymbolTable(p);
  +	symbols = new SymbolTable(p.getSymbols());
   	symbols.setProject(this);
       }
       
       /**
  -     * 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, 
  +    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";
  @@ -248,7 +250,23 @@
           } catch (IOException ioe) {
               throw new BuildException("Can't load default datatype list");
           }
  +    }
   
  +    /**
  +     * Creates a subproject of the current project.
  +     */
  +    public Project createSubProject() {
  +	return new Project(this);
  +    }
  +
  +    /**
  +     * Initialise the project.
  +     *
  +     * This involves setting the default task definitions and loading the
  +     * system properties.
  +     */
  +    public void init() throws BuildException {
  +        setJavaVersionProperty();
           setSystemProperties();
       }
   
  @@ -275,7 +293,7 @@
       /**
        * Get the symbols associated with this project.
        */
  -    public SymbolTable getSymbols() {
  +    private SymbolTable getSymbols() { // Package protected on purpose
   	return symbols;
       }
   
  @@ -618,6 +636,46 @@
           }
       }
   
  +    public ClassLoader addToLoader(String loader, Path path) {
  +	return symbols.addToLoader(loader, path);
  +    }
  +
  +    public boolean addRoleDefinition(String role, 
  +				     Class roleClass, Class adapter)
  +    {
  +	return symbols.addRole(role, roleClass, adapter);
  +    }
  +
  +    /**
  +     * 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
  +     */
  +    public boolean isRoleDefined(String name) {
  +	return (symbols.getRole(name) != null);
  +    }
  +
  +    public void addDefinitionOnRole(String role,
  +				    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);
  +        }
  +    }
  +
  +    /**
  +     * 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
  +     */
  +    public boolean isDefinedOnRole(String role, String name) {
  +	return (symbols.get(role, name) != null);
  +    }
  +    
       /**
        * add a new task definition, complain if there is an overwrite attempt
        * @param taskName name of the task
  @@ -627,21 +685,14 @@
        */
       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); 
  +	addDefinitionOnRole(TASK_ROLE, taskName, 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.
  +     * @deprecated this is done now when added to SymbolTable
        */
       public void checkTaskClass(final Class taskClass) throws BuildException {
           if( !Task.class.isAssignableFrom(taskClass) ) {
  @@ -653,7 +704,7 @@
        * get the current task definition hashtable
        */
       public Hashtable getTaskDefinitions() {
  -        return symbols.getTaskDefinitions();
  +        return symbols.getDefinitions(TASK_ROLE);
       }
   
       /**
  @@ -662,18 +713,14 @@
        * @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
        */
       public Hashtable getDataTypeDefinitions() {
  -        return symbols.getDataTypeDefinitions();
  +        return symbols.getDefinitions(DATATYPE_ROLE);
       }
   
       /**
  @@ -701,7 +748,7 @@
        * in the project.
        * @see Project#addOrReplaceTarget to replace existing Targets.
        */
  -     public void addTarget(String targetName, Target target)
  +    public void addTarget(String targetName, Target target)
            throws BuildException {
            if (targets.get(targetName) != null) {
                throw new BuildException("Duplicate target: `"+targetName+"'");
  @@ -738,45 +785,95 @@
       }
   
       /**
  +     * 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 = 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);
  +		}
  +	    }
  +            String msg = "   +" + role + ": " + type;
  +            log (msg, MSG_DEBUG);
  +	    return o;
  +	}
  +	catch (Throwable t) {
  +            String msg = "Could not create " + role + " of type: "
  +                 + type + " due to " + t;
  +            throw new BuildException(msg, t);
  +	}
  +    }
  +
  +    /**
  +     *
  +     */
  +    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
        * @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) {
  -            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;
  -            }
  -            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;
  -        } catch (Throwable t) {
  -            String msg = "Could not create task of type: "
  -                 + taskType + " due to " + t;
  -            throw new BuildException(msg, t);
  -        }
  +	return (Task) createForRole(TASK_ROLE, taskType);
       }
   
       /**
  @@ -820,47 +917,11 @@
        * @return null if the datatype name is unknown
        */
       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);
       }
   
       /**
  @@ -1227,7 +1288,10 @@
       }
   
       public void addReference(String name, Object value) {
  -        if (null != references.get(name)) {
  +	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);
           }
  
  
  
  1.2       +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.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- RoleAdapter.java	11 Feb 2002 03:39:00 -0000	1.1
  +++ RoleAdapter.java	18 Feb 2002 08:55:16 -0000	1.2
  @@ -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.2       +211 -143  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.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- SymbolTable.java	11 Feb 2002 03:39:00 -0000	1.1
  +++ SymbolTable.java	18 Feb 2002 08:55:16 -0000	1.2
  @@ -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.*;
  @@ -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;
       }
   
       /**
  @@ -109,6 +110,54 @@
       }
   
       /**
  +     * 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;
  +    }
  +    
  +    /**
        * Find all the roles supported by a Class
        * on this symbol table.
        * @param clz the class to analyze
  @@ -133,13 +182,13 @@
   		list.addElement(role);
   	    }
   	}
  -	if (parentTable != null) findRoles(clz, list);
  +	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);
  @@ -171,112 +220,6 @@
   	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;
  -    }
  -
  -    /**
  -     * 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.
  @@ -291,13 +234,13 @@
   	    throw new BuildException("Unknown role: " + role);
   	}
   	// Check if it is already defined
  -	Class old = get(role, name);
  +	Factory old = get(role, name);
   	if (old != null) {
  -	    if (old.equals(clz)) {
  +	    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;
  +		return old.getOriginalClass();
   	    }
   	    else {
                   project.log("Trying to override old definition of " +
  @@ -305,26 +248,33 @@
   			    project.MSG_WARN);
   	    }
   	}
  -	checkClass(clz);
  +	Factory f = checkClass(clz);
   	// Check that the Class is compatible with the role definition
  -	r.verifyAdaptability(role, clz);
  +	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, clz);
  -	return old;
  +	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) 
  +    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);
  @@ -342,8 +292,37 @@
               // 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 = 
  @@ -359,11 +338,11 @@
        * @param name the name of the element to sea
        * @return the Class implementation
        */
  -    public Class get(String role, String name) {
  +    public Factory 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;
  +	    Factory f = (Factory)defTable.get(name);
  +	    if (f != null) return f;
   	}
   	if (parentTable != null) {
   	    return parentTable.get(role, name);
  @@ -372,19 +351,12 @@
       }
   
       /**
  -     * Get a Hashtable that is usable for manipulating Tasks,
  +     * 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 getTaskDefinitions() {
  -	return new SymbolHashtable("task");
  -    }
  -
  -    /**
  -     * Get a Hashtable that is usable for manipulating Datatypes,
  -     * @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);
       }
   
       /**
  @@ -402,16 +374,43 @@
   	}
   
   	public synchronized Object get(Object key) {
  -	    return SymbolTable.this.get(role, (String)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;
  +	private Factory adapterFactory;
   	
   	/**
   	 * Creates a new Role object
  @@ -420,6 +419,7 @@
   	 */
   	Role(Class roleClz, Class adapterClz) {
   	    interfaceMethod = validInterface(roleClz);
  +	    adapterFactory = checkClass(adapterClz);
   	    adapterVerifier = validAdapter(adapterClz, interfaceMethod);
   	}
   
  @@ -433,12 +433,11 @@
   	/**
   	 * Instantiate a new adapter for this role.
   	 */
  -	public RoleAdapter createAdapter() {
  -	    if (adapterVerifier == null) return null;
  +	public RoleAdapter createAdapter(Project p) {
  +	    if (adapterFactory == null) return null;
   	    
   	    try {
  -		return (RoleAdapter) 
  -		    adapterVerifier.getDeclaringClass().newInstance();
  +		return (RoleAdapter) adapterFactory.create(p);
   	    }
   	    catch(BuildException be) {
   		throw be;
  @@ -451,11 +450,12 @@
   	/**
   	 * 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
  +	 * @param f the factory for the class to verify
   	 */
  -	public void verifyAdaptability(String role, Class clz) {
  +	public Factory verifyAdaptability(String role, final Factory f) {
  +	    final Class clz = f.getOriginalClass();
   	    if (interfaceMethod.getParameterTypes()[0].isAssignableFrom(clz)) {
  -		return;
  +		return f;
   	    }
   	    if (adapterVerifier == null) {
   		String msg = "Class " + clz.getName() + 
  @@ -464,8 +464,18 @@
   	    }
   	    try {
   		try {
  -		    adapterVerifier.invoke(null, 
  -					   new Object[]{clz, project});
  +		    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();
  @@ -487,5 +497,63 @@
   	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.2       +1 -0      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.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TaskAdapter.java	11 Feb 2002 03:39:00 -0000	1.1
  +++ TaskAdapter.java	18 Feb 2002 08:55:16 -0000	1.2
  @@ -165,4 +165,5 @@
           return this.proxy ;
       }
   
  +    public void setId(String id) {}
   }
  
  
  
  1.1                  jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/IntrospectionHelper.java
  
  Index: IntrospectionHelper.java
  ===================================================================
  /*
   * 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.1                  jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/ProjectHelper.java
  
  Index: ProjectHelper.java
  ===================================================================
  /*
   * 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);
          }
      }
  
  }
  
  
  
  1.2       +1 -23     jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java
  
  Index: Ant.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Ant.java	11 Feb 2002 03:39:00 -0000	1.1
  +++ Ant.java	18 Feb 2002 08:55:16 -0000	1.2
  @@ -138,10 +138,8 @@
       }
   
       public void init() {
  -        newProject = new Project(project);
  +        newProject = project.createSubProject();
           newProject.setJavaVersionProperty();
  -//          newProject.addTaskDefinition("property", 
  -//                               (Class)project.getTaskDefinitions().get("property"));
       }
   
       private void reinit() {
  @@ -184,26 +182,6 @@
                   log( "Ant: Can't set output to " + output );
               }
           }
  -
  -//          Hashtable taskdefs = project.getTaskDefinitions();
  -//          Enumeration et = taskdefs.keys();
  -//          while (et.hasMoreElements()) {
  -//              String taskName = (String) et.nextElement();
  -//              if (taskName.equals("property")) {
  -//                  // we have already added this taskdef in #init
  -//                  continue;
  -//              }
  -//              Class taskClass = (Class) taskdefs.get(taskName);
  -//              newProject.addTaskDefinition(taskName, taskClass);
  -//          }
  -
  -//          Hashtable typedefs = project.getDataTypeDefinitions();
  -//          Enumeration e = typedefs.keys();
  -//          while (e.hasMoreElements()) {
  -//              String typeName = (String) e.nextElement();
  -//              Class typeClass = (Class) typedefs.get(typeName);
  -//              newProject.addDataTypeDefinition(typeName, typeClass);
  -//          }
   
           // set user-defined or all properties from calling project
           Hashtable prop1;
  
  
  
  1.3       +7 -30     jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java
  
  Index: Antlib.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Antlib.java	11 Feb 2002 03:39:00 -0000	1.2
  +++ Antlib.java	18 Feb 2002 08:55:16 -0000	1.3
  @@ -418,7 +418,7 @@
           if (classpath != null) {
               clspath.append(classpath);
           }
  -	return project.getSymbols().addToLoader(loaderId, clspath);
  +	return project.addToLoader(loaderId, clspath);
       }
   
   
  @@ -505,8 +505,6 @@
   
   	private int level = 0;
   
  -	private SymbolTable symbols = null;
  -
   	private String name = null;
   	private String className = null;
   	private String adapter = null;
  @@ -520,7 +518,6 @@
           AntLibraryHandler(ClassLoader classloader, Properties als) {
               this.classloader = classloader;
               this.aliasMap = als;
  -	    this.symbols = project.getSymbols();
           }
   
           /**
  @@ -591,15 +588,15 @@
   
   	    try {
   		if ("role".equals(tag)) {
  -		    if (isRoleInUse(name)) {
  +		    if (project.isRoleDefined(name)) {
   			String msg = "Cannot override role: " + name;
   			log(msg, Project.MSG_WARN);
   			return;			
   		    }
   		    // Defining a new role
  -		    symbols.addRole(name, loadClass(className),
  -				    (adapter == null? 
  -				     null : loadClass(adapter))); 
  +		    project.addRoleDefinition(name, loadClass(className),
  +					      (adapter == null? 
  +					       null : loadClass(adapter))); 
   		    return;
   		}
   
  @@ -610,12 +607,12 @@
   		    name = alias;
   		}
   		//catch an attempted override of an existing name
  -		if (!override && isInUse(tag, name)) {
  +		if (!override && project.isDefinedOnRole(tag, name)) {
   		    String msg = "Cannot override " + tag + ": " + name;
   		    log(msg, Project.MSG_WARN);
   		    return;
   		}
  -		symbols.add(tag, name, loadClass(className));
  +		project.addDefinitionOnRole(tag, name, loadClass(className));
   	    }
   	    catch(BuildException be) {
   		throw new SAXParseException(be.getMessage(), locator, be);
  @@ -650,26 +647,6 @@
   		throw new SAXParseException(msg, locator);
   	    }
   	}
  -
  -        /**
  -         * 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
  -         */
  -        private boolean isInUse(String role, String name) {
  -            return (symbols.get(role, name) != null);
  -        }
  -
  -        /**
  -         * 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
  -         */
  -        private boolean isRoleInUse(String name) {
  -            return (symbols.getRole(name) != null);
  -        }
   
       //end inner class AntLibraryHandler
       }
  
  
  
  1.2       +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.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- DataTypeAdapterTask.java	11 Feb 2002 07:00:06 -0000	1.1
  +++ DataTypeAdapterTask.java	18 Feb 2002 08:55:16 -0000	1.2
  @@ -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.1                  jakarta-ant/proposal/sandbox/antlib/src/testcases/build.xml
  
  Index: build.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <project name="local" default="libs" >
  
    <taskdef name="antjar" classname="org.apache.tools.ant.taskdefs.Antjar" />
    <property name="src" location="." />
    <property name="classes" location="../../build/testcases" />
    <property name="contrib" location="../../build/case_contrib.jar" />
  
    <target name="libs" depends="compile" >
      <antjar destfile="${contrib}" antxml="${src}/case-antlib.xml" >
        <fileset dir="${classes}" >
          <include name="org/**" />
        </fileset>
      </antjar>
    </target>
  
    <target name="compile" >
      <mkdir dir="${classes}" />
      <javac srcdir="${src}" destdir="${classes}" >
       <include name="org/**/*.java" />
      </javac>
    </target>
  
    <target name="clean" >
      <delete dir="${classes}" />
      <delete file='${contrib}'/>
    </target>
  
  </project>
  
  
  
  1.1                  jakarta-ant/proposal/sandbox/antlib/src/testcases/case-antlib.xml
  
  Index: case-antlib.xml
  ===================================================================
  <?xml version="1.0"  ?>
  
  <antlib version="1.0" >
    <task name="case" class="org.apache.ant.contrib.Case" />
  </antlib>
  
  
  
  1.1                  jakarta-ant/proposal/sandbox/antlib/src/testcases/case.xml
  
  Index: case.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <project name="case-test" default="test" basedir=".">
  
    <property name="value" value="task.xml" />
  
    <target name="init">
      <taskdef name="antlib" classname="org.apache.tools.ant.taskdefs.Antlib" />
      <antlib file="../../build/case_contrib.jar" />
    </target>
  
    <target name="test" depends="init,case,test1,test2,test3">
     <echo message="Value=${value}" />
    </target>
  
    <target name="case" >
      <case property="value" >
        <when value="task.xml" property="value.xml" />
      </case>
      <case property="location" >
        <when value="loc" property="location.fail" />
        <when value="" property="location.fail" />
        <else property="location.unset" />
      </case>
    </target>
  
    <target name="test1" if="value.xml">
     <echo message="Value equals to itself" />
    </target>
  
    <target name="test2" if="location.fail">
     <fail message="Location passed" />
    </target>
  
    <target name="test3" if="location.unset">
     <echo message="Location does not exists" />
    </target>
  
  </project>
  
  
  
  1.1                  jakarta-ant/proposal/sandbox/antlib/src/testcases/org/apache/ant/contrib/Case.java
  
  Index: Case.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 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", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.ant.contrib;
  
  import java.io.*;
  import java.util.*;
  import org.apache.tools.ant.*;
  import org.apache.tools.ant.types.*;
  
  /**
   * Will set one of the given properties depending on the result of testing
   * the value of another property.
   *
   * <!ELEMENT case (when*, else) >
   * <!ATTLIST case property CDATA #REQUIRED > The name of the property to test
   * <!ELEMENT when EMPTY >
   * <!ATTLIST when value CDATA #REQUIRED > The value to compare and set prop.
   * <!ATTLIST when property CDATA #REQUIRED > The name of the property to set
   * <!ELEMENT else EMPTY >
   * <!ATTLIST else property CDATA #REQUIRED > The name of the property to set otherwise
   * <!ATTLIST else value CDATA #IMPLIED > The value to set; default "true".
   *
   * @author Jose Alberto Fernandez <a href="mailto:jfernandez@viquity.com">jfernandez@viquity.com</a>
   */
  
  public class Case extends Task {
  
      public class When {
        private String property;
        private String value;
  
        public void setProperty(String name) {
          property = name;
        }
  
        public String getProperty() {
          return property;
        }
  
        public void setValue(String val) {
          value = val;
        }
  
        public String getValue() {
          return value;
        }
  
        public boolean tryCase(String caseValue) throws BuildException {
          if (property == null)
            throw new BuildException("Property attribute is mandatory");
  
          if (value == null)
            throw new BuildException("Value attribute is mandatory");
  
          if (!value.equals(caseValue)) return false;
  
          if (getProject().getProperty(property) == null) {
            getProject().setProperty(property, value);
          } else {
            log("Override ignored for " + property, Project.MSG_VERBOSE);
          }
          return true;
        }       
  
        public void doElse() throws BuildException {
          if (property == null)
            throw new BuildException("Property attribute is mandatory");
  
          String elseValue = (value == null) ? "true" : value;
  
          if (getProject().getProperty(property) == null) {
            getProject().setProperty(property, elseValue);
          } else {
            log("Override ignored for " + property, Project.MSG_VERBOSE);
          }
        }
      }
        
      private String caseProperty;
      private Vector whenList = new Vector();
      private When elseCase = null;
  
      public When createWhen() throws BuildException {
        When w = new When();
        whenList.addElement(w);
        return w;
      }
  
      public When createElse() throws BuildException {
        if (elseCase != null) 
          throw new BuildException("Only one else element allowed per case");
  
        return (elseCase = new When());
      }
  
      public void setProperty(String property) {
          this.caseProperty = property;
      }
  
      public void execute() throws BuildException {
          if (caseProperty == null) {
              throw new BuildException("property attribute is required", 
                                       location);
          }
          
          String caseValue = getProject().getProperty(caseProperty);
  
          for (Enumeration e = whenList.elements(); e.hasMoreElements(); ) {
            When w = (When)e.nextElement();
            if (w.tryCase(caseValue)) return;
          }
          
          if (elseCase != null)
            elseCase.doElse();
      }
  
  }
  
  
  

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