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-ffa450edef68master
@@ -9,82 +9,18 @@ | |||||
<h2><a name="taskdef">Taskdef</a></h2> | <h2><a name="taskdef">Taskdef</a></h2> | ||||
<h3>Description</h3> | <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> | <h3>Examples</h3> | ||||
<pre> <taskdef name="myjavadoc" classname="com.mydomain.JavadocTask"/></pre> | <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> | <p>makes a task called <code>myjavadoc</code> available to Ant. The class <code>com.mydomain.JavadocTask</code> | ||||
implements the task.</p> | implements the task.</p> | ||||
<hr> | <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> | Reserved.</p> | ||||
</body> | </body> | ||||
@@ -21,7 +21,7 @@ format:</p> | |||||
<pre> | <pre> | ||||
typename=fully.qualified.java.classname | typename=fully.qualified.java.classname | ||||
</pre> | </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 | types are things like <a href="../using.html#path">paths</a> or <a | ||||
href="../CoreTypes/fileset.html">filesets</a> that can be defined at | href="../CoreTypes/fileset.html">filesets</a> that can be defined at | ||||
the project level and referenced via their ID attribute.</p> | 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> | <tr> | ||||
<td valign="top">name</td> | <td valign="top">name</td> | ||||
<td valign="top">the name of the data type</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> | ||||
<tr> | <tr> | ||||
<td valign="top">classname</td> | <td valign="top">classname</td> | ||||
<td valign="top">the full class name implementing the data type</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> | ||||
<tr> | <tr> | ||||
<td valign="top">file</td> | <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> | <td valign="top" align="center">No</td> | ||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">resource</td> | <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> | <td valign="top" align="center">No</td> | ||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
@@ -69,6 +67,40 @@ the project level and referenced via their ID attribute.</p> | |||||
each other. ( introduced in ant1.5 )</td> | each other. ( introduced in ant1.5 )</td> | ||||
<td align="center" valign="top">No</td> | <td align="center" valign="top">No</td> | ||||
</tr> | </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> | </table> | ||||
<h3>Parameters specified as nested elements</h3> | <h3>Parameters specified as nested elements</h3> | ||||
<h4>classpath</h4> | <h4>classpath</h4> | ||||
@@ -79,8 +111,9 @@ via a nested <i>classpath</i> element.</p> | |||||
<pre> <typedef name="urlset" classname="com.mydomain.URLSet"/></pre> | <pre> <typedef name="urlset" classname="com.mydomain.URLSet"/></pre> | ||||
<p>makes a data type called <code>urlset</code> available to Ant. The | <p>makes a data type called <code>urlset</code> available to Ant. The | ||||
class <code>com.mydomain.URLSet</code> implements this type.</p> | class <code>com.mydomain.URLSet</code> implements this type.</p> | ||||
<hr> | <hr> | ||||
<p align="center">Copyright © 2001-2002 Apache Software | |||||
<p align="center">Copyright © 2001-2003 Apache Software | |||||
Foundation. All rights Reserved.</p> | Foundation. All rights Reserved.</p> | ||||
</body> | </body> | ||||
@@ -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> | |||||
@@ -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); | |||||
} | |||||
} | |||||
} | |||||
@@ -59,7 +59,15 @@ import org.apache.tools.ant.util.WeakishReference; | |||||
import java.util.Enumeration; | import java.util.Enumeration; | ||||
import java.util.Hashtable; | import java.util.Hashtable; | ||||
import java.util.HashSet; | |||||
import java.util.Iterator; | |||||
import java.util.Properties; | 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.util.Vector; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
@@ -68,21 +76,28 @@ import java.lang.reflect.Modifier; | |||||
/** | /** | ||||
* Component creation and configuration. | * 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 | * A very simple hook mechnism is provided that allows users to plug | ||||
* in custom code. It is also possible to replace the default behavior | * in custom code. It is also possible to replace the default behavior | ||||
* ( for example in an app embeding ant ) | * ( for example in an app embeding ant ) | ||||
* | * | ||||
* @author Costin Manolache | * @author Costin Manolache | ||||
* @author Peter Reilly | |||||
* @since Ant1.6 | * @since Ant1.6 | ||||
*/ | */ | ||||
public class ComponentHelper { | 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 | * Map from task names to vectors of created tasks | ||||
* (String to Vector of Task). This is used to invalidate tasks if | * (String to Vector of Task). This is used to invalidate tasks if | ||||
@@ -120,10 +135,24 @@ public class ComponentHelper { | |||||
public void setProject(Project project) { | public void setProject(Project project) { | ||||
this.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. | /** Factory method to create the components. | ||||
* | * | ||||
* This should be called by UnknownElement. | * This should be called by UnknownElement. | ||||
@@ -139,63 +168,54 @@ public class ComponentHelper { | |||||
String taskName ) | String taskName ) | ||||
throws BuildException | 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; | 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) { | 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 | /** Initialization code - implementing the original ant component | ||||
* loading from /org/apache/tools/ant/taskdefs/default.properties | * loading from /org/apache/tools/ant/taskdefs/default.properties | ||||
* and .../types/default.properties | * and .../types/default.properties | ||||
@@ -203,40 +223,8 @@ public class ComponentHelper { | |||||
* @throws BuildException | * @throws BuildException | ||||
*/ | */ | ||||
public void initDefaultDefinitions() 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) | * @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); | 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 | * Returns the current task definition hashtable. The returned hashtable is | ||||
* "live" and so should not be modified. | * "live" and so should not be modified. | ||||
* This table does not contain any information | |||||
* | * | ||||
* @return a map of from task name to implementing class | * @return a map of from task name to implementing class | ||||
* (String to Class). | * (String to Class). | ||||
@@ -347,7 +309,7 @@ public class ComponentHelper { | |||||
public Hashtable getTaskDefinitions() { | public Hashtable getTaskDefinitions() { | ||||
return taskClassDefinitions; | return taskClassDefinitions; | ||||
} | } | ||||
/** | /** | ||||
* Adds a new datatype definition. | * Adds a new datatype definition. | ||||
* Attempting to override an existing definition with an | * Attempting to override an existing definition with an | ||||
@@ -362,26 +324,25 @@ public class ComponentHelper { | |||||
* Must not be <code>null</code>. | * Must not be <code>null</code>. | ||||
*/ | */ | ||||
public void addDataTypeDefinition(String typeName, Class typeClass) { | 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 + " " | String msg = " +User datatype: " + typeName + " " | ||||
+ typeClass.getName(); | + typeClass.getName(); | ||||
project.log(msg, Project.MSG_DEBUG); | 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 | * Returns the current datatype definition hashtable. The returned | ||||
* hashtable is "live" and so should not be modified. | * hashtable is "live" and so should not be modified. | ||||
@@ -390,7 +351,7 @@ public class ComponentHelper { | |||||
* (String to Class). | * (String to Class). | ||||
*/ | */ | ||||
public Hashtable getDataTypeDefinitions() { | public Hashtable getDataTypeDefinitions() { | ||||
return dataClassDefinitions; | |||||
return antTypeTable; | |||||
} | } | ||||
/** | /** | ||||
@@ -432,49 +393,26 @@ public class ComponentHelper { | |||||
* creation fails. | * creation fails. | ||||
*/ | */ | ||||
private Task createNewTask(String taskType) throws BuildException { | private Task createNewTask(String taskType) throws BuildException { | ||||
Class c = (Class) taskClassDefinitions.get(taskType); | |||||
Class c = antTypeTable.getExposedClass(taskType); | |||||
if (c == null) { | if (c == null) { | ||||
return 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. | * instance creation fails. | ||||
*/ | */ | ||||
public Object createDataType(String typeName) throws BuildException { | 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> | * <p> | ||||
* This is useful for logging purposes. | * This is useful for logging purposes. | ||||
* | * | ||||
@@ -595,127 +492,210 @@ public class ComponentHelper { | |||||
* @since Ant 1.6 | * @since Ant 1.6 | ||||
*/ | */ | ||||
public String getElementName(Object element) { | 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(); | 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(); | Enumeration enum = props.propertyNames(); | ||||
while (enum.hasMoreElements()) { | 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; | 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; | 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); | |||||
} | |||||
} | } | ||||
} | } |
@@ -267,6 +267,14 @@ public class Project { | |||||
inputHandler = new DefaultInputHandler(); | 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. | * Initialises the project. | ||||
* | * | ||||
@@ -324,8 +324,8 @@ public class ProjectHelper { | |||||
*/ | */ | ||||
public static void configure(Object target, AttributeList attrs, | public static void configure(Object target, AttributeList attrs, | ||||
Project project) throws BuildException { | Project project) throws BuildException { | ||||
if (target instanceof TaskAdapter) { | |||||
target = ((TaskAdapter) target).getProxy(); | |||||
if (target instanceof TypeAdapter) { | |||||
target = ((TypeAdapter) target).getProxy(); | |||||
} | } | ||||
IntrospectionHelper ih = | IntrospectionHelper ih = | ||||
@@ -389,8 +389,8 @@ public class ProjectHelper { | |||||
return; | return; | ||||
} | } | ||||
if (target instanceof TaskAdapter) { | |||||
target = ((TaskAdapter) target).getProxy(); | |||||
if (target instanceof TypeAdapter) { | |||||
target = ((TypeAdapter) target).getProxy(); | |||||
} | } | ||||
IntrospectionHelper.getHelper(target.getClass()).addText(project, | IntrospectionHelper.getHelper(target.getClass()).addText(project, | ||||
@@ -310,8 +310,8 @@ public class RuntimeConfigurable implements Serializable { | |||||
} | } | ||||
// Configure the object | // 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); | //PropertyHelper ph=PropertyHelper.getPropertyHelper(p); | ||||
IntrospectionHelper ih = | IntrospectionHelper ih = | ||||
@@ -63,7 +63,7 @@ import java.lang.reflect.Method; | |||||
* | * | ||||
* @author costin@dnt.ro | * @author costin@dnt.ro | ||||
*/ | */ | ||||
public class TaskAdapter extends Task { | |||||
public class TaskAdapter extends Task implements TypeAdapter { | |||||
/** Object to act as a proxy for. */ | /** Object to act as a proxy for. */ | ||||
private Object proxy; | private Object proxy; | ||||
@@ -108,6 +108,14 @@ public class TaskAdapter extends Task { | |||||
throw new BuildException(message); | 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. | * Executes the proxied task. | ||||
@@ -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 <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); | |||||
} |
@@ -300,8 +300,8 @@ public class UnknownElement extends Task { | |||||
protected void handleChildren(Object parent, | protected void handleChildren(Object parent, | ||||
RuntimeConfigurable parentWrapper) | RuntimeConfigurable parentWrapper) | ||||
throws BuildException { | throws BuildException { | ||||
if (parent instanceof TaskAdapter) { | |||||
parent = ((TaskAdapter) parent).getProxy(); | |||||
if (parent instanceof TypeAdapter) { | |||||
parent = ((TypeAdapter) parent).getProxy(); | |||||
} | } | ||||
Class parentClass = parent.getClass(); | Class parentClass = parent.getClass(); | ||||
@@ -1,7 +1,7 @@ | |||||
/* | /* | ||||
* The Apache Software License, Version 1.1 | * 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. | * reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * 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.RuntimeConfigurable; | ||||
import org.apache.tools.ant.Target; | import org.apache.tools.ant.Target; | ||||
import org.apache.tools.ant.Task; | 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.TaskContainer; | ||||
import org.apache.tools.ant.UnknownElement; | import org.apache.tools.ant.UnknownElement; | ||||
import org.apache.tools.ant.util.FileUtils; | import org.apache.tools.ant.util.FileUtils; | ||||
@@ -865,8 +865,8 @@ public class ProjectHelperImpl extends ProjectHelper { | |||||
Target target) { | Target target) { | ||||
super(helperImpl, parentHandler); | super(helperImpl, parentHandler); | ||||
if (parent instanceof TaskAdapter) { | |||||
this.parent = ((TaskAdapter) parent).getProxy(); | |||||
if (parent instanceof TypeAdapter) { | |||||
this.parent = ((TypeAdapter) parent).getProxy(); | |||||
} else { | } else { | ||||
this.parent = parent; | this.parent = parent; | ||||
} | } | ||||
@@ -156,9 +156,6 @@ public class Ant extends Task { | |||||
newProject = new Project(); | newProject = new Project(); | ||||
newProject.setDefaultInputStream(getProject().getDefaultInputStream()); | newProject.setDefaultInputStream(getProject().getDefaultInputStream()); | ||||
newProject.setJavaVersionProperty(); | 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 | // set user-defined properties | ||||
getProject().copyUserProperties(newProject); | getProject().copyUserProperties(newProject); | ||||
@@ -271,6 +250,7 @@ public class Ant extends Task { | |||||
addAlmostAll(getProject().getProperties()); | addAlmostAll(getProject().getProperties()); | ||||
} | } | ||||
Enumeration e; | |||||
e = propertySets.elements(); | e = propertySets.elements(); | ||||
while (e.hasMoreElements()) { | while (e.hasMoreElements()) { | ||||
PropertySet ps = (PropertySet) e.nextElement(); | PropertySet ps = (PropertySet) e.nextElement(); | ||||
@@ -58,15 +58,23 @@ import java.io.File; | |||||
import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.net.URL; | |||||
import java.util.Enumeration; | import java.util.Enumeration; | ||||
import java.util.Locale; | |||||
import java.util.Properties; | import java.util.Properties; | ||||
import org.apache.tools.ant.AntTypeDefinition; | |||||
import org.apache.tools.ant.AntClassLoader; | import org.apache.tools.ant.AntClassLoader; | ||||
import org.apache.tools.ant.ComponentHelper; | |||||
import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
import org.apache.tools.ant.Location; | |||||
import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
import org.apache.tools.ant.ProjectHelper; | |||||
import org.apache.tools.ant.Task; | import org.apache.tools.ant.Task; | ||||
import org.apache.tools.ant.types.Path; | import org.apache.tools.ant.types.Path; | ||||
import org.apache.tools.ant.types.Reference; | import org.apache.tools.ant.types.Reference; | ||||
import org.apache.tools.ant.util.ClasspathUtils; | import org.apache.tools.ant.util.ClasspathUtils; | ||||
import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
/** | /** | ||||
* Base class for Taskdef and Typedef - does all the classpath | * 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 { | public abstract class Definer extends Task { | ||||
private String name; | private String name; | ||||
private String value; | |||||
private String classname; | |||||
private File file; | private File file; | ||||
private String resource; | private String resource; | ||||
private ClasspathUtils.Delegate cpDelegate; | 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 | * @deprecated stop using this attribute | ||||
@@ -165,101 +224,107 @@ public abstract class Definer extends Task { | |||||
public void execute() throws BuildException { | public void execute() throws BuildException { | ||||
ClassLoader al = createLoader(); | 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 { | } 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 { | 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 | * create a classloader for this definition | ||||
*/ | */ | ||||
private ClassLoader createLoader() { | |||||
protected ClassLoader createLoader() { | |||||
if (internalClassLoader != null) { | |||||
return internalClassLoader; | |||||
} | |||||
ClassLoader al = this.cpDelegate.getClassLoader(); | ClassLoader al = this.cpDelegate.getClassLoader(); | ||||
// need to load Task via system classloader or the new | // need to load Task via system classloader or the new | ||||
// task we want to define will never be a Task but always | // 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. | * ant name/classname pairs from. | ||||
*/ | */ | ||||
public void setFile(File file) { | public void setFile(File file) { | ||||
if (definerSet) { | |||||
tooManyDefinitions(); | |||||
} | |||||
definerSet = true; | |||||
this.file = file; | this.file = file; | ||||
} | } | ||||
@@ -282,6 +351,10 @@ public abstract class Definer extends Task { | |||||
* ant name/classname pairs from. | * ant name/classname pairs from. | ||||
*/ | */ | ||||
public void setResource(String res) { | public void setResource(String res) { | ||||
if (definerSet) { | |||||
tooManyDefinitions(); | |||||
} | |||||
definerSet = true; | |||||
this.resource = res; | this.resource = res; | ||||
} | } | ||||
@@ -290,6 +363,10 @@ public abstract class Definer extends Task { | |||||
* ant name/classname pairs from. | * ant name/classname pairs from. | ||||
*/ | */ | ||||
public void setName(String name) { | public void setName(String name) { | ||||
if (definerSet) { | |||||
tooManyDefinitions(); | |||||
} | |||||
definerSet = true; | |||||
this.name = name; | this.name = name; | ||||
} | } | ||||
@@ -298,7 +375,7 @@ public abstract class Definer extends Task { | |||||
* May be <code>null</code>. | * May be <code>null</code>. | ||||
*/ | */ | ||||
public String getClassname() { | public String getClassname() { | ||||
return value; | |||||
return classname; | |||||
} | } | ||||
/** | /** | ||||
@@ -307,16 +384,29 @@ public abstract class Definer extends Task { | |||||
* been specified. | * been specified. | ||||
*/ | */ | ||||
public void setClassname(String v) { | 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() | * @see org.apache.tools.ant.Task#init() | ||||
* @since Ant 1.6 | * @since Ant 1.6 | ||||
@@ -326,4 +416,67 @@ public abstract class Definer extends Task { | |||||
super.init(); | 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,7 +1,7 @@ | |||||
/* | /* | ||||
* The Apache Software License, Version 1.1 | * 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. | * reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
@@ -55,6 +55,8 @@ | |||||
package org.apache.tools.ant.taskdefs; | package org.apache.tools.ant.taskdefs; | ||||
import org.apache.tools.ant.BuildException; | 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 | * 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 | * @since Ant 1.1 | ||||
* @ant.task category="internal" | * @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); | |||||
} | } | ||||
} | } |
@@ -183,9 +183,9 @@ public class ProjectTest extends TestCase { | |||||
p.addBuildListener(mbl); | p.addBuildListener(mbl); | ||||
p.addTaskDefinition("Ok", DummyTaskOk.class); | p.addTaskDefinition("Ok", DummyTaskOk.class); | ||||
assertEquals(DummyTaskOk.class, p.getTaskDefinitions().get("Ok")); | |||||
assertEquals(DummyTaskOk.class, p.getDataTypeDefinitions().get("Ok")); | |||||
p.addTaskDefinition("OkNonTask", DummyTaskOkNonTask.class); | p.addTaskDefinition("OkNonTask", DummyTaskOkNonTask.class); | ||||
assertEquals(DummyTaskOkNonTask.class, p.getTaskDefinitions().get("OkNonTask")); | |||||
assertEquals(DummyTaskOkNonTask.class, p.getDataTypeDefinitions().get("OkNonTask")); | |||||
mbl.assertEmpty(); | mbl.assertEmpty(); | ||||
assertTaskDefFails(DummyTaskPrivate.class, DummyTaskPrivate.class + " is not public"); | 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); | mbl.addBuildEvent("return type of execute() should be void but was \"int\" in " + DummyTaskWithNonVoidExecute.class, Project.MSG_WARN); | ||||
p.addTaskDefinition("NonVoidExecute", DummyTaskWithNonVoidExecute.class); | p.addTaskDefinition("NonVoidExecute", DummyTaskWithNonVoidExecute.class); | ||||
mbl.assertEmpty(); | mbl.assertEmpty(); | ||||
assertEquals(DummyTaskWithNonVoidExecute.class, p.getTaskDefinitions().get("NonVoidExecute")); | |||||
assertEquals(DummyTaskWithNonVoidExecute.class, p.getDataTypeDefinitions().get("NonVoidExecute")); | |||||
} | } | ||||
public void testInputHandler() { | public void testInputHandler() { | ||||
@@ -233,11 +233,12 @@ public class ProjectTest extends TestCase { | |||||
} | } | ||||
public void testTaskDefinitionContainsKey() { | public void testTaskDefinitionContainsKey() { | ||||
assertTrue(p.getTaskDefinitions().containsKey("echo")); | |||||
assertTrue(p.getDataTypeDefinitions().containsKey("echo")); | |||||
} | } | ||||
public void testTaskDefinitionContains() { | 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 { | private class DummyTaskPrivate extends Task { | ||||
@@ -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); | |||||
} | |||||
} | |||||
} | |||||
} |