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