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