peterreilly 2003/06/26 01:54:29 Modified: docs/manual/CoreTasks taskdef.html typedef.html src/main/org/apache/tools/ant ComponentHelper.java Project.java ProjectHelper.java RuntimeConfigurable.java TaskAdapter.java UnknownElement.java src/main/org/apache/tools/ant/helper ProjectHelperImpl.java src/main/org/apache/tools/ant/taskdefs Ant.java Definer.java Taskdef.java src/testcases/org/apache/tools/ant ProjectTest.java Added: src/etc/testcases/taskdefs typeadapter.xml src/main/org/apache/tools/ant AntTypeDefinition.java TypeAdapter.java src/testcases/org/apache/tools/ant/taskdefs TypeAdapterTest.java Log: Second patch from antlib update as described in http://issues.apache.org/bugzilla/show_bug.cgi?id=19897 This patch * unifies the type and task definitions into one table * types and tasks are represented by a AntTypeDefinition object * taskadapter has been generalized to a typeadapter * <typedef/> has a number of new attributes: - adapter - adaptto - onerror * <taskdef/> html page updated to refer to <typedef/> page PR: 19897 Submitted by: Peter Reilly Revision Changes Path 1.9 +7 -71 ant/docs/manual/CoreTasks/taskdef.html Index: taskdef.html =================================================================== RCS file: /home/cvs/ant/docs/manual/CoreTasks/taskdef.html,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- taskdef.html 22 Jun 2002 23:38:27 -0000 1.8 +++ taskdef.html 26 Jun 2003 08:54:27 -0000 1.9 @@ -9,82 +9,18 @@ <h2><a name="taskdef">Taskdef</a></h2> <h3>Description</h3> -<p>Adds a task definition to the current project, such that this new task can be -used in the current project. Two attributes are needed, the name that identifies -this task uniquely, and the full name of the class (including the packages) that -implements this task.</p> -<p>You can also define a group of tasks at once using the file or -resource attributes. These attributes point to files in the format of -Java property files. Each line defines a single task in the -format:</p> -<pre> -taskname=fully.qualified.java.classname -</pre> -<p>Taskdef should be used to add your own tasks to the system. See also "<a -href="../develop.html#writingowntask">Writing your own task</a>".</p> -<h3>Parameters</h3> -<table border="1" cellpadding="2" cellspacing="0"> - <tr> - <td valign="top"><b>Attribute</b></td> - <td valign="top"><b>Description</b></td> - <td align="center" valign="top"><b>Required</b></td> - </tr> - <tr> - <td valign="top">name</td> - <td valign="top">the name of the task</td> - <td valign="top" align="center">Yes, unless file or resource have - been specified.</td> - </tr> - <tr> - <td valign="top">classname</td> - <td valign="top">the full class name implementing the task</td> - <td valign="top" align="center">Yes, unless file or resource have - been specified.</td> - </tr> - <tr> - <td valign="top">file</td> - <td valign="top">Name of the property file to load - taskname/classname pairs from.</td> - <td valign="top" align="center">No</td> - </tr> - <tr> - <td valign="top">resource</td> - <td valign="top">Name of the property resource to load - taskname/classname pairs from.</td> - <td valign="top" align="center">No</td> - </tr> - <tr> - <td valign="top">classpath</td> <td valign="top">the classpath to - use when looking up <code>classname</code> or - <code>resource</code>.</td> - <td align="center" valign="top">No</td> - </tr> - <tr> - <td valign="top">classpathref</td> - <td valign="top">Reference to a classpath to - use when looking up <code>classname</code> or - <code>resource</code>.</td> - <td align="center" valign="top">No</td> - </tr> - <tr> - <td valign="top">loaderRef</td> <td valign="top">the name of the loader that is - used to load the class, constructed from the specified classpath. Use this to - allow multiple tasks/types to be loaded with the same loader, so they can call - each other. ( introduced in ant1.5 )</td> - <td align="center" valign="top">No</td> - </tr> -</table> -<h3>Parameters specified as nested elements</h3> -<h4>classpath</h4> -<p><code>Taskdef</code>'s <i>classpath</i> attribute is a <a -href="../using.html#path">PATH like structure</a> and can also be set via a nested -<i>classpath</i> element.</p> + <p>Adds a task definition to the current project, such that this new task can be + used in the current project.</p> + <p>This task is a form of <a href="typedef.html">Typedef</a> with the + attributes "adapter" and "adaptto" set to the values + "org.apache.tools.ant.TaskAdapter" and "org.apache.tools.ant.Task" + respectively. <h3>Examples</h3> <pre> <taskdef name="myjavadoc" classname="com.mydomain.JavadocTask"/></pre> <p>makes a task called <code>myjavadoc</code> available to Ant. The class <code>com.mydomain.JavadocTask</code> implements the task.</p> <hr> -<p align="center">Copyright © 2000-2002 Apache Software Foundation. All rights +<p align="center">Copyright © 2000-2003 Apache Software Foundation. All rights Reserved.</p> </body> 1.7 +43 -10 ant/docs/manual/CoreTasks/typedef.html Index: typedef.html =================================================================== RCS file: /home/cvs/ant/docs/manual/CoreTasks/typedef.html,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- typedef.html 1 Jun 2002 12:26:33 -0000 1.6 +++ typedef.html 26 Jun 2003 08:54:27 -0000 1.7 @@ -21,7 +21,7 @@ <pre> typename=fully.qualified.java.classname </pre> -<p>Typedef should be used to add your own types to the system. Data +<p>Typedef should be used to add your own tasks and types to the system. Data types are things like <a href="../using.html#path">paths</a> or <a href="../CoreTypes/fileset.html">filesets</a> that can be defined at the project level and referenced via their ID attribute.</p> @@ -36,25 +36,23 @@ <tr> <td valign="top">name</td> <td valign="top">the name of the data type</td> - <td valign="top" align="center">Yes, unless file or resource have - been specified.</td> + <td valign="top" align="center">Yes, unless the file or resource type + attributes have been specified.</td> </tr> <tr> <td valign="top">classname</td> <td valign="top">the full class name implementing the data type</td> - <td valign="top" align="center">Yes, unless file or resource have - been specified.</td> + <td valign="top" align="center">Yes, unless file or resource + have been specified.</td> </tr> <tr> <td valign="top">file</td> - <td valign="top">Name of the property file to load - typename/classname pairs from.</td> + <td valign="top">Name of the file to load definitions from.</td> <td valign="top" align="center">No</td> </tr> <tr> <td valign="top">resource</td> - <td valign="top">Name of the property resource to load - typename/classname pairs from.</td> + <td valign="top">Name of the resouce to load definitions from.</td> <td valign="top" align="center">No</td> </tr> <tr> @@ -69,6 +67,40 @@ each other. ( introduced in ant1.5 )</td> <td align="center" valign="top">No</td> </tr> + <tr> + <td valign="top">onerror</td> + <td valign="top">The action to take if there was a failure in defining the + type. The values are <i>fail</i> - cause a build exception, <i>warn</i> + output a warning, but continue, <i>ignore</i>, do nothing. The default + is <i>fail</i>. + (introduced in ant1.6) + </td> + <td valign="top" align="center">No</td> + </tr> + <tr> + <td valign="top">adapter</td> + <td valign="top">A class that is used to adapt the defined class to + another interface/class. The adapter class must implement the interface + "org.apache.tools.ant.TypeAdapter". The adapter class will be used + to wrap the defined class unless the defined class implements/extends + the class defined by the attribute "adaptto". + If "adaptto" is not set, + the defined class will always be wrapped. + (introduced in ant1.6) + </td> + <td valign="top" align="center">No</td> + </tr> + <tr> + <td valign="top">adaptto</td> + <td valign="top">This attribute is used in conjunction with the + adapter attribute. + If the defined class does not implement/extend the interface/class + specified by this attribute, the adaptor class will be used + to wrap the class. + (introduced in ant1.6) + </td> + <td valign="top" align="center">No</td> + </tr> </table> <h3>Parameters specified as nested elements</h3> <h4>classpath</h4> @@ -79,8 +111,9 @@ <pre> <typedef name="urlset" classname="com.mydomain.URLSet"/></pre> <p>makes a data type called <code>urlset</code> available to Ant. The class <code>com.mydomain.URLSet</code> implements this type.</p> + <hr> -<p align="center">Copyright © 2001-2002 Apache Software +<p align="center">Copyright © 2001-2003 Apache Software Foundation. All rights Reserved.</p> </body> 1.1 ant/src/etc/testcases/taskdefs/typeadapter.xml Index: typeadapter.xml =================================================================== <?xml version="1.0"?> <project name="test" basedir="." default="invalid"> <property name="testcases.dir" location="../../../../build/testcases"/> <path id="testclasses"> <pathelement location="${testcases.dir}" /> <pathelement path="${java.class.path}" /> </path> <target name="taskadapter"> <typedef name="myexec" classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyExec" classpathref="testclasses" adapter="org.apache.tools.ant.TaskAdapter"/> <myexec/> </target> <target name="runadapter"> <typedef name="myrunnable" classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyRunnable" classpathref="testclasses" adapter="org.apache.tools.ant.taskdefs.TypeAdapterTest$RunnableAdapter"/> <myrunnable/> </target> <target name="runadaptererror"> <typedef name="myrunnable" classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyExec" classpathref="testclasses" adapter="org.apache.tools.ant.taskdefs.TypeAdapterTest$RunnableAdapter"/> <myrunnable/> </target> <target name="delay"> <typedef name="mytask" classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyTask" classpathref="testclasses" onerror="ignore"/> <mytask/> </target> <target name="onerror.report"> <typedef name="mytask" classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyTaskNotPresent" classpathref="testclasses" onerror="report"/> </target> <target name="onerror.ignore"> <typedef name="mytask" classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyTaskNotPresent" classpathref="testclasses" onerror="ignore"/> </target> </project> 1.12 +292 -312 ant/src/main/org/apache/tools/ant/ComponentHelper.java Index: ComponentHelper.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/ComponentHelper.java,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- ComponentHelper.java 10 Jun 2003 14:05:58 -0000 1.11 +++ ComponentHelper.java 26 Jun 2003 08:54:28 -0000 1.12 @@ -59,7 +59,15 @@ import java.util.Enumeration; import java.util.Hashtable; +import java.util.HashSet; +import java.util.Iterator; import java.util.Properties; +import java.util.Set; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; + import java.util.Vector; import java.io.InputStream; import java.io.IOException; @@ -68,21 +76,28 @@ /** * Component creation and configuration. * - * This is cut&paste from Project.java of everything related to - * task/type management. Project will just delegate. + * The class is based around handing component + * definitions in an AntTypeTable. + * + * The old task/type methods have been kept + * for backward compatibly. + * Project will just delegate its calls to this class. * * A very simple hook mechnism is provided that allows users to plug * in custom code. It is also possible to replace the default behavior * ( for example in an app embeding ant ) * * @author Costin Manolache + * @author Peter Reilly * @since Ant1.6 */ public class ComponentHelper { - /** Map from data type names to implementing classes (String to Class). */ - private Hashtable dataClassDefinitions; - /** Map from task names to implementing classes (String to Class). */ - private Hashtable taskClassDefinitions; + // Map from task names to implementing classes - not used anymore + private Hashtable taskClassDefinitions = new Hashtable(); + + /** Map from compoennt name to anttypedefinition */ + private AntTypeTable antTypeTable; + /** * Map from task names to vectors of created tasks * (String to Vector of Task). This is used to invalidate tasks if @@ -120,10 +135,24 @@ public void setProject(Project project) { this.project = project; - dataClassDefinitions= new AntTaskTable(project, false); - taskClassDefinitions= new AntTaskTable(project, true); + antTypeTable = new AntTypeTable(project); } + /** + * Used with creating child projects. Each child + * project inherites the component definitions + * from its parent. + */ + public void initSubProject(ComponentHelper helper) { + // add the types of the parent project + AntTypeTable typeTable = helper.antTypeTable; + for (Iterator i = typeTable.values().iterator(); i.hasNext();) { + AntTypeDefinition def = (AntTypeDefinition) i.next(); + def = def.copy(project); + antTypeTable.put(def.getName(), def); + } + } + /** Factory method to create the components. * * This should be called by UnknownElement. @@ -139,63 +168,54 @@ String taskName ) throws BuildException { - Object component=null; - - // System.out.println("Fallback to project default " + taskName ); - // Can't create component. Default is to use the old methods in project. + Object component = createComponent(taskName); + if (component == null) { + return null; + } - // This policy is taken from 1.5 ProjectHelper. In future the difference between - // task and type should disapear. - if( project.getDataTypeDefinitions().get(taskName) != null ) { - // This is the original policy in ProjectHelper. The 1.5 version of UnkwnonwElement - // used to try first to create a task, and if it failed tried a type. In 1.6 the diff - // should disapear. - component = this.createDataType(taskName); - if( component!=null ) return component; - } - - // from UnkwnonwElement.createTask. The 'top level' case is removed, we're - // allways lazy - component = this.createTask(taskName); + if (component instanceof Task) { + Task task = (Task) component; + task.setTaskType(taskName); + task.setTaskName(taskName); + addCreatedTask(taskName, task); + } return component; } + + /** + * Create an object for a component. + * + * @param componentName the name of the component, if + * the component is in a namespace, the + * name is prefixed withe the namespace uri and ":" + * @return the class if found or null if not. + */ + public Object createComponent(String componentName) + throws BuildException + { + return antTypeTable.create(componentName); + } /** - * get the class of a particular component + * Return the class of the component name. + * + * @param componentName the name of the component, if + * the component is in a namespace, the + * name is prefixed withe the namespace uri and ":" + * @return the class if found or null if not. */ public Class getComponentClass(String componentName) { - Class elementClass = - (Class) getTaskDefinitions().get(componentName); - if (elementClass != null) { - if (! (Task.class.isAssignableFrom(elementClass))) { - elementClass = TaskAdapter.class; - } - return elementClass; - } - return (Class) getDataTypeDefinitions().get(componentName); + return antTypeTable.getExposedClass(componentName); } - + /** - * create a named component + * Return the antTypeDefinition for a componentName */ - public Object createComponent(String componentName) - throws BuildException - { - Object obj = createTask(componentName); - if (obj == null) { - obj = createDataType(componentName); - } - if (obj == null) { - return obj; - } - project.setProjectReference(obj); - if (obj instanceof Task) { - ((Task)obj).init(); // Needed here ?? - } - return obj; + public AntTypeDefinition getDefinition(String componentName) { + return antTypeTable.getDefinition(componentName); } - + /** Initialization code - implementing the original ant component * loading from /org/apache/tools/ant/taskdefs/default.properties * and .../types/default.properties @@ -203,40 +223,8 @@ * @throws BuildException */ public void initDefaultDefinitions() throws BuildException { - String defs = "/org/apache/tools/ant/taskdefs/defaults.properties"; - - try { - Properties props = new Properties(); - InputStream in = this.getClass().getResourceAsStream(defs); - if (in == null) { - throw new BuildException("Can't load default task list"); - } - props.load(in); - in.close(); - ((AntTaskTable)taskClassDefinitions).addDefinitions( props ); - - - } catch (IOException ioe) { - throw new BuildException("Can't load default task list"); - } - - String dataDefs = "/org/apache/tools/ant/types/defaults.properties"; - - try { - Properties props = new Properties(); - InputStream in = this.getClass().getResourceAsStream(dataDefs); - if (in == null) { - throw new BuildException("Can't load default datatype list"); - } - props.load(in); - in.close(); - - ((AntTaskTable)dataClassDefinitions).addDefinitions(props); - - - } catch (IOException ioe) { - throw new BuildException("Can't load default datatype list"); - } + initTasks(); + initTypes(); } /** @@ -259,44 +247,17 @@ * * @see #checkTaskClass(Class) */ - public void addTaskDefinition(String taskName, Class taskClass) - throws BuildException { - Class old = (Class) taskClassDefinitions.get(taskName); - if (null != old) { - if (old.equals(taskClass)) { -// project.log("Ignoring override for task " + taskName -// + ", it is already defined by the same class.", -// Project.MSG_VERBOSE); - return; - } else { - int logLevel = Project.MSG_WARN; - if (old.getName().equals(taskClass.getName())) { - ClassLoader oldLoader = old.getClassLoader(); - ClassLoader newLoader = taskClass.getClassLoader(); - // system classloader on older JDKs can be null - if (oldLoader != null - && newLoader != null - && oldLoader instanceof AntClassLoader - && newLoader instanceof AntClassLoader - && ((AntClassLoader) oldLoader).getClasspath() - .equals(((AntClassLoader) newLoader).getClasspath()) - ) { - // same classname loaded from the same - // classpath components - logLevel = Project.MSG_VERBOSE; - } - } - - project.log("Trying to override old definition of task " + taskName, - logLevel); - invalidateCreatedTasks(taskName); - } - } - - String msg = " +User task: " + taskName + " " + taskClass.getName(); - project.log(msg, Project.MSG_DEBUG); + public void addTaskDefinition(String taskName, Class taskClass) { checkTaskClass(taskClass); - taskClassDefinitions.put(taskName, taskClass); + AntTypeDefinition def = new AntTypeDefinition(); + def.setProject(project); + def.setName(taskName); + def.setClassLoader(taskClass.getClassLoader()); + def.setClass(taskClass); + def.setAdapterClass(TaskAdapter.class); + def.setClassName(taskClass.getName()); + def.setAdaptToClass(Task.class); + updateDataTypeDefinition(def); } /** @@ -340,6 +301,7 @@ /** * Returns the current task definition hashtable. The returned hashtable is * "live" and so should not be modified. + * This table does not contain any information * * @return a map of from task name to implementing class * (String to Class). @@ -347,7 +309,7 @@ public Hashtable getTaskDefinitions() { return taskClassDefinitions; } - + /** * Adds a new datatype definition. * Attempting to override an existing definition with an @@ -362,27 +324,26 @@ * Must not be <code>null</code>. */ public void addDataTypeDefinition(String typeName, Class typeClass) { - synchronized(dataClassDefinitions) { - Class old = (Class) dataClassDefinitions.get(typeName); - if (null != old) { - if (old.equals(typeClass)) { -// project.log("Ignoring override for datatype " + typeName -// + ", it is already defined by the same class.", -// Project.MSG_VERBOSE); - return; - } else { - project.log("Trying to override old definition of datatype " - + typeName, Project.MSG_WARN); - } - } - dataClassDefinitions.put(typeName, typeClass); - } + AntTypeDefinition def = new AntTypeDefinition(); + def.setProject(project); + def.setName(typeName); + def.setClass(typeClass); + updateDataTypeDefinition(def); String msg = " +User datatype: " + typeName + " " + typeClass.getName(); project.log(msg, Project.MSG_DEBUG); } /** + * Describe <code>addDataTypeDefinition</code> method here. + * + * @param def an <code>AntTypeDefinition</code> value + */ + public void addDataTypeDefinition(AntTypeDefinition def) { + updateDataTypeDefinition(def); + } + + /** * Returns the current datatype definition hashtable. The returned * hashtable is "live" and so should not be modified. * @@ -390,7 +351,7 @@ * (String to Class). */ public Hashtable getDataTypeDefinitions() { - return dataClassDefinitions; + return antTypeTable; } /** @@ -432,49 +393,26 @@ * creation fails. */ private Task createNewTask(String taskType) throws BuildException { - Class c = (Class) taskClassDefinitions.get(taskType); - + Class c = antTypeTable.getExposedClass(taskType); if (c == null) { return null; } + + if (! Task.class.isAssignableFrom(c)) { + return null; + } + Task task = (Task) antTypeTable.create(taskType); + if (task == null) { + return null; + } + task.setTaskType(taskType); - try { - Object o = c.newInstance(); - if ( project != null ) { - project.setProjectReference( o ); - } - 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); - if ( project != null ) { - project.setProjectReference( taskA ); - } - task = taskA; - } - task.setProject( project ); - task.setTaskType(taskType); - - // set default value, can be changed by the user - task.setTaskName(taskType); + // set default value, can be changed by the user + task.setTaskName(taskType); - String msg = " +Task: " + taskType; - project.log (msg, Project.MSG_DEBUG); - return task; - } catch (NoClassDefFoundError ncdfe) { - String msg = "Task " + taskType + ": A class needed by class " - + c + " cannot be found: " + ncdfe.getMessage(); - throw new BuildException(msg, ncdfe); - } catch (Throwable t) { - System.out.println("task CL=" + c.getClassLoader()); - String msg = "Could not create task of type: " - + taskType + " due to " + t; - throw new BuildException(msg, t); - } + String msg = " +Task: " + taskType; + project.log (msg, Project.MSG_DEBUG); + return task; } /** @@ -538,52 +476,11 @@ * instance creation fails. */ public Object createDataType(String typeName) throws BuildException { - Class c = (Class) dataClassDefinitions.get(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[] {project}); - } - if ( project != null ) { - project.setProjectReference( o ); - } - String msg = " +DataType: " + typeName; - project.log(msg, Project.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); - } + return antTypeTable.create(typeName); } /** - * Returns a description of the type of the given element, with - * special handling for instances of tasks and data types. + * Returns a description of the type of the given element. * <p> * This is useful for logging purposes. * @@ -595,127 +492,210 @@ * @since Ant 1.6 */ public String getElementName(Object element) { - Hashtable elements = taskClassDefinitions; + // PR: I do not know what to do if the object class + // has multiple defines + // but this is for logging only... Class elementClass = element.getClass(); - String typeName = "task"; - if (!elements.contains(elementClass)) { - elements = dataClassDefinitions; - typeName = "data type"; - if (!elements.contains(elementClass)) { - elements = null; + for (Iterator i = antTypeTable.values().iterator(); i.hasNext();) { + AntTypeDefinition def = (AntTypeDefinition) i.next(); + if (elementClass == def.getExposedClass()) { + return "The <" + def.getName() + "> type"; } } + return "Class " + elementClass.getName(); + } + + + /** return true if the two definitions are the same */ + private boolean sameDefinition( + AntTypeDefinition def, AntTypeDefinition old) { + if (! (old.getTypeClass().equals(def.getTypeClass()))) { + return false; + } + if (! (old.getExposedClass().equals(def.getExposedClass()))) { + return false; + } + return true; + } - if (elements != null) { - Enumeration e = elements.keys(); - while (e.hasMoreElements()) { - String name = (String) e.nextElement(); - Class clazz = (Class) elements.get(name); - if (elementClass.equals(clazz)) { - return "The <" + name + "> " + typeName; + /** + * update the component definition table with a new or + * modified definition. + */ + private void updateDataTypeDefinition(AntTypeDefinition def) { + String name = def.getName(); + synchronized (antTypeTable) { + AntTypeDefinition old = antTypeTable.getDefinition(name); + if (old != null) { + if (sameDefinition(def, old)) { + return; + } + Class oldClass = antTypeTable.getExposedClass(name); + if (Task.class.isAssignableFrom(oldClass)) { + int logLevel = Project.MSG_WARN; + if (def.getClassName().equals(old.getClassName()) && + def.getClassLoader() == old.getClassLoader()) { + logLevel = Project.MSG_VERBOSE; + } + project.log( + "Trying to override old definition of task " + + name, logLevel); + invalidateCreatedTasks(name); + } else { + project.log( + "Trying to override old definition of datatype " + + name, Project.MSG_WARN); } } + project.log(" +Datatype " + name + " " + def.getClassName(), + Project.MSG_DEBUG); + antTypeTable.put(name, def); } - - return "Class " + elementClass.getName(); } + /** + * load ant's tasks + */ + private void initTasks() { + ClassLoader classLoader = null; + if (project.getCoreLoader() != null && + ! ("only".equals(project.getProperty("build.sysclasspath")))) { + classLoader = project.getCoreLoader(); + } + String dataDefs = "/org/apache/tools/ant/taskdefs/defaults.properties"; - private static class AntTaskTable extends LazyHashtable { - Project project; - Properties props; - boolean tasks=false; + InputStream in = null; + try { + Properties props = new Properties(); + in = this.getClass().getResourceAsStream(dataDefs); + if (in == null) { + throw new BuildException("Can't load default task list"); + } + props.load(in); - public AntTaskTable( Project p, boolean tasks ) { - this.project=p; - this.tasks=tasks; + Enumeration enum = props.propertyNames(); + while (enum.hasMoreElements()) { + String name = (String) enum.nextElement(); + String className = props.getProperty(name); + AntTypeDefinition def = new AntTypeDefinition(); + def.setProject(project); + def.setName(name); + def.setClassName(className); + def.setClassLoader(classLoader); + def.setAdaptToClass(Task.class); + def.setAdapterClass(TaskAdapter.class); + antTypeTable.put(name, def); + } + } catch (IOException ex) { + throw new BuildException("Can't load default type list"); } + finally { + if (in != null) { + try {in.close();} catch (Exception ignore) {} + } + } + } - public void addDefinitions( Properties props ) { - this.props=props; + /** + * load ant's datatypes + */ + private void initTypes() { + ClassLoader classLoader = null; + if (project.getCoreLoader() != null && + ! ("only".equals(project.getProperty("build.sysclasspath")))) { + classLoader = project.getCoreLoader(); } + String dataDefs = "/org/apache/tools/ant/types/defaults.properties"; - protected void initAll( ) { - if( initAllDone ) { - return; - } - project.log("InitAll", Project.MSG_DEBUG); - if( props==null ) { - return; + InputStream in = null; + try { + Properties props = new Properties(); + in = this.getClass().getResourceAsStream(dataDefs); + if (in == null) { + throw new BuildException("Can't load default datatype list"); } + props.load(in); + Enumeration enum = props.propertyNames(); while (enum.hasMoreElements()) { - String key = (String) enum.nextElement(); - Class taskClass=getTask( key ); - if( taskClass!=null ) { - // This will call a get() and a put() - if( tasks ) { - project.addTaskDefinition(key, taskClass); - } else { - project.addDataTypeDefinition(key, taskClass ); - } - } + String name = (String) enum.nextElement(); + String className = props.getProperty(name); + AntTypeDefinition def = new AntTypeDefinition(); + def.setProject(project); + def.setName(name); + def.setClassName(className); + def.setClassLoader(classLoader); + antTypeTable.put(name, def); } - initAllDone=true; + } catch (IOException ex) { + throw new BuildException("Can't load default type list"); } - - protected Class getTask(String key) { - if( props==null ) { - return null; // for tasks loaded before init() + finally { + if (in != null) { + try {in.close();} catch (Exception ignore) {} } - String value=props.getProperty(key); - if( value==null) { - //project.log( "No class name for " + key, Project.MSG_VERBOSE ); + } + } + + /** + * map that contains the component definitions + */ + private static class AntTypeTable extends Hashtable { + Project project; + + public AntTypeTable(Project project) { + this.project = project; + } + + public AntTypeDefinition getDefinition(String key) { + AntTypeDefinition ret = (AntTypeDefinition) super.get(key); + return ret; + } + + /** Equivalent to getTypeType */ + public Object get(Object key) { + return getTypeClass((String) key); + } + + public Object create(String name) { + AntTypeDefinition def = getDefinition(name); + if (def == null) { return null; } - try { - Class taskClass=null; - if( project.getCoreLoader() != null && - !("only".equals(project.getProperty("build.sysclasspath")))) { - try { - project.log("Loading with the core loader " + value, - Project.MSG_DEBUG); - taskClass=project.getCoreLoader().loadClass(value); - if( taskClass != null ) { - return taskClass; - } - } catch( Exception ex ) { - //ignore - } - } - taskClass = Class.forName(value); - return taskClass; - } catch (NoClassDefFoundError ncdfe) { - project.log("Could not load a dependent class (" - + ncdfe.getMessage() + ") for task " - + key, Project.MSG_DEBUG); - } catch (ClassNotFoundException cnfe) { - project.log("Could not load class (" + value - + ") for task " + key, Project.MSG_DEBUG); + return def.create(); + } + + public Class getTypeClass(String name) { + AntTypeDefinition def = getDefinition(name); + if (def == null) { + return null; } - return null; + return def.getTypeClass(); } - // Hashtable implementation - public Object get( Object key ) { - Object orig=super.get( key ); - if( orig!= null ) { - return orig; - } - if( ! (key instanceof String) ) { + public Class getExposedClass(String name) { + AntTypeDefinition def = getDefinition(name); + if (def == null) { return null; } - project.log("Get task " + key, Project.MSG_DEBUG ); - Object taskClass=getTask( (String) key); - if( taskClass != null) { - super.put( key, taskClass ); - } - return taskClass; + return def.getExposedClass(); } - public boolean containsKey( Object key ) { - return get( key ) != null; + public boolean contains(Object clazz) { + // only used in unit test ProjectTest + // needed ??? + for (Iterator i = values().iterator(); i.hasNext();) { + AntTypeDefinition def = (AntTypeDefinition) i.next(); + Class c = def.getExposedClass(); + if (c == clazz) + return true; + } + return false; } + public boolean containsValue(Object value) { + return contains(value); + } } + } 1.141 +9 -1 ant/src/main/org/apache/tools/ant/Project.java Index: Project.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/Project.java,v retrieving revision 1.140 retrieving revision 1.141 diff -u -r1.140 -r1.141 --- Project.java 10 Jun 2003 13:29:55 -0000 1.140 +++ Project.java 26 Jun 2003 08:54:28 -0000 1.141 @@ -268,6 +268,14 @@ } /** + * inits a sub project - used by taskdefs.Ant + */ + public void initSubProject(Project subProject) { + ComponentHelper.getComponentHelper(subProject) + .initSubProject(ComponentHelper.getComponentHelper(this)); + } + + /** * Initialises the project. * * This involves setting the default task definitions and loading the 1.96 +4 -4 ant/src/main/org/apache/tools/ant/ProjectHelper.java Index: ProjectHelper.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/ProjectHelper.java,v retrieving revision 1.95 retrieving revision 1.96 diff -u -r1.95 -r1.96 --- ProjectHelper.java 14 Apr 2003 14:47:41 -0000 1.95 +++ ProjectHelper.java 26 Jun 2003 08:54:28 -0000 1.96 @@ -324,8 +324,8 @@ */ public static void configure(Object target, AttributeList attrs, Project project) throws BuildException { - if (target instanceof TaskAdapter) { - target = ((TaskAdapter) target).getProxy(); + if (target instanceof TypeAdapter) { + target = ((TypeAdapter) target).getProxy(); } IntrospectionHelper ih = @@ -389,8 +389,8 @@ return; } - if (target instanceof TaskAdapter) { - target = ((TaskAdapter) target).getProxy(); + if (target instanceof TypeAdapter) { + target = ((TypeAdapter) target).getProxy(); } IntrospectionHelper.getHelper(target.getClass()).addText(project, 1.35 +2 -2 ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java Index: RuntimeConfigurable.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java,v retrieving revision 1.34 retrieving revision 1.35 diff -u -r1.34 -r1.35 --- RuntimeConfigurable.java 9 Jun 2003 13:38:06 -0000 1.34 +++ RuntimeConfigurable.java 26 Jun 2003 08:54:28 -0000 1.35 @@ -310,8 +310,8 @@ } // Configure the object - Object target = (wrappedObject instanceof TaskAdapter) ? - ((TaskAdapter) wrappedObject).getProxy() : wrappedObject; + Object target = (wrappedObject instanceof TypeAdapter) ? + ((TypeAdapter) wrappedObject).getProxy() : wrappedObject; //PropertyHelper ph=PropertyHelper.getPropertyHelper(p); IntrospectionHelper ih = 1.21 +9 -1 ant/src/main/org/apache/tools/ant/TaskAdapter.java Index: TaskAdapter.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/TaskAdapter.java,v retrieving revision 1.20 retrieving revision 1.21 diff -u -r1.20 -r1.21 --- TaskAdapter.java 5 Jun 2003 09:35:47 -0000 1.20 +++ TaskAdapter.java 26 Jun 2003 08:54:28 -0000 1.21 @@ -63,7 +63,7 @@ * * @author [EMAIL PROTECTED] */ -public class TaskAdapter extends Task { +public class TaskAdapter extends Task implements TypeAdapter { /** Object to act as a proxy for. */ private Object proxy; @@ -107,6 +107,14 @@ project.log(message, Project.MSG_ERR); throw new BuildException(message); } + } + + /** + * check if the proxy class is a valid class to use + * with this adapter. + */ + public void checkProxyClass(Class proxyClass) { + checkTaskClass(proxyClass, getProject()); } /** 1.51 +2 -2 ant/src/main/org/apache/tools/ant/UnknownElement.java Index: UnknownElement.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/UnknownElement.java,v retrieving revision 1.50 retrieving revision 1.51 diff -u -r1.50 -r1.51 --- UnknownElement.java 9 Jun 2003 13:38:06 -0000 1.50 +++ UnknownElement.java 26 Jun 2003 08:54:28 -0000 1.51 @@ -300,8 +300,8 @@ protected void handleChildren(Object parent, RuntimeConfigurable parentWrapper) throws BuildException { - if (parent instanceof TaskAdapter) { - parent = ((TaskAdapter) parent).getProxy(); + if (parent instanceof TypeAdapter) { + parent = ((TypeAdapter) parent).getProxy(); } Class parentClass = parent.getClass(); 1.1 ant/src/main/org/apache/tools/ant/AntTypeDefinition.java Index: AntTypeDefinition.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 2003 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 "Ant" and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.tools.ant; import java.util.Iterator; import java.util.Locale; import java.util.Map; /** * This class contains all the information * on a particular ant type, * the classname, adaptor and the class * it should be assignable from. * This type replaces the task/datatype split * of pre ant 1.6. * * @author Peter Reilly */ public class AntTypeDefinition { private Project project; private String name; private Class clazz; private Class adapterClass; private Class adaptToClass; private String className; private ClassLoader classLoader; /** * Clone this definiton and changed the cloned definitions' project. * @param p the project the cloned definition lives in * @return the cloned definition */ public AntTypeDefinition copy(Project p) { AntTypeDefinition copy = new AntTypeDefinition(); copy.project = p; copy.name = name; copy.clazz = clazz; copy.adapterClass = adapterClass; copy.className = className; copy.classLoader = classLoader; copy.adaptToClass = adaptToClass; return copy; } /** set the project on the definition */ public void setProject(Project project) { this.project = project; } /** set the definiton's name */ public void setName(String name) { this.name = name; } /** return the definition's name */ public String getName() { return name; } /** * set the class of the definition. * as a side-effect may set the classloader and classname */ public void setClass(Class clazz) { this.clazz = clazz; if (clazz == null) { return; } if (classLoader == null) { this.classLoader = clazz.getClassLoader(); } if (className == null) { this.className = clazz.getName(); } } /** set the classname of the definition */ public void setClassName(String className) { this.className = className; } /** get the classname of the definition */ public String getClassName() { return className; } /** * set the adapter class for this definition. * this class is used to adapt the definitions class if * required. */ public void setAdapterClass(Class adapterClass) { this.adapterClass = adapterClass; } /** * set the assignable class for this definition. */ public void setAdaptToClass(Class adaptToClass) { this.adaptToClass = adaptToClass; } /** * set the classloader to use to create an instance * of the definition */ public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } /** get the classloader for this definition */ public ClassLoader getClassLoader() { return classLoader; } /** * get the exposed class for this * definition. This will be a proxy class * (adapted class) if there is an adpater * class and the definition class is not * assignable from the assignable class. */ public Class getExposedClass() { if (adaptToClass != null) { Class z = getTypeClass(); if (z == null) return null; if (adaptToClass.isAssignableFrom(z)) { return z; } } if (adapterClass != null) { return adapterClass; } return getTypeClass(); } /** * get the definition class */ public Class getTypeClass() { if (clazz != null) { return clazz; } try { if (classLoader == null) { clazz = Class.forName(className); } else { clazz = classLoader.loadClass(className); } } catch (NoClassDefFoundError ncdfe) { project.log("Could not load a dependent class (" + ncdfe.getMessage() + ") for type " + name, Project.MSG_DEBUG); } catch (ClassNotFoundException cnfe) { project.log("Could not load class (" + className + ") for type " + name, Project.MSG_DEBUG); } return clazz; } /** * create an instance of the definition. * The instance may be wrapped in a proxy class. */ public Object create() { Object o = icreate(); return o; } /** * Create a component object based on * its definition */ private Object icreate() { Class c = getTypeClass(); if (c == null) { return null; } Object o = createAndSet(c); if (o == null || adapterClass == null) { return o; } if (adaptToClass != null) { if (adaptToClass.isAssignableFrom(o.getClass())) { return o; } } TypeAdapter adapterObject = (TypeAdapter) createAndSet(adapterClass); if (adapterObject == null) { return null; } adapterObject.setProxy(o); return adapterObject; } /** * check if the attributes are correct * <dl> * <li>if an adapter class can be created</li> * <li>if the type is * * * (Used during creation of the definition). */ public void checkClass() { if (clazz == null) { clazz = getTypeClass(); if (clazz == null) { throw new BuildException( "Unable to create class for " + getName()); } } // check adapter if (adapterClass != null) { boolean needToCheck = true; if (adaptToClass != null && adaptToClass.isAssignableFrom(clazz)) { needToCheck = false; } if (needToCheck) { TypeAdapter adapter = (TypeAdapter) createAndSet(adapterClass); if (adapter == null) { throw new BuildException("Unable to create adapter object"); } adapter.checkProxyClass(clazz); } } } /** * get the constructor of the defintion * and invoke it. */ private Object createAndSet(Class c) { 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[] {project}); } project.setProjectReference(o); return o; } catch (java.lang.reflect.InvocationTargetException ex) { Throwable t = ex.getTargetException(); throw new BuildException( "Could not create type " + name + " due to " + t, t); } catch (NoClassDefFoundError ncdfe) { String msg = "Type " + name + ": A class needed by class " + c + " cannot be found: " + ncdfe.getMessage(); throw new BuildException(msg, ncdfe); } catch (Throwable t) { throw new BuildException( "Could not create type " + name + " due to " + t, t); } } } 1.1 ant/src/main/org/apache/tools/ant/TypeAdapter.java Index: TypeAdapter.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 2003 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 "Ant" and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.tools.ant; import java.lang.reflect.Method; /** * Used to wrap types. * * @author [EMAIL PROTECTED] * @author peter reilly */ public interface TypeAdapter { /** * Sets the project */ public void setProject(Project p); /** * Gets the project */ public Project getProject(); /** * Sets the proxy object, whose methods are going to be * invoked by ant. * A proxy object is normally the object defined by * a <typedef/> task that is adapted by the "adapter" * attribute. * * @param o The target object. Must not be <code>null</code>. */ public void setProxy(Object o); /** * Returns the proxy object. * * @return the target proxy object */ public Object getProxy(); /** * Check if the proxy class matchs the criteria */ public void checkProxyClass(Class proxyClass); } 1.19 +4 -4 ant/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java Index: ProjectHelperImpl.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java,v retrieving revision 1.18 retrieving revision 1.19 diff -u -r1.18 -r1.19 --- ProjectHelperImpl.java 14 Apr 2003 11:58:04 -0000 1.18 +++ ProjectHelperImpl.java 26 Jun 2003 08:54:28 -0000 1.19 @@ -1,7 +1,7 @@ /* * The Apache Software License, Version 1.1 * - * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,7 +68,7 @@ import org.apache.tools.ant.RuntimeConfigurable; import org.apache.tools.ant.Target; import org.apache.tools.ant.Task; -import org.apache.tools.ant.TaskAdapter; +import org.apache.tools.ant.TypeAdapter; import org.apache.tools.ant.TaskContainer; import org.apache.tools.ant.UnknownElement; import org.apache.tools.ant.util.FileUtils; @@ -865,8 +865,8 @@ Target target) { super(helperImpl, parentHandler); - if (parent instanceof TaskAdapter) { - this.parent = ((TaskAdapter) parent).getProxy(); + if (parent instanceof TypeAdapter) { + this.parent = ((TypeAdapter) parent).getProxy(); } else { this.parent = parent; } 1.78 +2 -22 ant/src/main/org/apache/tools/ant/taskdefs/Ant.java Index: Ant.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/Ant.java,v retrieving revision 1.77 retrieving revision 1.78 diff -u -r1.77 -r1.78 --- Ant.java 12 May 2003 12:57:04 -0000 1.77 +++ Ant.java 26 Jun 2003 08:54:29 -0000 1.78 @@ -156,9 +156,6 @@ newProject = new Project(); newProject.setDefaultInputStream(getProject().getDefaultInputStream()); newProject.setJavaVersionProperty(); - newProject.addTaskDefinition("property", - (Class) getProject().getTaskDefinitions() - .get("property")); } /** @@ -238,25 +235,7 @@ } } - Hashtable taskdefs = getProject().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 = getProject().getDataTypeDefinitions(); - Enumeration e = typedefs.keys(); - while (e.hasMoreElements()) { - String typeName = (String) e.nextElement(); - Class typeClass = (Class) typedefs.get(typeName); - newProject.addDataTypeDefinition(typeName, typeClass); - } + getProject().initSubProject(newProject); // set user-defined properties getProject().copyUserProperties(newProject); @@ -271,6 +250,7 @@ addAlmostAll(getProject().getProperties()); } + Enumeration e; e = propertySets.elements(); while (e.hasMoreElements()) { PropertySet ps = (PropertySet) e.nextElement(); 1.31 +241 -88 ant/src/main/org/apache/tools/ant/taskdefs/Definer.java Index: Definer.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/Definer.java,v retrieving revision 1.30 retrieving revision 1.31 diff -u -r1.30 -r1.31 --- Definer.java 10 Jun 2003 14:05:58 -0000 1.30 +++ Definer.java 26 Jun 2003 08:54:29 -0000 1.31 @@ -58,15 +58,23 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URL; import java.util.Enumeration; +import java.util.Locale; import java.util.Properties; + +import org.apache.tools.ant.AntTypeDefinition; import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.ComponentHelper; import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Location; import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectHelper; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Reference; import org.apache.tools.ant.util.ClasspathUtils; +import org.apache.tools.ant.types.EnumeratedAttribute; /** * Base class for Taskdef and Typedef - does all the classpath @@ -79,10 +87,61 @@ */ public abstract class Definer extends Task { private String name; - private String value; + private String classname; private File file; private String resource; private ClasspathUtils.Delegate cpDelegate; + + private int format = Format.PROPERTIES; + private boolean definerSet = false; + private ClassLoader internalClassLoader; + private int onError = OnError.FAIL; + private String adapter; + private String adaptTo; + + private Class adapterClass; + private Class adaptToClass; + + public static class OnError extends EnumeratedAttribute { + public static final int FAIL = 0, REPORT = 1, IGNORE = 2; + public OnError() { + super(); + } + public OnError(String value) { + setValue(value); + } + public String[] getValues() { + return new String[] {"fail", "report", "ignore"}; + } + } + + public static class Format extends EnumeratedAttribute { + public static final int PROPERTIES=0, XML=1; + public String[] getValues() { + return new String[] {"properties", "xml"}; + } + } + + /** + * What to do if there is an error in loading the class. + * <dl> + * <li>error - throw build exception</li> + * <li>report - output at warning level</li> + * <li>ignore - output at debug level</li> + * </dl> + * + * @param onError an <code>OnError</code> value + */ + public void setOnError(OnError onError) { + this.onError = onError.getIndex(); + } + + /** + * Sets the format of the file or resource + */ + public void setFormat(Format format) { + this.format = format.getIndex(); + } /** * @deprecated stop using this attribute @@ -165,101 +224,107 @@ public void execute() throws BuildException { ClassLoader al = createLoader(); - if (file == null && resource == null) { - - // simple case - one definition - if (name == null || value == null) { - String msg = "name or classname attributes of " - + getTaskName() + " element " - + "are undefined"; - throw new BuildException(msg); + if (!definerSet) { + throw new BuildException( + "name, file or resource attribute of " + + getTaskName() + " is undefined", getLocation()); + } + + if (name != null) { + if (classname == null) { + throw new BuildException( + "classname attribute of " + getTaskName() + " element " + + "is undefined", getLocation()); } - addDefinition(al, name, value); - + addDefinition(al, name, classname); } else { + if (classname != null) { + String msg = "You must not specify classname " + + "together with file or resource."; + throw new BuildException(msg, getLocation()); + } + URL url = null; + if (file != null) { + url = fileToURL(); + } + if (resource != null) { + url = resourceToURL(al); + } - InputStream is = null; - try { - if (name != null || value != null) { - String msg = "You must not specify name or value " - + "together with file or resource."; - throw new BuildException(msg, getLocation()); - } - - if (file != null && resource != null) { - String msg = "You must not specify both, file and " - + "resource."; - throw new BuildException(msg, getLocation()); - } + if (url == null) { + return; + } + loadProperties(al, url); + } + } - Properties props = new Properties(); - if (file != null) { - log("Loading definitions from file " + file, - Project.MSG_VERBOSE); - is = new FileInputStream(file); - if (is == null) { - log("Could not load definitions from file " + file - + ". It doesn\'t exist.", Project.MSG_WARN); - } - } - if (resource != null) { - log("Loading definitions from resource " + resource, - Project.MSG_VERBOSE); - is = al.getResourceAsStream(resource); - if (is == null) { - log("Could not load definitions from resource " - + resource + ". It could not be found.", - Project.MSG_WARN); - } - } + private URL fileToURL() { + if (! file.exists()) { + log("File " + file + " does not exist", Project.MSG_WARN); + return null; + } + if (! file.isFile()) { + log("File " + file + " is not a file", Project.MSG_WARN); + return null; + } + try { + return file.toURL(); + } catch (Exception ex) { + log("File " + file + " cannot use as URL: " + + ex.toString(), Project.MSG_WARN); + return null; + } + } - if (is != null) { - props.load(is); - Enumeration keys = props.keys(); - while (keys.hasMoreElements()) { - String n = (String) keys.nextElement(); - String v = props.getProperty(n); - addDefinition(al, n, v); - } - } - } catch (IOException ex) { - throw new BuildException(ex, getLocation()); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) {} - } + private URL resourceToURL(ClassLoader classLoader) { + URL ret = classLoader.getResource(resource); + if (ret == null) { + if (onError != OnError.IGNORE) { + log("Could not load definitions from resource " + + resource + ". It could not be found.", + Project.MSG_WARN); } } + return ret; } - - /** - * create the classloader then hand the definition off to the subclass; - * @throws BuildException when the class wont load for any reason - */ - private void addDefinition(ClassLoader al, String name, String value) - throws BuildException { + + protected void loadProperties(ClassLoader al, URL url) { + InputStream is = null; try { - Class c = al.loadClass(value); - AntClassLoader.initializeClass(c); - addDefinition(name, c); - } catch (ClassNotFoundException cnfe) { - String msg = getTaskName() + " class " + value - + " cannot be found"; - throw new BuildException(msg, cnfe, getLocation()); - } catch (NoClassDefFoundError ncdfe) { - String msg = getTaskName() + ": A class needed by class " - + value + " cannot be found: " + ncdfe.getMessage(); - throw new BuildException(msg, ncdfe, location); + is = url.openStream(); + if (is == null) { + log("Could not load definitions from " + url, + Project.MSG_WARN); + return; + } + Properties props = new Properties(); + props.load(is); + Enumeration keys = props.keys(); + while (keys.hasMoreElements()) { + name = ((String) keys.nextElement()); + classname = props.getProperty(name); + addDefinition(al, name, classname); + } + } catch (IOException ex) { + throw new BuildException(ex, getLocation()); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) {} + } } } + /** * create a classloader for this definition */ - private ClassLoader createLoader() { + protected ClassLoader createLoader() { + if (internalClassLoader != null) { + return internalClassLoader; + } ClassLoader al = this.cpDelegate.getClassLoader(); // need to load Task via system classloader or the new // task we want to define will never be a Task but always @@ -274,6 +339,10 @@ * ant name/classname pairs from. */ public void setFile(File file) { + if (definerSet) { + tooManyDefinitions(); + } + definerSet = true; this.file = file; } @@ -282,6 +351,10 @@ * ant name/classname pairs from. */ public void setResource(String res) { + if (definerSet) { + tooManyDefinitions(); + } + definerSet = true; this.resource = res; } @@ -290,6 +363,10 @@ * ant name/classname pairs from. */ public void setName(String name) { + if (definerSet) { + tooManyDefinitions(); + } + definerSet = true; this.name = name; } @@ -298,7 +375,7 @@ * May be <code>null</code>. */ public String getClassname() { - return value; + return classname; } /** @@ -307,16 +384,29 @@ * been specified. */ public void setClassname(String v) { - value = v; + classname = v; } - /** - * This must be implemented by subclasses; it is the callback - * they will get to add a new definition of their type. - */ - protected abstract void addDefinition(String name, Class c); - + public void setAdapter(String adapter) { + this.adapter = adapter; + } + + protected void setAdapterClass(Class adapterClass) { + this.adapterClass = adapterClass; + } + public void setAdaptTo(String adaptTo) { + this.adaptTo = adaptTo; + } + + protected void setAdaptToClass(Class adaptToClass) { + this.adaptToClass = adaptToClass; + } + + protected void setInternalClassLoader(ClassLoader classLoader) { + this.internalClassLoader = classLoader; + } + /** * @see org.apache.tools.ant.Task#init() * @since Ant 1.6 @@ -326,4 +416,67 @@ super.init(); } + protected void addDefinition(ClassLoader al, String name, String classname) + throws BuildException + { + Class cl = null; + try { + try { + if (onError != OnError.IGNORE) { + cl = al.loadClass(classname); + AntClassLoader.initializeClass(cl); + } + + if (adapter != null) { + adapterClass = al.loadClass(adapter); + AntClassLoader.initializeClass(adapterClass); + } + + if (adaptTo != null) { + adaptToClass = al.loadClass(adaptTo); + AntClassLoader.initializeClass(adaptToClass); + } + + AntTypeDefinition def = new AntTypeDefinition(); + def.setName(name); + def.setProject(getProject()); + def.setClassName(classname); + def.setClass(cl); + def.setAdapterClass(adapterClass); + def.setAdaptToClass(adaptToClass); + def.setClassLoader(al); + if (cl != null) { + def.checkClass(); + } + ComponentHelper.getComponentHelper(getProject()) + .addDataTypeDefinition(def); + } catch (ClassNotFoundException cnfe) { + String msg = getTaskName() + " class " + classname + + " cannot be found"; + throw new BuildException(msg, cnfe, getLocation()); + } catch (NoClassDefFoundError ncdfe) { + String msg = getTaskName() + "A class needed by class " + + classname + " cannot be found: " + ncdfe.getMessage(); + throw new BuildException(msg, ncdfe, location); + } + } catch (BuildException ex) { + switch (onError) { + case OnError.FAIL: + throw ex; + case OnError.REPORT: + log(ex.getLocation() + "Warning: " + ex.getMessage(), + Project.MSG_WARN); + break; + default: + log(ex.getLocation() + ex.getMessage(), + Project.MSG_DEBUG); + } + } + } + + private void tooManyDefinitions() { + throw new BuildException( + "Only one of the attributes name,file,resource" + + " can be set", getLocation()); + } } 1.27 +8 -9 ant/src/main/org/apache/tools/ant/taskdefs/Taskdef.java Index: Taskdef.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/Taskdef.java,v retrieving revision 1.26 retrieving revision 1.27 diff -u -r1.26 -r1.27 --- Taskdef.java 7 Mar 2003 11:23:02 -0000 1.26 +++ Taskdef.java 26 Jun 2003 08:54:29 -0000 1.27 @@ -1,7 +1,7 @@ /* * The Apache Software License, Version 1.1 * - * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,6 +55,8 @@ package org.apache.tools.ant.taskdefs; import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.TaskAdapter; /** * Adds a task definition to the current project, such that this new task can be @@ -72,13 +74,10 @@ * @since Ant 1.1 * @ant.task category="internal" */ -public class Taskdef extends Definer { - - /** - * subclassed handler for definitions; called by parent during - * execution. - */ - protected void addDefinition(String name, Class c) throws BuildException { - getProject().addTaskDefinition(name, c); +public class Taskdef extends Typedef { + + public Taskdef() { + setAdapterClass(TaskAdapter.class); + setAdaptToClass(Task.class); } } 1.17 +6 -5 ant/src/testcases/org/apache/tools/ant/ProjectTest.java Index: ProjectTest.java =================================================================== RCS file: /home/cvs/ant/src/testcases/org/apache/tools/ant/ProjectTest.java,v retrieving revision 1.16 retrieving revision 1.17 diff -u -r1.16 -r1.17 --- ProjectTest.java 7 Mar 2003 11:23:10 -0000 1.16 +++ ProjectTest.java 26 Jun 2003 08:54:29 -0000 1.17 @@ -183,9 +183,9 @@ p.addBuildListener(mbl); p.addTaskDefinition("Ok", DummyTaskOk.class); - assertEquals(DummyTaskOk.class, p.getTaskDefinitions().get("Ok")); + assertEquals(DummyTaskOk.class, p.getDataTypeDefinitions().get("Ok")); p.addTaskDefinition("OkNonTask", DummyTaskOkNonTask.class); - assertEquals(DummyTaskOkNonTask.class, p.getTaskDefinitions().get("OkNonTask")); + assertEquals(DummyTaskOkNonTask.class, p.getDataTypeDefinitions().get("OkNonTask")); mbl.assertEmpty(); assertTaskDefFails(DummyTaskPrivate.class, DummyTaskPrivate.class + " is not public"); @@ -220,7 +220,7 @@ mbl.addBuildEvent("return type of execute() should be void but was \"int\" in " + DummyTaskWithNonVoidExecute.class, Project.MSG_WARN); p.addTaskDefinition("NonVoidExecute", DummyTaskWithNonVoidExecute.class); mbl.assertEmpty(); - assertEquals(DummyTaskWithNonVoidExecute.class, p.getTaskDefinitions().get("NonVoidExecute")); + assertEquals(DummyTaskWithNonVoidExecute.class, p.getDataTypeDefinitions().get("NonVoidExecute")); } public void testInputHandler() { @@ -233,11 +233,12 @@ } public void testTaskDefinitionContainsKey() { - assertTrue(p.getTaskDefinitions().containsKey("echo")); + assertTrue(p.getDataTypeDefinitions().containsKey("echo")); } public void testTaskDefinitionContains() { - assertTrue(p.getTaskDefinitions().contains(org.apache.tools.ant.taskdefs.Echo.class)); + assertTrue(p.getDataTypeDefinitions() + .contains(org.apache.tools.ant.taskdefs.Echo.class)); } private class DummyTaskPrivate extends Task { 1.1 ant/src/testcases/org/apache/tools/ant/taskdefs/TypeAdapterTest.java Index: TypeAdapterTest.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 2003 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 "Ant" and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.tools.ant.taskdefs; import java.lang.reflect.Method; import org.apache.tools.ant.BuildFileTest; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.TypeAdapter; /** * @author Peter Reilly */ public class TypeAdapterTest extends BuildFileTest { public TypeAdapterTest(String name) { super(name); } public void setUp() { configureProject("src/etc/testcases/taskdefs/typeadapter.xml"); } public void testTaskAdapter() { expectLogContaining("taskadapter", "MyExec called"); } public void testRunAdapter() { expectLogContaining("runadapter", "MyRunnable called"); } public void testRunAdapterError() { expectBuildExceptionContaining( "runadaptererror", "xx", "No public run() method in"); } public void testDelay() { expectLogContaining("delay", "MyTask called"); } public void testOnErrorReport() { expectLogContaining("onerror.report", "MyTaskNotPresent cannot be found"); } public void testOnErrorIgnore() { expectLog("onerror.ignore",""); } public static class MyTask extends Task { public void execute() { log("MyTask called"); } } public static class MyExec { private Project project; public void setProject(Project project) { this.project = project; } public void execute() { project.log("MyExec called"); } } public static class MyRunnable { private Project project; public void setProject(Project project) { this.project = project; } public void run() { project.log("MyRunnable called"); } } public static class RunnableAdapter extends Task implements TypeAdapter { private String execMethodName = "run"; private Object proxy; public Method getExecuteMethod(Class proxyClass) { try { Method execMethod = proxyClass.getMethod( execMethodName, null); if (!Void.TYPE.equals(execMethod.getReturnType())) { String message = "return type of " + execMethodName + "() should be " + "void but was \"" + execMethod.getReturnType() + "\" in " + proxyClass; log(message, Project.MSG_WARN); } return execMethod; } catch (NoSuchMethodException e) { String message = "No public "+ execMethodName + "() method in " + proxyClass; log(message, Project.MSG_ERR); throw new BuildException(message); } } public void checkProxyClass(Class proxyClass) { getExecuteMethod(proxyClass); } public void setProxy(Object o) { getExecuteMethod(o.getClass()); this.proxy = o; } public Object getProxy() { return proxy; } public void execute() { getProject().setProjectReference(proxy); Method executeMethod = getExecuteMethod(proxy.getClass()); try { executeMethod.invoke(proxy, null); } catch (java.lang.reflect.InvocationTargetException ie) { log("Error in " + proxy.getClass(), Project.MSG_ERR); Throwable t = ie.getTargetException(); if (t instanceof BuildException) { throw ((BuildException) t); } else { throw new BuildException(t); } } catch (Exception ex) { log("Error in " + proxy.getClass(), Project.MSG_ERR); throw new BuildException(ex); } } } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]