Browse Source

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
Reviewed by:


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@274718 13f79535-47bb-0310-9956-ffa450edef68
master
Peter Reilly 22 years ago
parent
commit
8701272b24
17 changed files with 1313 additions and 530 deletions
  1. +7
    -71
      docs/manual/CoreTasks/taskdef.html
  2. +43
    -10
      docs/manual/CoreTasks/typedef.html
  3. +63
    -0
      src/etc/testcases/taskdefs/typeadapter.xml
  4. +331
    -0
      src/main/org/apache/tools/ant/AntTypeDefinition.java
  5. +292
    -312
      src/main/org/apache/tools/ant/ComponentHelper.java
  6. +8
    -0
      src/main/org/apache/tools/ant/Project.java
  7. +4
    -4
      src/main/org/apache/tools/ant/ProjectHelper.java
  8. +2
    -2
      src/main/org/apache/tools/ant/RuntimeConfigurable.java
  9. +9
    -1
      src/main/org/apache/tools/ant/TaskAdapter.java
  10. +99
    -0
      src/main/org/apache/tools/ant/TypeAdapter.java
  11. +2
    -2
      src/main/org/apache/tools/ant/UnknownElement.java
  12. +4
    -4
      src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java
  13. +2
    -22
      src/main/org/apache/tools/ant/taskdefs/Ant.java
  14. +241
    -88
      src/main/org/apache/tools/ant/taskdefs/Definer.java
  15. +8
    -9
      src/main/org/apache/tools/ant/taskdefs/Taskdef.java
  16. +6
    -5
      src/testcases/org/apache/tools/ant/ProjectTest.java
  17. +192
    -0
      src/testcases/org/apache/tools/ant/taskdefs/TypeAdapterTest.java

+ 7
- 71
docs/manual/CoreTasks/taskdef.html View File

@@ -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 &quot;<a
href="../develop.html#writingowntask">Writing your own task</a>&quot;.</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> &lt;taskdef name=&quot;myjavadoc&quot; classname=&quot;com.mydomain.JavadocTask&quot;/&gt;</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 &copy; 2000-2002 Apache Software Foundation. All rights
<p align="center">Copyright &copy; 2000-2003 Apache Software Foundation. All rights
Reserved.</p>

</body>


+ 43
- 10
docs/manual/CoreTasks/typedef.html View File

@@ -21,7 +21,7 @@ format:</p>
<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 @@ the project level and referenced via their ID attribute.</p>
<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 @@ the project level and referenced via their ID attribute.</p>
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 @@ via a nested <i>classpath</i> element.</p>
<pre> &lt;typedef name=&quot;urlset&quot; classname=&quot;com.mydomain.URLSet&quot;/&gt;</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 &copy; 2001-2002 Apache Software
<p align="center">Copyright &copy; 2001-2003 Apache Software
Foundation. All rights Reserved.</p>

</body>


+ 63
- 0
src/etc/testcases/taskdefs/typeadapter.xml View File

@@ -0,0 +1,63 @@
<?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>


+ 331
- 0
src/main/org/apache/tools/ant/AntTypeDefinition.java View File

@@ -0,0 +1,331 @@
/*
* 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 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.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);
}
}
}


+ 292
- 312
src/main/org/apache/tools/ant/ComponentHelper.java View File

@@ -59,7 +59,15 @@ import org.apache.tools.ant.util.WeakishReference;

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 @@ import java.lang.reflect.Modifier;
/**
* 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 class ComponentHelper {

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 @@ public class ComponentHelper {
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.

// 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;
Object component = createComponent(taskName);
if (component == null) {
return null;
}

// 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 @@ public class ComponentHelper {
* @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 @@ public class ComponentHelper {
*
* @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 @@ public class ComponentHelper {
/**
* 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 class ComponentHelper {
public Hashtable getTaskDefinitions() {
return taskClassDefinitions;
}
/**
* Adds a new datatype definition.
* Attempting to override an existing definition with an
@@ -362,26 +324,25 @@ public class ComponentHelper {
* 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 @@ public class ComponentHelper {
* (String to Class).
*/
public Hashtable getDataTypeDefinitions() {
return dataClassDefinitions;
return antTypeTable;
}

/**
@@ -432,49 +393,26 @@ public class ComponentHelper {
* 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;
}

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

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);
if (! Task.class.isAssignableFrom(c)) {
return null;
}
Task task = (Task) antTypeTable.create(taskType);
if (task == null) {
return null;
}
task.setTaskType(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;
}

/**
@@ -538,52 +476,11 @@ public class ComponentHelper {
* 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 @@ public class ComponentHelper {
* @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);
}
}

}

+ 8
- 0
src/main/org/apache/tools/ant/Project.java View File

@@ -267,6 +267,14 @@ public class Project {
inputHandler = new DefaultInputHandler();
}

/**
* inits a sub project - used by taskdefs.Ant
*/
public void initSubProject(Project subProject) {
ComponentHelper.getComponentHelper(subProject)
.initSubProject(ComponentHelper.getComponentHelper(this));
}

/**
* Initialises the project.
*


+ 4
- 4
src/main/org/apache/tools/ant/ProjectHelper.java View File

@@ -324,8 +324,8 @@ public class ProjectHelper {
*/
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 @@ public class ProjectHelper {
return;
}

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

IntrospectionHelper.getHelper(target.getClass()).addText(project,


+ 2
- 2
src/main/org/apache/tools/ant/RuntimeConfigurable.java View File

@@ -310,8 +310,8 @@ public class RuntimeConfigurable implements Serializable {
}

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


+ 9
- 1
src/main/org/apache/tools/ant/TaskAdapter.java View File

@@ -63,7 +63,7 @@ import java.lang.reflect.Method;
*
* @author costin@dnt.ro
*/
public class TaskAdapter extends Task {
public class TaskAdapter extends Task implements TypeAdapter {

/** Object to act as a proxy for. */
private Object proxy;
@@ -108,6 +108,14 @@ public class TaskAdapter extends Task {
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());
}
/**
* Executes the proxied task.


+ 99
- 0
src/main/org/apache/tools/ant/TypeAdapter.java View File

@@ -0,0 +1,99 @@
/*
* 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 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.lang.reflect.Method;

/**
* Used to wrap types.
*
* @author costin@dnt.ro
* @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 &lttypedef/&gt 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);
}

+ 2
- 2
src/main/org/apache/tools/ant/UnknownElement.java View File

@@ -300,8 +300,8 @@ public class UnknownElement extends Task {
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();


+ 4
- 4
src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java View File

@@ -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.ProjectHelper;
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 @@ public class ProjectHelperImpl extends ProjectHelper {
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;
}


+ 2
- 22
src/main/org/apache/tools/ant/taskdefs/Ant.java View File

@@ -156,9 +156,6 @@ public class Ant extends Task {
newProject = new Project();
newProject.setDefaultInputStream(getProject().getDefaultInputStream());
newProject.setJavaVersionProperty();
newProject.addTaskDefinition("property",
(Class) getProject().getTaskDefinitions()
.get("property"));
}

/**
@@ -238,25 +235,7 @@ public class Ant extends Task {
}
}

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 @@ public class Ant extends Task {
addAlmostAll(getProject().getProperties());
}

Enumeration e;
e = propertySets.elements();
while (e.hasMoreElements()) {
PropertySet ps = (PropertySet) e.nextElement();


+ 241
- 88
src/main/org/apache/tools/ant/taskdefs/Definer.java View File

@@ -58,15 +58,23 @@ import java.io.File;
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 @@ import org.apache.tools.ant.util.ClasspathUtils;
*/
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 abstract class Definer extends Task {
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 @@ public abstract class Definer extends Task {
* ant name/classname pairs from.
*/
public void setFile(File file) {
if (definerSet) {
tooManyDefinitions();
}
definerSet = true;
this.file = file;
}

@@ -282,6 +351,10 @@ public abstract class Definer extends Task {
* ant name/classname pairs from.
*/
public void setResource(String res) {
if (definerSet) {
tooManyDefinitions();
}
definerSet = true;
this.resource = res;
}

@@ -290,6 +363,10 @@ public abstract class Definer extends Task {
* ant name/classname pairs from.
*/
public void setName(String name) {
if (definerSet) {
tooManyDefinitions();
}
definerSet = true;
this.name = name;
}

@@ -298,7 +375,7 @@ public abstract class Definer extends Task {
* May be <code>null</code>.
*/
public String getClassname() {
return value;
return classname;
}

/**
@@ -307,16 +384,29 @@ public abstract class Definer extends Task {
* 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 @@ public abstract class Definer extends Task {
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());
}
}

+ 8
- 9
src/main/org/apache/tools/ant/taskdefs/Taskdef.java View File

@@ -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 @@ import org.apache.tools.ant.BuildException;
* @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);
}
}

+ 6
- 5
src/testcases/org/apache/tools/ant/ProjectTest.java View File

@@ -183,9 +183,9 @@ public class ProjectTest extends TestCase {
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 @@ public class ProjectTest extends TestCase {
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 class ProjectTest extends TestCase {
}

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 {


+ 192
- 0
src/testcases/org/apache/tools/ant/taskdefs/TypeAdapterTest.java View File

@@ -0,0 +1,192 @@
/*
* 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 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.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);
}
}
}
}

Loading…
Cancel
Save