git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271281 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -0,0 +1,60 @@ | |||
| <?xml version='1.0' ?> | |||
| <project name="antlib" default="all"> | |||
| <property name='orig' location='../../..' /> | |||
| <property name='orig-build' location='${orig}/build' /> | |||
| <property name='orig-classes' location='${orig-build}/classes' /> | |||
| <property name='build' location='build' /> | |||
| <property name='dist' location='dist' /> | |||
| <property name='classes' location='${build}/classes' /> | |||
| <property name="debug" value="true" /> | |||
| <property name="deprecation" value="false" /> | |||
| <property name="optimize" value="true" /> | |||
| <target name='init'> | |||
| <ant target='build' dir='${orig}' inheritAll='false' /> | |||
| <mkdir dir='${classes}' /> | |||
| <copy toDir='${classes}' preservelastmodified='true' > | |||
| <fileset dir='${orig-classes}'> | |||
| <include name='**' /> | |||
| <exclude name='org/apache/tools/ant/Project.class' /> | |||
| <exclude name='org/apache/tools/ant/TaskAdapter.class' /> | |||
| <exclude name='org/apache/tools/ant/taskdefs/Ant.class' /> | |||
| </fileset> | |||
| </copy> | |||
| </target> | |||
| <target name='all' depends='init, build' /> | |||
| <target name='fullbuild' depends='init, compile'> | |||
| <ant target='internal_dist' dir='${orig}'> | |||
| <property name="build.dir" value="${build}"/> | |||
| <property name="dist.dir" value="${dist}"/> | |||
| </ant> | |||
| </target> | |||
| <target name='build' depends='init, compile'> | |||
| <ant target='dist-lite' dir='${orig}'> | |||
| <property name="build.dir" value="${build}"/> | |||
| <property name="dist.dir" value="${dist}"/> | |||
| </ant> | |||
| </target> | |||
| <target name='compile'> | |||
| <javac srcdir='src/main' destdir='${classes}' | |||
| debug="${debug}" | |||
| deprecation="${deprecation}" | |||
| optimize="${optimize}"> | |||
| <include name='**/*.java'/> | |||
| </javac> | |||
| </target> | |||
| <target name='clean'> | |||
| <delete dir='${build}' /> | |||
| </target> | |||
| <target name='cleanall' depends='clean'> | |||
| <delete dir='${dist}' /> | |||
| </target> | |||
| </project> | |||
| @@ -12,41 +12,50 @@ | |||
| <p>An extension of the <a href="jar.html">Jar</a> task with special | |||
| treatment for the library descriptor file that should end up in the | |||
| <code>META-INF</code> directory of the Ant Archive.</p> | |||
| <p>This task validates the provided library descriptor making certain | |||
| it specifies the following SYSTEM ID: | |||
| <b>"http://jakarta.apache.org/ant/AntlibV1_0.dtd"</b>. | |||
| This DTD is defined as follows:</p> | |||
| <p> | |||
| Descriptors must follow the following rules, although there is no fix DTD | |||
| for them: | |||
| <pre> | |||
| <?xml version='1.0' encoding="UTF8" ?> | |||
| <!-- | |||
| This file defines the XML format for ANT library descriptors. | |||
| Descriptors must especify a DOCTYPE of | |||
| "http://jakarta.apache.org/ant/Antlib-V1_0.dtd" | |||
| as the SystemId for the document. | |||
| --> | |||
| <!-- Root element for the Antlib descriptor. --> | |||
| <!ELEMENT antlib (task | type)* > | |||
| <!ATTLIST antlib | |||
| version CDATA #IMPLIED | |||
| > | |||
| <!ELEMENT antlib (role | <i>rolename</i>)* > | |||
| <!-- Declaration of tasks contained in the library. --> | |||
| <!ELEMENT task EMPTY> | |||
| <!ATTLIST task | |||
| <!-- Declaration of roles contained in the library. --> | |||
| <!ELEMENT role EMPTY> | |||
| <!ATTLIST role | |||
| name CDATA #REQUIRED | |||
| class CDATA #REQUIRED | |||
| proxy CDATA #IMPLIED | |||
| > | |||
| <!-- Declaration of datatypes contained in the library --> | |||
| <!ELEMENT type EMPTY> | |||
| <!ATTLIST type | |||
| <!ELEMENT <i>rolename</i> EMPTY> | |||
| <!ATTLIST <i>rolename</i> | |||
| name CDATA #REQUIRED | |||
| class CDATA #REQUIRED | |||
| > | |||
| </pre> | |||
| There are two predefined roles: <i><b>task</b></i> and <i><b>datatype</b></i>. | |||
| <p> | |||
| <h4>Role definition</h4> | |||
| The <b>name</b> of the role. This name is used when specifying | |||
| elements for this role. | |||
| <p> | |||
| The <b>class</b> defining a role must be an interface containing a | |||
| unique void method with only one argument whose type is the that of | |||
| elements declared on the role. | |||
| <p> | |||
| The <b>proxy</b> defined in a role specifies a class that can be used | |||
| to bridge between the type expected by the role and the type of | |||
| elements declared for that role. | |||
| <h4>Element definition</h4> | |||
| Any element whose name is that of a role declares an element for that role. | |||
| <p> | |||
| The <b>name</b> defined the name of the element to use in the buildfile | |||
| to specify the element being declared. | |||
| <p> | |||
| The <b>class</b> the class defining the element. | |||
| <h3>Parameters</h3> | |||
| <table border="1" cellpadding="2" cellspacing="0"> | |||
| <tr> | |||
| @@ -78,7 +87,6 @@ in <code>META-INF/antlib.xml</code>.</p> | |||
| <p>Here is a sample <code>META-INF/antlib.xml</code>:</p> | |||
| <pre> | |||
| <?xml version="1.0" encoding="UTF8" ?> | |||
| <!DOCTYPE antlib SYSTEM "http://jakarta.apache.org/ant/Antlib-V1_0.dtd" > | |||
| <antlib version="1.0" > | |||
| <task name="case" class="org.apache.ant.contrib.Case" /> | |||
| @@ -9,16 +9,38 @@ | |||
| <h2><a name="antlib">AntLib</a></h2> | |||
| <h3>Description</h3> | |||
| <p>Defines and loads any tasks and datatypes contained in an ANT library.</p> | |||
| <p>Defines and loads elements contained in an ANT library.</p> | |||
| <p>It also allows the aliasing of the names being defined in order to avoid | |||
| collisions and provides means to override definitions with the ones defined | |||
| in the library.</p> | |||
| Ant libraries can be loaded in the current classloader, which is more efficient, | |||
| Ant libraries are associated with ClassLoaders identified by the | |||
| <tt>loaderid</tt> attribute. If no loader is specified a default loader | |||
| will be used. Ant libraries specifying the same loader are loaded by the | |||
| same ClassLoader as long as the libraries are defined on the same project. | |||
| Classloaders with the same ID in a subproject have the corresponding | |||
| classloader in the parent project as their parent classloader. | |||
| <p> | |||
| Ant libraries can be loaded in the current classloader, | |||
| which is more efficient, | |||
| but requires the tasks to be in the path already (such as in the ant lib | |||
| directory) - set <tt>useCurrentClassloader</tt> to true to enable this. | |||
| It is also possible to add more libraries to the path, such as any | |||
| libraries the task is dependent on. | |||
| <p> | |||
| Ant libraries define objects of several types: | |||
| <ol> | |||
| <li> <b>Roles</b>: Define an interface to be implemented by elements | |||
| (usually tasks) that accept subelements of the specified role. | |||
| Roles may also define a proxy class which may be applied to an element | |||
| in order to make it compatible with the role. | |||
| </li> | |||
| <li> <b>Tasks</b>: define elements that belong to the predefined | |||
| role "task". | |||
| <li> <b>Data types</b>: define elements that belong to the predefined role | |||
| "datatype". | |||
| <li> <b>Other role elements</b>: declare elements for other roles that | |||
| have been previously defined. | |||
| </ol> | |||
| <h3>Parameters</h3> | |||
| <table border="1" cellpadding="2" cellspacing="0"> | |||
| <tr> | |||
| @@ -33,11 +55,13 @@ libraries the task is dependent on. | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">library</td> | |||
| <td valign="top">The name of a library relative to ${ant.home}/lib.</td> | |||
| <td valign="top">The name of a library relative to ${ant.home}/antlib.</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">override</td> | |||
| <td valign="top">Replace any existing definition with the same name. ("true"/"false"). When "false" already defined tasks | |||
| <td valign="top">Replace any existing definition with the same name. | |||
| ("true"/"false"). | |||
| When "false" already defined tasks | |||
| and datatytes take precedence over those in the library. | |||
| Default is "false" when omitted.</td> | |||
| <td align="center" valign="top">No</td> | |||
| @@ -59,12 +83,20 @@ libraries the task is dependent on. | |||
| </td> | |||
| <td valign="top" align="center">No</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">loaderid</td> | |||
| <td valign="top">The ID of the ClassLoader to use to load the classes | |||
| defined in this library. If omitted a default per project ClassLoader | |||
| will be used. | |||
| </td> | |||
| <td valign="top" align="center">No</td> | |||
| </tr> | |||
| </table> | |||
| <h3><a name="nested">Parameters specified as nested elements</a></h3> | |||
| <h4>alias</h4> | |||
| <p>Specifies the usage of a different name from that defined in the library | |||
| descriptor.</p> | |||
| descriptor. Applies only to element definitions (not role declarations).</p> | |||
| <table border="1" cellpadding="2" cellspacing="0"> | |||
| <tr> | |||
| <td valign="top"><b>Attribute</b></td> | |||
| @@ -85,10 +117,9 @@ descriptor.</p> | |||
| <p>Specifies the usage of a different name from that defined in the library | |||
| descriptor. This is used to deal with name clashes </p> | |||
| <h4>classpath</h4> | |||
| <h4>classpath</h4> | |||
| A classpath of extra libraries to import to support this task. | |||
| A classpath of extra libraries to import to support this library. | |||
| <h4>classpathref</h4> | |||
| A reference to an existing classpath. | |||
| @@ -0,0 +1,69 @@ | |||
| /* | |||
| * The Apache Software License, Version 1.1 | |||
| * | |||
| * Copyright (c) 1999 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 "The Jakarta Project", "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; | |||
| public interface RoleAdapter { | |||
| /** | |||
| * Set the object being adapted. | |||
| * @param o the object being adapted | |||
| */ | |||
| public void setProxy(Object o); | |||
| /** | |||
| * Get the object adapted by this class. | |||
| * @return the object being adapted, if any. | |||
| */ | |||
| public Object getProxy(); | |||
| } | |||
| @@ -0,0 +1,491 @@ | |||
| /* | |||
| * The Apache Software License, Version 1.1 | |||
| * | |||
| * Copyright (c) 1999 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 "The Jakarta Project", "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.InvocationTargetException; | |||
| import java.lang.reflect.Method; | |||
| import java.lang.reflect.Modifier; | |||
| import java.util.*; | |||
| import org.apache.tools.ant.types.Path; | |||
| public class SymbolTable { | |||
| /** Parent symbol table */ | |||
| private SymbolTable parentTable; | |||
| /** Project associated with this symbol table */ | |||
| private Project project; | |||
| /** The table of roles available to this Project */ | |||
| private Hashtable roles = new Hashtable(); | |||
| /** The table of loaders active on this Project */ | |||
| private Hashtable loaders = new Hashtable(); | |||
| /** | |||
| * Table of per role definitions. | |||
| */ | |||
| private Hashtable defs = new Hashtable(); | |||
| /** | |||
| * Parameters for checking adapters. | |||
| */ | |||
| private static final Class[] CHECK_ADAPTER_PARAMS = | |||
| new Class[]{Class.class, Project.class}; | |||
| /** | |||
| * Create a top level Symbol table. | |||
| */ | |||
| public SymbolTable() { | |||
| } | |||
| /** | |||
| * Create a symbol table inheriting the definitions | |||
| * from that defined in the calling Project. | |||
| * @param p the calling project | |||
| */ | |||
| public SymbolTable(Project p) { | |||
| parentTable = p.getSymbols(); | |||
| } | |||
| /** | |||
| * Set the project associated with this symbol table. | |||
| * @param p the project for this symbol table | |||
| */ | |||
| public void setProject(Project p) { | |||
| this.project = p; | |||
| } | |||
| /** | |||
| * Find all the roles supported by a Class | |||
| * on this symbol table. | |||
| * @param clz the class to analyze | |||
| * @return an array of roles supported by the class | |||
| */ | |||
| public String[] findRoles(final Class clz) { | |||
| Vector list = new Vector(); | |||
| findRoles(clz, list); | |||
| return (String[])list.toArray(new String[list.size()]); | |||
| } | |||
| /** | |||
| * Collect the roles for the class | |||
| * @param clz the class being inspected | |||
| * @param list the roles collected up to this point | |||
| */ | |||
| private void findRoles(final Class clz, Vector list) { | |||
| for (Enumeration e = roles.keys(); e.hasMoreElements();) { | |||
| String role = (String) e.nextElement(); | |||
| if (((Role) roles.get(role)).isImplementedBy(clz)) { | |||
| list.addElement(role); | |||
| } | |||
| } | |||
| if (parentTable != null) findRoles(clz, list); | |||
| } | |||
| /** | |||
| * Get the Role definition | |||
| * @param role the name of the role | |||
| * @return the method used to support objects on this role | |||
| */ | |||
| public Role getRole(String role) { | |||
| Role r = (Role) roles.get(role); | |||
| if (r == null && parentTable != null) { | |||
| return parentTable.getRole(role); | |||
| } | |||
| return r; | |||
| } | |||
| /** | |||
| * Add a new role definition to this project. | |||
| * @param role the name of the role | |||
| * @param rclz the interface used to specify support for the role. | |||
| * @param aclz the optional adapter class | |||
| * @return whether the role replaced a different definition | |||
| */ | |||
| public boolean addRole(String role, Class rclz, Class aclz) { | |||
| // Check if role already declared | |||
| Role old = getRole(role); | |||
| if (old != null && old.isSameAsFor(rclz, aclz) | |||
| ) { | |||
| project.log("Ignoring override for role " + role | |||
| + ", it is already defined by the same definition.", | |||
| project.MSG_VERBOSE); | |||
| return false; | |||
| } | |||
| // Role interfaces should only contain one method | |||
| roles.put(role, new Role(rclz, aclz)); | |||
| return (old != null); | |||
| } | |||
| /** | |||
| * Verify if the interface is valid. | |||
| * @param clz the interface to validate | |||
| * @return the method defined by the interface | |||
| */ | |||
| private Method validInterface(Class clz) { | |||
| Method m[] = clz.getDeclaredMethods(); | |||
| if (m.length == 1 | |||
| && java.lang.Void.TYPE.equals(m[0].getReturnType())) { | |||
| Class args[] = m[0].getParameterTypes(); | |||
| if (args.length == 1 | |||
| && !java.lang.String.class.equals(args[0]) | |||
| && !args[0].isArray() | |||
| && !args[0].isPrimitive()) { | |||
| return m[0]; | |||
| } | |||
| else { | |||
| throw new BuildException("Invalid role interface method in: " | |||
| + clz.getName()); | |||
| } | |||
| } | |||
| else { | |||
| throw new BuildException("More than one method on role interface"); | |||
| } | |||
| } | |||
| /** | |||
| * Verify if the adapter is valid with respect to the interface. | |||
| * @param clz the class adapter to validate | |||
| * @param mtd the method whose only argument must match | |||
| * @return the static method to use for validating adaptees | |||
| */ | |||
| private Method validAdapter(Class clz, Method mtd) { | |||
| if (clz == null) return null; | |||
| checkClass(clz); | |||
| if (!mtd.getParameterTypes()[0].isAssignableFrom(clz)) { | |||
| String msg = "Adapter " + clz.getName() + | |||
| " is incompatible with role interface " + | |||
| mtd.getDeclaringClass().getName(); | |||
| throw new BuildException(msg); | |||
| } | |||
| String msg = "Class " + clz.getName() + " is not an adapter: "; | |||
| if (!RoleAdapter.class.isAssignableFrom(clz)) { | |||
| throw new BuildException(msg + "does not implement RoleAdapter"); | |||
| } | |||
| try { | |||
| Method chk = clz.getMethod("checkClass", CHECK_ADAPTER_PARAMS); | |||
| if (!Modifier.isStatic(chk.getModifiers())) { | |||
| throw new BuildException(msg + "checkClass() is not static"); | |||
| } | |||
| return chk; | |||
| } | |||
| catch(NoSuchMethodException nme){ | |||
| throw new BuildException(msg + "checkClass() not found", nme); | |||
| } | |||
| } | |||
| /** | |||
| * Get the specified loader for the project. | |||
| * @param name the name of the loader | |||
| * @return the corresponding ANT classloader | |||
| */ | |||
| private AntClassLoader getLoader(String name) { | |||
| AntClassLoader cl = (AntClassLoader) loaders.get(name); | |||
| if (cl == null && parentTable != null) { | |||
| return parentTable.getLoader(name); | |||
| } | |||
| return cl; | |||
| } | |||
| /** | |||
| * Add the specified class-path to a loader. | |||
| * If the loader is defined in an ancestor project then a new | |||
| * classloader inheritin from the one already existing | |||
| * will be created, otherwise the path willbe added to the existing | |||
| * ClassLoader. | |||
| * @param name the name of the loader to use. | |||
| * @param clspath the path to be added to the classloader | |||
| */ | |||
| public ClassLoader addToLoader(String name, Path clspath) { | |||
| // Find if the loader is already defined in the current project | |||
| AntClassLoader cl = (AntClassLoader) loaders.get(name); | |||
| if (cl == null) { | |||
| // Is it inherited from the calling project | |||
| if (parentTable != null) { | |||
| cl = parentTable.getLoader(name); | |||
| } | |||
| cl = new AntClassLoader(cl, project, clspath, true); | |||
| loaders.put(name, cl); | |||
| } | |||
| else { | |||
| // Add additional path to the existing definition | |||
| String[] pathElements = clspath.list(); | |||
| for (int i = 0; i < pathElements.length; ++i) { | |||
| try { | |||
| cl.addPathElement(pathElements[i]); | |||
| } | |||
| catch (BuildException e) { | |||
| // ignore path elements invalid relative to the project | |||
| } | |||
| } | |||
| } | |||
| return cl; | |||
| } | |||
| /** | |||
| * Add a new type of element to a role. | |||
| * @param role the role for this Class. | |||
| * @param name the name of the element for this Class | |||
| * @param clz the Class being declared | |||
| * @return the old definition | |||
| */ | |||
| public Class add(String role, String name, Class clz) { | |||
| // Find the role definition | |||
| Role r = getRole(role); | |||
| if (r == null) { | |||
| throw new BuildException("Unknown role: " + role); | |||
| } | |||
| // Check if it is already defined | |||
| Class old = get(role, name); | |||
| if (old != null) { | |||
| if (old.equals(clz)) { | |||
| project.log("Ignoring override for "+ role + " " + name | |||
| + ", it is already defined by the same class.", | |||
| project.MSG_VERBOSE); | |||
| return old; | |||
| } | |||
| else { | |||
| project.log("Trying to override old definition of " + | |||
| role + " " + name, | |||
| project.MSG_WARN); | |||
| } | |||
| } | |||
| checkClass(clz); | |||
| // Check that the Class is compatible with the role definition | |||
| r.verifyAdaptability(role, clz); | |||
| // Record the new type | |||
| Hashtable defTable = (Hashtable)defs.get(role); | |||
| if (defTable == null) { | |||
| defTable = new Hashtable(); | |||
| defs.put(role, defTable); | |||
| } | |||
| defTable.put(name, clz); | |||
| return old; | |||
| } | |||
| /** | |||
| * Checks a class, whether it is suitable for serving in ANT. | |||
| * @throws BuildException and logs as Project.MSG_ERR for | |||
| * conditions, that will cause execution to fail. | |||
| */ | |||
| void checkClass(final Class clz) | |||
| throws BuildException { | |||
| if(!Modifier.isPublic(clz.getModifiers())) { | |||
| final String message = clz + " is not public"; | |||
| project.log(message, Project.MSG_ERR); | |||
| throw new BuildException(message); | |||
| } | |||
| if(Modifier.isAbstract(clz.getModifiers())) { | |||
| final String message = clz + " is abstract"; | |||
| project.log(message, Project.MSG_ERR); | |||
| throw new BuildException(message); | |||
| } | |||
| try { | |||
| // Class can have a "no arg" constructor or take a single | |||
| // Project argument. | |||
| // don't have to check for public, since | |||
| // getConstructor finds public constructors only. | |||
| try { | |||
| clz.getConstructor(new Class[0]); | |||
| } catch (NoSuchMethodException nse) { | |||
| clz.getConstructor(new Class[] {Project.class}); | |||
| } | |||
| } catch(NoSuchMethodException e) { | |||
| final String message = | |||
| "No valid public constructor in " + clz; | |||
| project.log(message, Project.MSG_ERR); | |||
| throw new BuildException(message); | |||
| } | |||
| } | |||
| /** | |||
| * Get the class in the role identified with the element name. | |||
| * @param role the role to look into. | |||
| * @param name the name of the element to sea | |||
| * @return the Class implementation | |||
| */ | |||
| public Class get(String role, String name) { | |||
| Hashtable defTable = (Hashtable)defs.get(role); | |||
| if (defTable != null) { | |||
| Class clz = (Class)defTable.get(name); | |||
| if (clz != null) return clz; | |||
| } | |||
| if (parentTable != null) { | |||
| return parentTable.get(role, name); | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * Get a Hashtable that is usable for manipulating Tasks, | |||
| * @return a Hashtable that delegates to the Symbol table. | |||
| */ | |||
| public Hashtable getTaskDefinitions() { | |||
| return new SymbolHashtable("task"); | |||
| } | |||
| /** | |||
| * Get a Hashtable that is usable for manipulating Datatypes, | |||
| * @return a Hashtable that delegates to the Symbol table. | |||
| */ | |||
| public Hashtable getDataTypeDefinitions() { | |||
| return new SymbolHashtable("datatype"); | |||
| } | |||
| /** | |||
| * Hashtable implementation that delegates | |||
| * the search operations to the Symbol table | |||
| */ | |||
| private class SymbolHashtable extends Hashtable { | |||
| final String role; | |||
| SymbolHashtable(String role) { | |||
| this.role = role; | |||
| } | |||
| public synchronized Object put(Object key, Object value) { | |||
| return SymbolTable.this.add(role, (String) key, (Class) value); | |||
| } | |||
| public synchronized Object get(Object key) { | |||
| return SymbolTable.this.get(role, (String)key); | |||
| } | |||
| } | |||
| /** | |||
| * The definition of a role | |||
| */ | |||
| public class Role { | |||
| private Method interfaceMethod; | |||
| private Method adapterVerifier; | |||
| /** | |||
| * Creates a new Role object | |||
| * @param roleClz the class that defines the role | |||
| * @param adapterClz the class for the adapter, or null if none | |||
| */ | |||
| Role(Class roleClz, Class adapterClz) { | |||
| interfaceMethod = validInterface(roleClz); | |||
| adapterVerifier = validAdapter(adapterClz, interfaceMethod); | |||
| } | |||
| /** | |||
| * Get the method used to set on interface | |||
| */ | |||
| public Method getInterfaceMethod() { | |||
| return interfaceMethod; | |||
| } | |||
| /** | |||
| * Instantiate a new adapter for this role. | |||
| */ | |||
| public RoleAdapter createAdapter() { | |||
| if (adapterVerifier == null) return null; | |||
| try { | |||
| return (RoleAdapter) | |||
| adapterVerifier.getDeclaringClass().newInstance(); | |||
| } | |||
| catch(BuildException be) { | |||
| throw be; | |||
| } | |||
| catch(Exception e) { | |||
| throw new BuildException(e); | |||
| } | |||
| } | |||
| /** | |||
| * Verify if the class can be adapted to use by the role | |||
| * @param role the name of the role to verify | |||
| * @param clz the class to verify | |||
| */ | |||
| public void verifyAdaptability(String role, Class clz) { | |||
| if (interfaceMethod.getParameterTypes()[0].isAssignableFrom(clz)) { | |||
| return; | |||
| } | |||
| if (adapterVerifier == null) { | |||
| String msg = "Class " + clz.getName() + | |||
| " incompatible with role: " + role; | |||
| throw new BuildException(msg); | |||
| } | |||
| try { | |||
| try { | |||
| adapterVerifier.invoke(null, | |||
| new Object[]{clz, project}); | |||
| } | |||
| catch (InvocationTargetException ite) { | |||
| throw ite.getTargetException(); | |||
| } | |||
| } | |||
| catch(BuildException be) { throw be; } | |||
| catch(Error err) {throw err; } | |||
| catch(Throwable t) { | |||
| throw new BuildException(t); | |||
| } | |||
| } | |||
| public boolean isSameAsFor(Class clz, Class pclz) { | |||
| return interfaceMethod.getDeclaringClass().equals(clz) && | |||
| ((adapterVerifier == null && pclz == null) || | |||
| adapterVerifier.getDeclaringClass().equals(pclz)); | |||
| } | |||
| public boolean isImplementedBy(Class clz) { | |||
| return interfaceMethod.getDeclaringClass().isAssignableFrom(clz); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,168 @@ | |||
| /* | |||
| * The Apache Software License, Version 1.1 | |||
| * | |||
| * Copyright (c) 2000-2001 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 "The Jakarta Project", "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; | |||
| /** | |||
| * Use introspection to "adapt" an arbitrary Bean ( not extending Task, but with similar | |||
| * patterns). | |||
| * | |||
| * @author costin@dnt.ro | |||
| * @author j_a_fernandez@yahoo.com | |||
| */ | |||
| public class TaskAdapter extends Task implements RoleAdapter { | |||
| Object proxy; | |||
| /** | |||
| * Checks a class, whether it is suitable to be adapted by TaskAdapter. | |||
| * | |||
| * Checks conditions only, which are additionally required for a tasks | |||
| * adapted by TaskAdapter. Thus, this method should be called by | |||
| * {@link Project#checkTaskClass}. | |||
| * | |||
| * Throws a BuildException and logs as Project.MSG_ERR for | |||
| * conditions, that will cause the task execution to fail. | |||
| * Logs other suspicious conditions with Project.MSG_WARN. | |||
| */ | |||
| public static void checkTaskClass(final Class taskClass, final Project project) { | |||
| // This code is for backward compatibility | |||
| checkClass(taskClass, project); | |||
| } | |||
| /** | |||
| * Checks a class, whether it is suitable to be adapted. | |||
| * | |||
| * Checks conditions only, which are additionally required for a tasks | |||
| * adapted by TaskAdapter. | |||
| * | |||
| * Throws a BuildException and logs as Project.MSG_ERR for | |||
| * conditions, that will cause the task execution to fail. | |||
| * Logs other suspicious conditions with Project.MSG_WARN. | |||
| */ | |||
| public static void checkClass(final Class taskClass, final Project project) { | |||
| // don't have to check for interface, since then | |||
| // taskClass would be abstract too. | |||
| try { | |||
| final Method executeM = taskClass.getMethod( "execute", null ); | |||
| // don't have to check for public, since | |||
| // getMethod finds public method only. | |||
| // don't have to check for abstract, since then | |||
| // taskClass would be abstract too. | |||
| if(!Void.TYPE.equals(executeM.getReturnType())) { | |||
| final String message = "return type of execute() should be void but was \""+executeM.getReturnType()+"\" in " + taskClass; | |||
| project.log(message, Project.MSG_WARN); | |||
| } | |||
| } catch(NoSuchMethodException e) { | |||
| final String message = "No public execute() in " + taskClass; | |||
| project.log(message, Project.MSG_ERR); | |||
| throw new BuildException(message); | |||
| } | |||
| } | |||
| /** | |||
| * Do the execution. | |||
| */ | |||
| public void execute() throws BuildException { | |||
| Method setProjectM = null; | |||
| try { | |||
| Class c = proxy.getClass(); | |||
| setProjectM = | |||
| c.getMethod( "setProject", new Class[] {Project.class}); | |||
| if(setProjectM != null) { | |||
| setProjectM.invoke(proxy, new Object[] {project}); | |||
| } | |||
| } catch (NoSuchMethodException e) { | |||
| // ignore this if the class being used as a task does not have | |||
| // a set project method. | |||
| } catch( Exception ex ) { | |||
| log("Error setting project in " + proxy.getClass(), | |||
| Project.MSG_ERR); | |||
| throw new BuildException( ex ); | |||
| } | |||
| Method executeM=null; | |||
| try { | |||
| Class c=proxy.getClass(); | |||
| executeM=c.getMethod( "execute", new Class[0] ); | |||
| if( executeM == null ) { | |||
| log("No public execute() in " + proxy.getClass(), Project.MSG_ERR); | |||
| throw new BuildException("No public execute() in " + proxy.getClass()); | |||
| } | |||
| executeM.invoke(proxy, null); | |||
| return; | |||
| } catch( Exception ex ) { | |||
| log("Error in " + proxy.getClass(), Project.MSG_ERR); | |||
| throw new BuildException( ex ); | |||
| } | |||
| } | |||
| /** | |||
| * Set the target object class | |||
| */ | |||
| public void setProxy(Object o) { | |||
| this.proxy = o; | |||
| } | |||
| public Object getProxy() { | |||
| return this.proxy ; | |||
| } | |||
| } | |||
| @@ -0,0 +1,475 @@ | |||
| /* | |||
| * The Apache Software License, Version 1.1 | |||
| * | |||
| * Copyright (c) 2000-2002 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 "The Jakarta Project", "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 org.apache.tools.ant.Task; | |||
| import org.apache.tools.ant.Project; | |||
| import org.apache.tools.ant.ProjectComponent; | |||
| import org.apache.tools.ant.BuildListener; | |||
| import org.apache.tools.ant.DefaultLogger; | |||
| import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.ProjectHelper; | |||
| import org.apache.tools.ant.util.FileUtils; | |||
| import java.io.File; | |||
| import java.io.PrintStream; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.lang.reflect.Method; | |||
| import java.util.Vector; | |||
| import java.util.Hashtable; | |||
| import java.util.Enumeration; | |||
| /** | |||
| * Call Ant in a sub-project | |||
| * | |||
| * <pre> | |||
| * <target name="foo" depends="init"> | |||
| * <ant antfile="build.xml" target="bar" > | |||
| * <property name="property1" value="aaaaa" /> | |||
| * <property name="foo" value="baz" /> | |||
| * </ant></SPAN> | |||
| * </target></SPAN> | |||
| * | |||
| * <target name="bar" depends="init"> | |||
| * <echo message="prop is ${property1} ${foo}" /> | |||
| * </target> | |||
| * </pre> | |||
| * | |||
| * | |||
| * @author costin@dnt.ro | |||
| */ | |||
| public class Ant extends Task { | |||
| /** the basedir where is executed the build file */ | |||
| private File dir = null; | |||
| /** the build.xml file (can be absolute) in this case dir will be ignored */ | |||
| private String antFile = null; | |||
| /** the target to call if any */ | |||
| private String target = null; | |||
| /** the output */ | |||
| private String output = null; | |||
| /** should we inherit properties from the parent ? */ | |||
| private boolean inheritAll = true; | |||
| /** should we inherit references from the parent ? */ | |||
| private boolean inheritRefs = false; | |||
| /** the properties to pass to the new project */ | |||
| private Vector properties = new Vector(); | |||
| /** the references to pass to the new project */ | |||
| private Vector references = new Vector(); | |||
| /** the temporary project created to run the build file */ | |||
| private Project newProject; | |||
| /** | |||
| * If true, inherit all properties from parent Project | |||
| * If false, inherit only userProperties and those defined | |||
| * inside the ant call itself | |||
| */ | |||
| public void setInheritAll(boolean value) { | |||
| inheritAll = value; | |||
| } | |||
| /** | |||
| * If true, inherit all references from parent Project | |||
| * If false, inherit only those defined | |||
| * inside the ant call itself | |||
| */ | |||
| public void setInheritRefs(boolean value) { | |||
| inheritRefs = value; | |||
| } | |||
| public void init() { | |||
| newProject = new Project(project); | |||
| newProject.setJavaVersionProperty(); | |||
| // newProject.addTaskDefinition("property", | |||
| // (Class)project.getTaskDefinitions().get("property")); | |||
| } | |||
| private void reinit() { | |||
| init(); | |||
| final int count = properties.size(); | |||
| for (int i = 0; i < count; i++) { | |||
| Property p = (Property) properties.elementAt(i); | |||
| Property newP = (Property) newProject.createTask("property"); | |||
| newP.setName(p.getName()); | |||
| if (p.getValue() != null) { | |||
| newP.setValue(p.getValue()); | |||
| } | |||
| if (p.getFile() != null) { | |||
| newP.setFile(p.getFile()); | |||
| } | |||
| if (p.getResource() != null) { | |||
| newP.setResource(p.getResource()); | |||
| } | |||
| properties.setElementAt(newP, i); | |||
| } | |||
| } | |||
| private void initializeProject() { | |||
| Vector listeners = project.getBuildListeners(); | |||
| final int count = listeners.size(); | |||
| for (int i = 0; i < count; i++) { | |||
| newProject.addBuildListener((BuildListener)listeners.elementAt(i)); | |||
| } | |||
| if (output != null) { | |||
| try { | |||
| PrintStream out = new PrintStream(new FileOutputStream(output)); | |||
| DefaultLogger logger = new DefaultLogger(); | |||
| logger.setMessageOutputLevel(Project.MSG_INFO); | |||
| logger.setOutputPrintStream(out); | |||
| logger.setErrorPrintStream(out); | |||
| newProject.addBuildListener(logger); | |||
| } | |||
| catch( IOException ex ) { | |||
| log( "Ant: Can't set output to " + output ); | |||
| } | |||
| } | |||
| // Hashtable taskdefs = project.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 = project.getDataTypeDefinitions(); | |||
| // Enumeration e = typedefs.keys(); | |||
| // while (e.hasMoreElements()) { | |||
| // String typeName = (String) e.nextElement(); | |||
| // Class typeClass = (Class) typedefs.get(typeName); | |||
| // newProject.addDataTypeDefinition(typeName, typeClass); | |||
| // } | |||
| // set user-defined or all properties from calling project | |||
| Hashtable prop1; | |||
| if (inheritAll) { | |||
| prop1 = project.getProperties(); | |||
| } else { | |||
| prop1 = project.getUserProperties(); | |||
| // set Java built-in properties separately, | |||
| // b/c we won't inherit them. | |||
| newProject.setSystemProperties(); | |||
| } | |||
| Enumeration e = prop1.keys(); | |||
| while (e.hasMoreElements()) { | |||
| String arg = (String) e.nextElement(); | |||
| if ("basedir".equals(arg) || "ant.file".equals(arg)) { | |||
| // basedir and ant.file get special treatment in execute() | |||
| continue; | |||
| } | |||
| String value = (String) prop1.get(arg); | |||
| if (inheritAll){ | |||
| newProject.setProperty(arg, value); | |||
| } else { | |||
| newProject.setUserProperty(arg, value); | |||
| } | |||
| } | |||
| } | |||
| protected void handleOutput(String line) { | |||
| if (newProject != null) { | |||
| newProject.demuxOutput(line, false); | |||
| } else { | |||
| super.handleOutput(line); | |||
| } | |||
| } | |||
| protected void handleErrorOutput(String line) { | |||
| if (newProject != null) { | |||
| newProject.demuxOutput(line, true); | |||
| } else { | |||
| super.handleErrorOutput(line); | |||
| } | |||
| } | |||
| /** | |||
| * Do the execution. | |||
| */ | |||
| public void execute() throws BuildException { | |||
| try { | |||
| if (newProject == null) { | |||
| reinit(); | |||
| } | |||
| if ( (dir == null) && (inheritAll) ) { | |||
| dir = project.getBaseDir(); | |||
| } | |||
| initializeProject(); | |||
| if (dir != null) { | |||
| newProject.setBaseDir(dir); | |||
| newProject.setUserProperty("basedir" , dir.getAbsolutePath()); | |||
| } else { | |||
| dir = project.getBaseDir(); | |||
| } | |||
| overrideProperties(); | |||
| if (antFile == null) { | |||
| antFile = "build.xml"; | |||
| } | |||
| File file = FileUtils.newFileUtils().resolveFile(dir, antFile); | |||
| antFile = file.getAbsolutePath(); | |||
| newProject.setUserProperty( "ant.file" , antFile ); | |||
| ProjectHelper.configureProject(newProject, new File(antFile)); | |||
| if (target == null) { | |||
| target = newProject.getDefaultTarget(); | |||
| } | |||
| addReferences(); | |||
| // Are we trying to call the target in which we are defined? | |||
| if (newProject.getBaseDir().equals(project.getBaseDir()) && | |||
| newProject.getProperty("ant.file").equals(project.getProperty("ant.file")) && | |||
| getOwningTarget() != null && | |||
| target.equals(this.getOwningTarget().getName())) { | |||
| throw new BuildException("ant task calling its own parent target"); | |||
| } | |||
| newProject.executeTarget(target); | |||
| } finally { | |||
| // help the gc | |||
| newProject = null; | |||
| } | |||
| } | |||
| /** | |||
| * Override the properties in the new project with the one | |||
| * explicitly defined as nested elements here. | |||
| */ | |||
| private void overrideProperties() throws BuildException { | |||
| Enumeration e = properties.elements(); | |||
| while (e.hasMoreElements()) { | |||
| Property p = (Property) e.nextElement(); | |||
| p.setProject(newProject); | |||
| p.execute(); | |||
| } | |||
| } | |||
| /** | |||
| * Add the references explicitly defined as nested elements to the | |||
| * new project. Also copy over all references that don't override | |||
| * existing references in the new project if inheritall has been | |||
| * requested. | |||
| */ | |||
| private void addReferences() throws BuildException { | |||
| Hashtable thisReferences = (Hashtable) project.getReferences().clone(); | |||
| Hashtable newReferences = newProject.getReferences(); | |||
| Enumeration e; | |||
| if (references.size() > 0) { | |||
| for(e = references.elements(); e.hasMoreElements();) { | |||
| Reference ref = (Reference)e.nextElement(); | |||
| String refid = ref.getRefId(); | |||
| if (refid == null) { | |||
| throw new BuildException("the refid attribute is required for reference elements"); | |||
| } | |||
| if (!thisReferences.containsKey(refid)) { | |||
| log("Parent project doesn't contain any reference '" | |||
| + refid + "'", | |||
| Project.MSG_WARN); | |||
| continue; | |||
| } | |||
| thisReferences.remove(refid); | |||
| String toRefid = ref.getToRefid(); | |||
| if (toRefid == null) { | |||
| toRefid = refid; | |||
| } | |||
| copyReference(refid, toRefid); | |||
| } | |||
| } | |||
| // Now add all references that are not defined in the | |||
| // subproject, if inheritRefs is true | |||
| if (inheritRefs) { | |||
| for(e = thisReferences.keys(); e.hasMoreElements();) { | |||
| String key = (String)e.nextElement(); | |||
| if (newReferences.containsKey(key)) { | |||
| continue; | |||
| } | |||
| copyReference(key, key); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * Try to clone and reconfigure the object referenced by oldkey in | |||
| * the parent project and add it to the new project with the key | |||
| * newkey. | |||
| * | |||
| * <p>If we cannot clone it, copy the referenced object itself and | |||
| * keep our fingers crossed.</p> | |||
| */ | |||
| private void copyReference(String oldKey, String newKey) { | |||
| Object orig = project.getReference(oldKey); | |||
| Class c = orig.getClass(); | |||
| Object copy = orig; | |||
| try { | |||
| Method cloneM = c.getMethod("clone", new Class[0]); | |||
| if (cloneM != null) { | |||
| copy = cloneM.invoke(orig, new Object[0]); | |||
| } | |||
| } catch (Exception e) { | |||
| // not Clonable | |||
| } | |||
| if (copy instanceof ProjectComponent) { | |||
| ((ProjectComponent) copy).setProject(newProject); | |||
| } else { | |||
| try { | |||
| Method setProjectM = | |||
| c.getMethod( "setProject", new Class[] {Project.class}); | |||
| if(setProjectM != null) { | |||
| setProjectM.invoke(copy, new Object[] {newProject}); | |||
| } | |||
| } catch (NoSuchMethodException e) { | |||
| // ignore this if the class being referenced does not have | |||
| // a set project method. | |||
| } catch(Exception e2) { | |||
| String msg = "Error setting new project instance for reference with id " | |||
| + oldKey; | |||
| throw new BuildException(msg, e2, location); | |||
| } | |||
| } | |||
| newProject.addReference(newKey, copy); | |||
| } | |||
| /** | |||
| * ... | |||
| */ | |||
| public void setDir(File d) { | |||
| this.dir = d; | |||
| } | |||
| /** | |||
| * set the build file, it can be either absolute or relative. | |||
| * If it is absolute, <tt>dir</tt> will be ignored, if it is | |||
| * relative it will be resolved relative to <tt>dir</tt>. | |||
| */ | |||
| public void setAntfile(String s) { | |||
| // @note: it is a string and not a file to handle relative/absolute | |||
| // otherwise a relative file will be resolved based on the current | |||
| // basedir. | |||
| this.antFile = s; | |||
| } | |||
| /** | |||
| * set the target to execute. If none is defined it will | |||
| * execute the default target of the build file | |||
| */ | |||
| public void setTarget(String s) { | |||
| this.target = s; | |||
| } | |||
| public void setOutput(String s) { | |||
| this.output = s; | |||
| } | |||
| /** create a property to pass to the new project as a 'user property' */ | |||
| public Property createProperty() { | |||
| if (newProject == null) { | |||
| reinit(); | |||
| } | |||
| Property p = new Property(true); | |||
| p.setProject(newProject); | |||
| p.setTaskName("property"); | |||
| properties.addElement( p ); | |||
| return p; | |||
| } | |||
| /** | |||
| * create a reference element that identifies a data type that | |||
| * should be carried over to the new project. | |||
| */ | |||
| public void addReference(Reference r) { | |||
| references.addElement(r); | |||
| } | |||
| /** | |||
| * Helper class that implements the nested <reference> | |||
| * element of <ant> and <antcall>. | |||
| */ | |||
| public static class Reference | |||
| extends org.apache.tools.ant.types.Reference { | |||
| public Reference() {super();} | |||
| private String targetid=null; | |||
| public void setToRefid(String targetid) { this.targetid=targetid; } | |||
| public String getToRefid() { return targetid; } | |||
| } | |||
| } | |||
| @@ -107,9 +107,6 @@ public class Antjar extends Jar { | |||
| throw new BuildException("Deployment descriptor: " + libraryDescriptor + " does not exist."); | |||
| } | |||
| //check | |||
| validateDescriptor(); | |||
| // Create a ZipFileSet for this file, and pass it up. | |||
| ZipFileSet fs = new ZipFileSet(); | |||
| fs.setDir(new File(libraryDescriptor.getParent())); | |||
| @@ -130,7 +127,7 @@ public class Antjar extends Jar { | |||
| throws IOException, BuildException { | |||
| // If no antxml file is specified, it's an error. | |||
| if (libraryDescriptor == null) { | |||
| throw new BuildException("webxml attribute is required", location); | |||
| throw new BuildException("antxml attribute is required", location); | |||
| } | |||
| super.initZipOutputStream(zOut); | |||
| @@ -177,140 +174,4 @@ public class Antjar extends Jar { | |||
| super.cleanUp(); | |||
| } | |||
| /** | |||
| * validate the descriptor against the DTD | |||
| * | |||
| * @exception BuildException failure to validate | |||
| */ | |||
| protected void validateDescriptor() | |||
| throws BuildException { | |||
| SAXParserFactory saxFactory = SAXParserFactory.newInstance(); | |||
| saxFactory.setValidating(true); | |||
| InputStream is = null; | |||
| try { | |||
| SAXParser saxParser = saxFactory.newSAXParser(); | |||
| Parser parser = saxParser.getParser(); | |||
| is = new FileInputStream(libraryDescriptor); | |||
| InputSource inputSource = new InputSource(is); | |||
| inputSource.setSystemId("file:" + libraryDescriptor); | |||
| project.log("Validating library descriptor: " + libraryDescriptor, | |||
| Project.MSG_VERBOSE); | |||
| saxParser.parse(inputSource, new AntLibraryValidator()); | |||
| } | |||
| catch (ParserConfigurationException exc) { | |||
| throw new BuildException("Parser has not been configured correctly", exc); | |||
| } | |||
| catch (SAXParseException exc) { | |||
| Location location = | |||
| new Location(libraryDescriptor.toString(), | |||
| exc.getLineNumber(), exc.getColumnNumber()); | |||
| Throwable t = exc.getException(); | |||
| if (t instanceof BuildException) { | |||
| BuildException be = (BuildException) t; | |||
| if (be.getLocation() == Location.UNKNOWN_LOCATION) { | |||
| be.setLocation(location); | |||
| } | |||
| throw be; | |||
| } | |||
| throw new BuildException(exc.getMessage(), t, location); | |||
| } | |||
| catch (SAXException exc) { | |||
| Throwable t = exc.getException(); | |||
| if (t instanceof BuildException) { | |||
| throw (BuildException) t; | |||
| } | |||
| throw new BuildException(exc.getMessage(), t); | |||
| } | |||
| catch (IOException exc) { | |||
| throw new BuildException("Error reading library descriptor", exc); | |||
| } | |||
| finally { | |||
| if (is != null) { | |||
| try { | |||
| is.close(); | |||
| } | |||
| catch (IOException ioe) { | |||
| // ignore this | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * Parses the document describing the content of the library. | |||
| */ | |||
| private class AntLibraryValidator extends HandlerBase { | |||
| /** | |||
| * flag to track whether the DOCTYPE was hit in the prolog | |||
| */ | |||
| private boolean doctypePresent = false; | |||
| /** | |||
| * doc locator | |||
| */ | |||
| private Locator locator = null; | |||
| /** | |||
| * Sets the DocumentLocator attribute of the AntLibraryValidator | |||
| * object | |||
| * | |||
| * @param locator The new DocumentLocator value | |||
| */ | |||
| public void setDocumentLocator(Locator locator) { | |||
| this.locator = locator; | |||
| } | |||
| /** | |||
| * SAX callback handler | |||
| * | |||
| * @param tag XML tag | |||
| * @param attrs attributes | |||
| * @exception SAXParseException parse trouble | |||
| */ | |||
| public void startElement(String tag, AttributeList attrs) | |||
| throws SAXParseException { | |||
| // By the time an element is found | |||
| // the DOCTYPE should have been found. | |||
| if (!doctypePresent) { | |||
| String msg = "Missing DOCTYPE declaration or wrong SYSTEM ID"; | |||
| throw new SAXParseException(msg, locator); | |||
| } | |||
| } | |||
| /** | |||
| * Recognizes the DTD declaration for antlib and returns the corresponding | |||
| * DTD definition from a resource. <P> | |||
| * | |||
| * To allow for future versions of the DTD format it will search | |||
| * for any DTDs of the form "Antlib-V.*\.dtd". | |||
| * | |||
| * @param publicId public ID (ignored) | |||
| * @param systemId system ID (matched against) | |||
| * @return local DTD instance | |||
| */ | |||
| public InputSource resolveEntity(String publicId, | |||
| String systemId) { | |||
| log("Looking for entity with PublicID=" + publicId + | |||
| " and SystemId=" + systemId, Project.MSG_VERBOSE); | |||
| if (Antlib.matchDtdId(systemId)) { | |||
| String resId = | |||
| systemId.substring(Antlib.ANTLIB_DTD_URL.length()); | |||
| InputSource is = | |||
| new InputSource(this.getClass().getResourceAsStream(resId)); | |||
| is.setSystemId(systemId); | |||
| doctypePresent = true; | |||
| return is; | |||
| } | |||
| return null; | |||
| } | |||
| //end inner class AntLibraryValidator | |||
| } | |||
| } | |||
| @@ -76,9 +76,11 @@ import java.io.*; | |||
| * @since ant1.5 | |||
| */ | |||
| public class Antlib extends Task { | |||
| /* | |||
| * implements DeclaringTask | |||
| /** | |||
| * The named classloader to use. | |||
| * Defaults to the default classLoader. | |||
| */ | |||
| private String loaderId = ""; | |||
| /** | |||
| * library attribute | |||
| @@ -177,6 +179,15 @@ public class Antlib extends Task { | |||
| this.file = file; | |||
| } | |||
| /** | |||
| * Set the ClassLoader to use for this library. | |||
| * | |||
| * @param id the id for the ClassLoader to use, | |||
| * if other than the default. | |||
| */ | |||
| public void setLoaderid(String id) { | |||
| this.loaderId = id; | |||
| } | |||
| /** | |||
| * Set whether to override any existing definitions. | |||
| @@ -189,8 +200,9 @@ public class Antlib extends Task { | |||
| /** | |||
| * Set whether to use a new classloader or not. Default is <code>false</code> | |||
| * . This property is mostly used by the core when loading core tasks. | |||
| * Set whether to use a new classloader or not. | |||
| * Default is <code>false</code>. | |||
| * This property is mostly used by the core when loading core tasks. | |||
| * | |||
| * @param useCurrentClassloader if true the current classloader will | |||
| * be used to load the definitions. | |||
| @@ -264,7 +276,7 @@ public class Antlib extends Task { | |||
| String msg = "You cannot specify both file and library."; | |||
| throw new BuildException(msg, location); | |||
| } | |||
| // For the time being libraries live in $ANT_HOME/lib. | |||
| // For the time being libraries live in $ANT_HOME/antlib. | |||
| // The idea being that we would not load all the jars there anymore | |||
| String home = project.getProperty("ant.home"); | |||
| @@ -272,7 +284,7 @@ public class Antlib extends Task { | |||
| throw new BuildException("ANT_HOME not set as required."); | |||
| } | |||
| realFile = new File(new File(home, "lib"), library); | |||
| realFile = new File(new File(home, "antlib"), library); | |||
| } | |||
| else if (file == null) { | |||
| String msg = "Must specify either library or file attribute."; | |||
| @@ -406,8 +418,7 @@ public class Antlib extends Task { | |||
| if (classpath != null) { | |||
| clspath.append(classpath); | |||
| } | |||
| AntClassLoader al = new AntClassLoader(project, clspath, true); | |||
| return al; | |||
| return project.getSymbols().addToLoader(loaderId, clspath); | |||
| } | |||
| @@ -473,30 +484,6 @@ public class Antlib extends Task { | |||
| } | |||
| /** | |||
| * get a DTD URI from url, prefix and extension | |||
| * | |||
| * @return URI for this dtd version | |||
| */ | |||
| public static String dtdVersion() { | |||
| return ANTLIB_DTD_URL + ANTLIB_DTD_PREFIX + | |||
| ANTLIB_DTD_VERSION + ANTLIB_DTD_EXT; | |||
| } | |||
| /** | |||
| * compare system ID with the dtd string | |||
| * -ignoring any version number | |||
| * @param systemId Description of Parameter | |||
| * @return true if this is a an ant library descriptor | |||
| */ | |||
| public static boolean matchDtdId(String systemId) { | |||
| return (systemId != null && | |||
| systemId.startsWith(ANTLIB_DTD_URL + ANTLIB_DTD_PREFIX) && | |||
| systemId.endsWith(ANTLIB_DTD_EXT)); | |||
| } | |||
| /** | |||
| * Parses the document describing the content of the | |||
| * library. An inner class for access to Project.log | |||
| @@ -516,6 +503,14 @@ public class Antlib extends Task { | |||
| */ | |||
| private Locator locator = null; | |||
| private int level = 0; | |||
| private SymbolTable symbols = null; | |||
| private String name = null; | |||
| private String className = null; | |||
| private String adapter = null; | |||
| /** | |||
| * Constructor for the AntLibraryHandler object | |||
| * | |||
| @@ -525,9 +520,9 @@ public class Antlib extends Task { | |||
| AntLibraryHandler(ClassLoader classloader, Properties als) { | |||
| this.classloader = classloader; | |||
| this.aliasMap = als; | |||
| this.symbols = project.getSymbols(); | |||
| } | |||
| /** | |||
| * Sets the DocumentLocator attribute of the AntLibraryHandler | |||
| * object | |||
| @@ -538,6 +533,35 @@ public class Antlib extends Task { | |||
| this.locator = locator; | |||
| } | |||
| private void parseAttributes(String tag, AttributeList attrs) | |||
| throws SAXParseException { | |||
| name = null; | |||
| className = null; | |||
| adapter = null; | |||
| for (int i = 0, last = attrs.getLength(); i < last; i++) { | |||
| String key = attrs.getName(i); | |||
| String value = attrs.getValue(i); | |||
| if (key.equals("name")) { | |||
| name = value; | |||
| } | |||
| else if (key.equals("class")) { | |||
| className = value; | |||
| } | |||
| else if ("role".equals(tag) && key.equals("adapter")) { | |||
| adapter = value; | |||
| } | |||
| else { | |||
| throw new SAXParseException("Unexpected attribute \"" | |||
| + key + "\"", locator); | |||
| } | |||
| } | |||
| if (name == null || className == null) { | |||
| String msg = "Underspecified " + tag + " declaration."; | |||
| throw new SAXParseException(msg, locator); | |||
| } | |||
| } | |||
| /** | |||
| * SAX callback handler | |||
| @@ -548,121 +572,105 @@ public class Antlib extends Task { | |||
| */ | |||
| public void startElement(String tag, AttributeList attrs) | |||
| throws SAXParseException { | |||
| level ++; | |||
| if ("antlib".equals(tag)) { | |||
| if (level > 1) { | |||
| throw new SAXParseException("Unexpected element: " + tag, | |||
| locator); | |||
| } | |||
| // No attributes to worry about | |||
| return; | |||
| } | |||
| if ("task".equals(tag) || "type".equals(tag)) { | |||
| String name = null; | |||
| String className = null; | |||
| for (int i = 0, last = attrs.getLength(); i < last; i++) { | |||
| String key = attrs.getName(i); | |||
| String value = attrs.getValue(i); | |||
| if (key.equals("name")) { | |||
| name = value; | |||
| } | |||
| else if (key.equals("class")) { | |||
| className = value; | |||
| } | |||
| else { | |||
| throw new SAXParseException("Unexpected attribute \"" | |||
| + key + "\"", locator); | |||
| } | |||
| } | |||
| if (name == null || className == null) { | |||
| String msg = "Underspecified " + tag + " declaration."; | |||
| throw new SAXParseException(msg, locator); | |||
| } | |||
| try { | |||
| //check for name alias | |||
| String alias = aliasMap.getProperty(name); | |||
| if (alias != null) { | |||
| name = alias; | |||
| } | |||
| //catch an attempted override of an existing name | |||
| if (!override && inUse(name)) { | |||
| String msg = "Cannot override " + tag + ": " + name; | |||
| log(msg, Project.MSG_WARN); | |||
| return; | |||
| } | |||
| //load the named class | |||
| Class cls; | |||
| if(classloader==null) { | |||
| cls=Class.forName(className); | |||
| } | |||
| else { | |||
| cls=classloader.loadClass(className); | |||
| } | |||
| //register it as a task or a datatype | |||
| if (tag.equals("task")) { | |||
| project.addTaskDefinition(name, cls); | |||
| } | |||
| else { | |||
| project.addDataTypeDefinition(name, cls); | |||
| } | |||
| } | |||
| catch (ClassNotFoundException cnfe) { | |||
| String msg = "Class " + className + | |||
| " cannot be found"; | |||
| throw new SAXParseException(msg, locator, cnfe); | |||
| } | |||
| catch (NoClassDefFoundError ncdfe) { | |||
| String msg = "Class " + className + | |||
| " cannot be found"; | |||
| throw new SAXParseException(msg, locator); | |||
| } | |||
| } | |||
| else { | |||
| throw new SAXParseException("Unexpected element \"" + | |||
| tag + "\"", | |||
| locator); | |||
| } | |||
| if (level == 1) { | |||
| throw new SAXParseException("Missing antlib root element", | |||
| locator); | |||
| } | |||
| // Must have the two attributes declared | |||
| parseAttributes(tag, attrs); | |||
| try { | |||
| if ("role".equals(tag)) { | |||
| if (isRoleInUse(name)) { | |||
| String msg = "Cannot override role: " + name; | |||
| log(msg, Project.MSG_WARN); | |||
| return; | |||
| } | |||
| // Defining a new role | |||
| symbols.addRole(name, loadClass(className), | |||
| (adapter == null? | |||
| null : loadClass(adapter))); | |||
| return; | |||
| } | |||
| // Defining a new element kind | |||
| //check for name alias | |||
| String alias = aliasMap.getProperty(name); | |||
| if (alias != null) { | |||
| name = alias; | |||
| } | |||
| //catch an attempted override of an existing name | |||
| if (!override && isInUse(tag, name)) { | |||
| String msg = "Cannot override " + tag + ": " + name; | |||
| log(msg, Project.MSG_WARN); | |||
| return; | |||
| } | |||
| symbols.add(tag, name, loadClass(className)); | |||
| } | |||
| catch(BuildException be) { | |||
| throw new SAXParseException(be.getMessage(), locator, be); | |||
| } | |||
| } | |||
| public void endElement(String tag) { | |||
| level--; | |||
| } | |||
| private Class loadClass(String className) | |||
| throws SAXParseException { | |||
| try { | |||
| //load the named class | |||
| Class cls; | |||
| if(classloader==null) { | |||
| cls=Class.forName(className); | |||
| } | |||
| else { | |||
| cls=classloader.loadClass(className); | |||
| } | |||
| return cls; | |||
| } | |||
| catch (ClassNotFoundException cnfe) { | |||
| String msg = "Class " + className + | |||
| " cannot be found"; | |||
| throw new SAXParseException(msg, locator, cnfe); | |||
| } | |||
| catch (NoClassDefFoundError ncdfe) { | |||
| String msg = "Class " + className + | |||
| " cannot be found"; | |||
| throw new SAXParseException(msg, locator); | |||
| } | |||
| } | |||
| /** | |||
| * test for a name being in use already | |||
| * test for a name being in use already on this role | |||
| * | |||
| * @param name the name to test | |||
| * @return true if it is a task or a datatype | |||
| */ | |||
| private boolean inUse(String name) { | |||
| return (project.getTaskDefinitions().get(name) != null || | |||
| project.getDataTypeDefinitions().get(name) != null); | |||
| private boolean isInUse(String role, String name) { | |||
| return (symbols.get(role, name) != null); | |||
| } | |||
| /** | |||
| * Recognizes the DTD declaration for antlib and returns the corresponding | |||
| * DTD definition from a resource. <P> | |||
| * | |||
| * To allow for future versions of the DTD format it will search | |||
| * for any DTDs of the form "Antlib-V.*\.dtd". | |||
| * test for a role name being in use already | |||
| * | |||
| * @param publicId public ID (ignored) | |||
| * @param systemId system ID (matched against) | |||
| * @return local DTD instance | |||
| * @param name the name to test | |||
| * @return true if it is a task or a datatype | |||
| */ | |||
| public InputSource resolveEntity(String publicId, | |||
| String systemId) { | |||
| log("Looking for entiry with PublicID=" + publicId + | |||
| " and SystemId=" + systemId, Project.MSG_VERBOSE); | |||
| if (matchDtdId(systemId)) { | |||
| String resId = systemId.substring(ANTLIB_DTD_URL.length()); | |||
| InputSource is = | |||
| new InputSource(this.getClass().getResourceAsStream(resId)); | |||
| is.setSystemId(systemId); | |||
| return is; | |||
| } | |||
| return null; | |||
| private boolean isRoleInUse(String name) { | |||
| return (symbols.getRole(name) != null); | |||
| } | |||
| //end inner class AntLibraryHandler | |||
| } | |||