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