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> | |||
<h3>Description</h3> | |||
<p>Adds a task definition to the current project, such that this new task can be | |||
used in the current project. Two attributes are needed, the name that identifies | |||
this task uniquely, and the full name of the class (including the packages) that | |||
implements this task.</p> | |||
<p>You can also define a group of tasks at once using the file or | |||
resource attributes. These attributes point to files in the format of | |||
Java property files. Each line defines a single task in the | |||
format:</p> | |||
<pre> | |||
taskname=fully.qualified.java.classname | |||
</pre> | |||
<p>Taskdef should be used to add your own tasks to the system. See also "<a | |||
href="../develop.html#writingowntask">Writing your own task</a>".</p> | |||
<h3>Parameters</h3> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
<td valign="top"><b>Attribute</b></td> | |||
<td valign="top"><b>Description</b></td> | |||
<td align="center" valign="top"><b>Required</b></td> | |||
</tr> | |||
<tr> | |||
<td valign="top">name</td> | |||
<td valign="top">the name of the task</td> | |||
<td valign="top" align="center">Yes, unless file or resource have | |||
been specified.</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">classname</td> | |||
<td valign="top">the full class name implementing the task</td> | |||
<td valign="top" align="center">Yes, unless file or resource have | |||
been specified.</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">file</td> | |||
<td valign="top">Name of the property file to load | |||
taskname/classname pairs from.</td> | |||
<td valign="top" align="center">No</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">resource</td> | |||
<td valign="top">Name of the property resource to load | |||
taskname/classname pairs from.</td> | |||
<td valign="top" align="center">No</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">classpath</td> <td valign="top">the classpath to | |||
use when looking up <code>classname</code> or | |||
<code>resource</code>.</td> | |||
<td align="center" valign="top">No</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">classpathref</td> | |||
<td valign="top">Reference to a classpath to | |||
use when looking up <code>classname</code> or | |||
<code>resource</code>.</td> | |||
<td align="center" valign="top">No</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">loaderRef</td> <td valign="top">the name of the loader that is | |||
used to load the class, constructed from the specified classpath. Use this to | |||
allow multiple tasks/types to be loaded with the same loader, so they can call | |||
each other. ( introduced in ant1.5 )</td> | |||
<td align="center" valign="top">No</td> | |||
</tr> | |||
</table> | |||
<h3>Parameters specified as nested elements</h3> | |||
<h4>classpath</h4> | |||
<p><code>Taskdef</code>'s <i>classpath</i> attribute is a <a | |||
href="../using.html#path">PATH like structure</a> and can also be set via a nested | |||
<i>classpath</i> element.</p> | |||
<p>Adds a task definition to the current project, such that this new task can be | |||
used in the current project.</p> | |||
<p>This task is a form of <a href="typedef.html">Typedef</a> with the | |||
attributes "adapter" and "adaptto" set to the values | |||
"org.apache.tools.ant.TaskAdapter" and "org.apache.tools.ant.Task" | |||
respectively. | |||
<h3>Examples</h3> | |||
<pre> <taskdef name="myjavadoc" classname="com.mydomain.JavadocTask"/></pre> | |||
<p>makes a task called <code>myjavadoc</code> available to Ant. The class <code>com.mydomain.JavadocTask</code> | |||
implements the task.</p> | |||
<hr> | |||
<p align="center">Copyright © 2000-2002 Apache Software Foundation. All rights | |||
<p align="center">Copyright © 2000-2003 Apache Software Foundation. All rights | |||
Reserved.</p> | |||
</body> | |||
@@ -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> <typedef name="urlset" classname="com.mydomain.URLSet"/></pre> | |||
<p>makes a data type called <code>urlset</code> available to Ant. The | |||
class <code>com.mydomain.URLSet</code> implements this type.</p> | |||
<hr> | |||
<p align="center">Copyright © 2001-2002 Apache Software | |||
<p align="center">Copyright © 2001-2003 Apache Software | |||
Foundation. All rights Reserved.</p> | |||
</body> | |||
@@ -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.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); | |||
} | |||
} | |||
} |
@@ -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. | |||
* | |||
@@ -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, | |||
@@ -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 = | |||
@@ -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. | |||
@@ -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, | |||
RuntimeConfigurable parentWrapper) | |||
throws BuildException { | |||
if (parent instanceof TaskAdapter) { | |||
parent = ((TaskAdapter) parent).getProxy(); | |||
if (parent instanceof TypeAdapter) { | |||
parent = ((TypeAdapter) parent).getProxy(); | |||
} | |||
Class parentClass = parent.getClass(); | |||
@@ -1,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; | |||
} | |||
@@ -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(); | |||
@@ -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()); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 { | |||
@@ -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); | |||
} | |||
} | |||
} | |||
} |