Hi, This is the second installment on the <antlib> proposal for ANT1.x. It contains the following:
- A patch file with changes for the files already in existance. - A zip file with new files to be added to proposal/sandbox/antlib/src With this installment, ProjectHelper and IntrospectionHelper have changed to use the new Role based mechanism for finding and construction tasks. And datatypes are created in such a manner that the type rules are not circumvented. In addition there is a testcase for testing <antjar> and <antlib>. The new code passes all the testcases defined for ANT except for two: 1) A test where the expected error message is now different. 2) One of the tests for the <description> element. Which cannot be used the way it was because the special rules it was using now are more strict and consistent. See discussion later. I still think I can do further cleanup of the code and eliminate more of the special code in ProjectHelper but that would be as part of the third installment. Can one of the committers try a GUMP run using this version so as to find out any broken things? The problem with <description>: --------------------------------------- <description> elements as direct children of <project> work fine as expected. What it works differently are <description> elements inside <target>. The way <description> was implemented, nodes inside <target> where added to the Project description during instantiation of the project. However, nodes inside tasks implementing TaskContainer were not taken into consideration. Now all TaskContainers are treated equally, including Target, hence the discrepancy. There are several ways of resolve the issue: 1) Make <description> only available at <project> level. 2) Change implementation of <description> so that is treated as part of the syntax of <project> and <target> instead of using a <datatype> that works by sideeffect. We can always keep the task arround for backward compatibility purposes. Jose Alberto
Index: proposal/sandbox/antlib/build.xml
===================================================================
RCS file: /home/cvspublic/jakarta-ant/proposal/sandbox/antlib/build.xml,v
retrieving revision 1.1
diff -u -r1.1 build.xml
--- proposal/sandbox/antlib/build.xml 11 Feb 2002 03:39:00 -0000 1.1
+++ proposal/sandbox/antlib/build.xml 17 Feb 2002 03:00:07 -0000
@@ -6,6 +6,7 @@
<property name='build' location='build' />
<property name='dist' location='dist' />
<property name='classes' location='${build}/classes' />
+ <property name='testcases' location='src/testcases' />
<property name="debug" value="true" />
<property name="deprecation" value="false" />
@@ -19,6 +20,8 @@
<fileset dir='${orig-classes}'>
<include name='**' />
<exclude name='org/apache/tools/ant/Project.class' />
+ <exclude name='org/apache/tools/ant/ProjectHelper.class' />
+ <exclude name='org/apache/tools/ant/IntrospectionHelper.class' />
<exclude name='org/apache/tools/ant/TaskAdapter.class' />
<exclude name='org/apache/tools/ant/taskdefs/Ant.class' />
</fileset>
@@ -52,6 +55,12 @@
<target name='clean'>
<delete dir='${build}' />
+ </target>
+
+ <target name='test'>
+ <ant dir='${testcases}' inheritAll='false'/>
+ <ant dir='${testcases}'
+ antfile='${testcases}/case.xml' inheritAll='false'/>
</target>
<target name='cleanall' depends='clean'>
Index: proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java,v
retrieving revision 1.1
diff -u -r1.1 Project.java
--- proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java 11 Feb
2002 03:39:00 -0000 1.1
+++ proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java 17 Feb
2002 03:00:13 -0000
@@ -63,12 +63,15 @@
import java.util.Enumeration;
import java.util.Stack;
import java.lang.reflect.Modifier;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
import org.apache.tools.ant.types.DataTypeAdapterTask;
import org.apache.tools.ant.types.FilterSet;
import org.apache.tools.ant.types.FilterSetCollection;
import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.types.Path;
/**
* Central representation of an Ant project. This class defines a
@@ -90,6 +93,9 @@
public final static int MSG_VERBOSE = 3;
public final static int MSG_DEBUG = 4;
+ public final static String TASK_ROLE = "task";
+ public final static String DATATYPE_ROLE = "datatype";
+
// private set of constants to represent the state
// of a DFS of the Target dependencies
private final static String VISITING = "VISITING";
@@ -165,30 +171,26 @@
fileUtils = FileUtils.newFileUtils();
symbols = new SymbolTable();
symbols.setProject(this);
+ loadDefinitions();
}
/**
* create a new ant project that inherits from caler project
* @param p the calling project
*/
- public Project(Project p) {
+ private Project(Project p) {
fileUtils = FileUtils.newFileUtils();
- symbols = new SymbolTable(p);
+ symbols = new SymbolTable(p.getSymbols());
symbols.setProject(this);
}
/**
- * Initialise the project.
- *
- * This involves setting the default task definitions and loading the
- * system properties.
+ * Loads the core definitions into the Root project.
*/
- public void init() throws BuildException {
- setJavaVersionProperty();
-
- // Initialize simbol table just in case
- symbols.addRole("task", TaskContainer.class, TaskAdapter.class);
- symbols.addRole("datatype", TaskContainer.class,
+ private void loadDefinitions() {
+ // Initialize symbol table just in case
+ symbols.addRole(TASK_ROLE, TaskContainer.class, TaskAdapter.class);
+ symbols.addRole(DATATYPE_ROLE, TaskContainer.class,
DataTypeAdapterTask.class);
String defs = "/org/apache/tools/ant/taskdefs/defaults.properties";
@@ -248,7 +250,23 @@
} catch (IOException ioe) {
throw new BuildException("Can't load default datatype list");
}
+ }
+ /**
+ * Creates a subproject of the current project.
+ */
+ public Project createSubProject() {
+ return new Project(this);
+ }
+
+ /**
+ * Initialise the project.
+ *
+ * This involves setting the default task definitions and loading the
+ * system properties.
+ */
+ public void init() throws BuildException {
+ setJavaVersionProperty();
setSystemProperties();
}
@@ -275,7 +293,7 @@
/**
* Get the symbols associated with this project.
*/
- public SymbolTable getSymbols() {
+ private SymbolTable getSymbols() { // Package protected on purpose
return symbols;
}
@@ -618,6 +636,46 @@
}
}
+ public ClassLoader addToLoader(String loader, Path path) {
+ return symbols.addToLoader(loader, path);
+ }
+
+ public boolean addRoleDefinition(String role,
+ Class roleClass, Class adapter)
+ {
+ return symbols.addRole(role, roleClass, adapter);
+ }
+
+ /**
+ * test for a role name being in use already
+ *
+ * @param name the name to test
+ * @return true if it is a task or a datatype
+ */
+ public boolean isRoleDefined(String name) {
+ return (symbols.getRole(name) != null);
+ }
+
+ public void addDefinitionOnRole(String role,
+ String type, Class clz)
+ {
+ Class old = symbols.add(role, type, clz);
+ // Special management for Tasks
+ if (TASK_ROLE.equals(role) && null != old && !old.equals(clz)) {
+ invalidateCreatedTasks(type);
+ }
+ }
+
+ /**
+ * 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
+ */
+ public boolean isDefinedOnRole(String role, String name) {
+ return (symbols.get(role, name) != null);
+ }
+
/**
* add a new task definition, complain if there is an overwrite attempt
* @param taskName name of the task
@@ -627,21 +685,14 @@
*/
public void addTaskDefinition(String taskName, Class taskClass)
throws BuildException {
- Class old = symbols.add("task", taskName, taskClass);
- if (null != old && !old.equals(taskClass)) {
- invalidateCreatedTasks(taskName);
- }
-
- String msg =
- " +User task: " + taskName + " " + taskClass.getName();
- log(msg, MSG_DEBUG);
- checkTaskClass(taskClass);
+ addDefinitionOnRole(TASK_ROLE, taskName, taskClass);
}
/**
* Checks a class, whether it is suitable for serving as ant task.
* @throws BuildException and logs as Project.MSG_ERR for
* conditions, that will cause the task execution to fail.
+ * @deprecated this is done now when added to SymbolTable
*/
public void checkTaskClass(final Class taskClass) throws BuildException {
if( !Task.class.isAssignableFrom(taskClass) ) {
@@ -653,7 +704,7 @@
* get the current task definition hashtable
*/
public Hashtable getTaskDefinitions() {
- return symbols.getTaskDefinitions();
+ return symbols.getDefinitions(TASK_ROLE);
}
/**
@@ -662,18 +713,14 @@
* @param typeClass full datatype classname
*/
public void addDataTypeDefinition(String typeName, Class typeClass) {
- symbols.add("datatype", typeName, typeClass);
-
- String msg =
- " +User datatype: " + typeName + " " + typeClass.getName();
- log(msg, MSG_DEBUG);
+ addDefinitionOnRole(DATATYPE_ROLE, typeName, typeClass);
}
/**
* get the current task definition hashtable
*/
public Hashtable getDataTypeDefinitions() {
- return symbols.getDataTypeDefinitions();
+ return symbols.getDefinitions(DATATYPE_ROLE);
}
/**
@@ -701,7 +748,7 @@
* in the project.
* @see Project#addOrReplaceTarget to replace existing Targets.
*/
- public void addTarget(String targetName, Target target)
+ public void addTarget(String targetName, Target target)
throws BuildException {
if (targets.get(targetName) != null) {
throw new BuildException("Duplicate target: `"+targetName+"'");
@@ -738,45 +785,95 @@
}
/**
+ * Create a new element instance on a Role
+ * @param role name of the role to use
+ * @param type name of the element to create
+ * @return null if element unknown on this role
+ */
+ public Object createForRole(String role, String type) {
+ SymbolTable.Factory f = symbols.get(role, type);
+ if (f == null) return null;
+
+ try {
+ Object o = f.create(this);
+ // Do special book keeping for ProjectComponents
+ if ( o instanceof ProjectComponent ) {
+ ((ProjectComponent)o).setProject(this);
+ if (o instanceof Task) {
+ Task task = (Task) o;
+ task.setTaskType(type);
+
+ // set default value, can be changed by the user
+ task.setTaskName(type);
+ addCreatedTask(type, task);
+ }
+ }
+ String msg = " +" + role + ": " + type;
+ log (msg, MSG_DEBUG);
+ return o;
+ }
+ catch (Throwable t) {
+ String msg = "Could not create " + role + " of type: "
+ + type + " due to " + t;
+ throw new BuildException(msg, t);
+ }
+ }
+
+ /**
+ *
+ */
+ public Object createInRole(Object container, String type) {
+ Class clz = container.getClass();
+ String roles[] = symbols.findRoles(clz);
+ Object theOne = null;
+ Method add = null;
+
+ for(int i = 0; i < roles.length; i++) {
+ Object o = createForRole(roles[i], type);
+ if (o != null) {
+ if (theOne != null) {
+ String msg = "Element " + type +
+ " is ambiguous for container " + clz.getName();
+ if (theOne instanceof RoleAdapter)
+ theOne = ((RoleAdapter)theOne).getProxy();
+ if (o instanceof RoleAdapter)
+ o = ((RoleAdapter)o).getProxy();
+
+ log(msg, MSG_ERR);
+ log("cannot distinguish between " +
+ theOne.getClass().getName() +
+ " and " + o.getClass().getName(), MSG_ERR);
+ throw new BuildException(msg);
+ }
+ theOne = o;
+ add = symbols.getRole(roles[i]).getInterfaceMethod();
+ }
+ }
+ if (theOne != null) {
+ try {
+ add.invoke(container, new Object[]{theOne});
+ }
+ catch(InvocationTargetException ite) {
+ if (ite.getTargetException() instanceof BuildException) {
+ throw (BuildException)ite.getTargetException();
+ }
+ throw new BuildException(ite.getTargetException());
+ }
+ catch(Exception e) {
+ throw new BuildException(e);
+ }
+ }
+ return theOne;
+ }
+
+ /**
* create a new task instance
* @param taskType name of the task
* @throws BuildException when task creation goes bad
* @return null if the task name is unknown
*/
public Task createTask(String taskType) throws BuildException {
- Class c = symbols.get("task", taskType);
-
- if (c == null) {
- return null;
- }
-
- try {
- Object o = c.newInstance();
- Task task = null;
- if( o instanceof Task ) {
- task=(Task)o;
- } else {
- // "Generic" Bean - use the setter pattern
- // and an Adapter
- TaskAdapter taskA=new TaskAdapter();
- taskA.setProxy( o );
- task=taskA;
- }
- task.setProject(this);
- task.setTaskType(taskType);
-
- // set default value, can be changed by the user
- task.setTaskName(taskType);
-
- String msg = " +Task: " + taskType;
- log (msg, MSG_DEBUG);
- addCreatedTask(taskType, task);
- return task;
- } catch (Throwable t) {
- String msg = "Could not create task of type: "
- + taskType + " due to " + t;
- throw new BuildException(msg, t);
- }
+ return (Task) createForRole(TASK_ROLE, taskType);
}
/**
@@ -820,47 +917,11 @@
* @return null if the datatype name is unknown
*/
public Object createDataType(String typeName) throws BuildException {
- Class c = symbols.get("datatype", typeName);
-
- if (c == null) {
- return null;
- }
-
- try {
- java.lang.reflect.Constructor ctor = null;
- boolean noArg = false;
- // DataType can have a "no arg" constructor or take a single
- // Project argument.
- try {
- ctor = c.getConstructor(new Class[0]);
- noArg = true;
- } catch (NoSuchMethodException nse) {
- ctor = c.getConstructor(new Class[] {Project.class});
- noArg = false;
- }
-
- Object o = null;
- if (noArg) {
- o = ctor.newInstance(new Object[0]);
- } else {
- o = ctor.newInstance(new Object[] {this});
- }
- if (o instanceof ProjectComponent) {
- ((ProjectComponent)o).setProject(this);
- }
- String msg = " +DataType: " + typeName;
- log (msg, MSG_DEBUG);
- return o;
- } catch (java.lang.reflect.InvocationTargetException ite) {
- Throwable t = ite.getTargetException();
- String msg = "Could not create datatype of type: "
- + typeName + " due to " + t;
- throw new BuildException(msg, t);
- } catch (Throwable t) {
- String msg = "Could not create datatype of type: "
- + typeName + " due to " + t;
- throw new BuildException(msg, t);
- }
+ // This is to make the function backward compatible
+ // Since we know if it returning an adapter for it
+ DataTypeAdapterTask dt =
+ (DataTypeAdapterTask) createForRole(DATATYPE_ROLE, typeName);
+ return (dt != null? dt.getProxy() : null);
}
/**
@@ -1227,7 +1288,10 @@
}
public void addReference(String name, Object value) {
- if (null != references.get(name)) {
+ Object o = references.get(name);
+ if (null != o && o != value
+ && (!(o instanceof RoleAdapter)
+ || ((RoleAdapter)o).getProxy() != value)) {
log("Overriding previous definition of reference to " + name,
MSG_WARN);
}
Index: proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java,v
retrieving revision 1.1
diff -u -r1.1 RoleAdapter.java
--- proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java
11 Feb 2002 03:39:00 -0000 1.1
+++ proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java
17 Feb 2002 03:00:13 -0000
@@ -56,6 +56,11 @@
public interface RoleAdapter {
/**
+ * Obtain the id in case it is needed.
+ */
+ public void setId(String id);
+
+ /**
* Set the object being adapted.
* @param o the object being adapted
*/
Index: proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java,v
retrieving revision 1.1
diff -u -r1.1 SymbolTable.java
--- proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java
11 Feb 2002 03:39:00 -0000 1.1
+++ proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java
17 Feb 2002 03:00:15 -0000
@@ -54,6 +54,7 @@
package org.apache.tools.ant;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
@@ -96,8 +97,8 @@
* from that defined in the calling Project.
* @param p the calling project
*/
- public SymbolTable(Project p) {
- parentTable = p.getSymbols();
+ public SymbolTable(SymbolTable st) {
+ parentTable = st;
}
/**
@@ -109,6 +110,54 @@
}
/**
+ * 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;
+ }
+
+ /**
* Find all the roles supported by a Class
* on this symbol table.
* @param clz the class to analyze
@@ -133,13 +182,13 @@
list.addElement(role);
}
}
- if (parentTable != null) findRoles(clz, list);
+ if (parentTable != null) parentTable.findRoles(clz, list);
}
/**
* Get the Role definition
* @param role the name of the role
- * @return the method used to support objects on this role
+ * @return the Role description
*/
public Role getRole(String role) {
Role r = (Role) roles.get(role);
@@ -171,112 +220,6 @@
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.
@@ -291,13 +234,13 @@
throw new BuildException("Unknown role: " + role);
}
// Check if it is already defined
- Class old = get(role, name);
+ Factory old = get(role, name);
if (old != null) {
- if (old.equals(clz)) {
+ if (old.getOriginalClass().equals(clz)) {
project.log("Ignoring override for "+ role + " " + name
+ ", it is already defined by the same class.",
project.MSG_VERBOSE);
- return old;
+ return old.getOriginalClass();
}
else {
project.log("Trying to override old definition of " +
@@ -305,26 +248,33 @@
project.MSG_WARN);
}
}
- checkClass(clz);
+ Factory f = checkClass(clz);
// Check that the Class is compatible with the role definition
- r.verifyAdaptability(role, clz);
+ f = r.verifyAdaptability(role, f);
// 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;
+ defTable.put(name, f);
+
+ String msg =
+ " +User " + role + ": " + name + " " + clz.getName();
+ project.log(msg, project.MSG_DEBUG);
+ return (old != null ? old.getOriginalClass() : null);
}
/**
* Checks a class, whether it is suitable for serving in ANT.
+ * @return the factory to use when instantiating the class
* @throws BuildException and logs as Project.MSG_ERR for
* conditions, that will cause execution to fail.
*/
- void checkClass(final Class clz)
+ Factory checkClass(final Class clz) // Package on purpose
throws BuildException {
+ if (clz == null) return null;
+
if(!Modifier.isPublic(clz.getModifiers())) {
final String message = clz + " is not public";
project.log(message, Project.MSG_ERR);
@@ -342,8 +292,37 @@
// getConstructor finds public constructors only.
try {
clz.getConstructor(new Class[0]);
+ return new Factory(){
+ public Object create(Project p) {
+ try {
+ return clz.newInstance();
+ }
+ catch(Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ public Class getOriginalClass() {
+ return clz;
+ }
+ };
} catch (NoSuchMethodException nse) {
- clz.getConstructor(new Class[] {Project.class});
+ final Constructor c =
+ clz.getConstructor(new Class[] {Project.class});
+ return new Factory(){
+ public Object create(Project p) {
+ try {
+ return c.newInstance(new Object[]{p});
+ }
+ catch(Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ public Class getOriginalClass() {
+ return clz;
+ }
+ };
}
} catch(NoSuchMethodException e) {
final String message =
@@ -359,11 +338,11 @@
* @param name the name of the element to sea
* @return the Class implementation
*/
- public Class get(String role, String name) {
+ public Factory 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;
+ Factory f = (Factory)defTable.get(name);
+ if (f != null) return f;
}
if (parentTable != null) {
return parentTable.get(role, name);
@@ -372,19 +351,12 @@
}
/**
- * Get a Hashtable that is usable for manipulating Tasks,
+ * Get a Hashtable that is usable for manipulating elements on Role.
+ * @param role the role of the elements in the table
* @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 getDefinitions(String role) { // package scope on purpose
+ return new SymbolHashtable(role);
}
/**
@@ -402,16 +374,43 @@
}
public synchronized Object get(Object key) {
- return SymbolTable.this.get(role, (String)key);
+ Factory f = SymbolTable.this.get(role, (String)key);
+ return (f == null? null : f.getOriginalClass());
}
}
/**
+ * Factory for creating ANT objects.
+ * Class objects are not instanciated directly but through a Factory
+ * which is able to resolve issues such as proxys and such.
+ */
+ public static interface Factory {
+ /**
+ * Creates an object for the Role
+ * @param the project in which it is created
+ * @return the instantiated object with a proxy if necessary
+ */
+ public Object create(Project p);
+
+ /**
+ * Creates an object for the Role, adapted if necessary
+ * for a particular interface.
+ */
+// public Object adaptFor(Class clz, Project p, Object o);
+
+ /**
+ * The original class of the object without proxy.
+ */
+ public Class getOriginalClass();
+ }
+
+ /**
* The definition of a role
*/
public class Role {
private Method interfaceMethod;
private Method adapterVerifier;
+ private Factory adapterFactory;
/**
* Creates a new Role object
@@ -420,6 +419,7 @@
*/
Role(Class roleClz, Class adapterClz) {
interfaceMethod = validInterface(roleClz);
+ adapterFactory = checkClass(adapterClz);
adapterVerifier = validAdapter(adapterClz, interfaceMethod);
}
@@ -433,12 +433,11 @@
/**
* Instantiate a new adapter for this role.
*/
- public RoleAdapter createAdapter() {
- if (adapterVerifier == null) return null;
+ public RoleAdapter createAdapter(Project p) {
+ if (adapterFactory == null) return null;
try {
- return (RoleAdapter)
- adapterVerifier.getDeclaringClass().newInstance();
+ return (RoleAdapter) adapterFactory.create(p);
}
catch(BuildException be) {
throw be;
@@ -451,11 +450,12 @@
/**
* 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
+ * @param f the factory for the class to verify
*/
- public void verifyAdaptability(String role, Class clz) {
+ public Factory verifyAdaptability(String role, final Factory f) {
+ final Class clz = f.getOriginalClass();
if (interfaceMethod.getParameterTypes()[0].isAssignableFrom(clz)) {
- return;
+ return f;
}
if (adapterVerifier == null) {
String msg = "Class " + clz.getName() +
@@ -464,8 +464,18 @@
}
try {
try {
- adapterVerifier.invoke(null,
- new Object[]{clz, project});
+ adapterVerifier.invoke(null, new Object[]{clz, project});
+ return new Factory(){
+ public Object create(Project p) {
+ RoleAdapter ra = createAdapter(p);
+ ra.setProxy(f.create(p));
+ return ra;
+ }
+
+ public Class getOriginalClass() {
+ return clz;
+ }
+ };
}
catch (InvocationTargetException ite) {
throw ite.getTargetException();
@@ -487,5 +497,63 @@
public boolean isImplementedBy(Class clz) {
return interfaceMethod.getDeclaringClass().isAssignableFrom(clz);
}
+
+ /**
+ * 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;
+
+ 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);
+ }
+ }
+
}
}
Index: proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java,v
retrieving revision 1.1
diff -u -r1.1 TaskAdapter.java
--- proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java
11 Feb 2002 03:39:00 -0000 1.1
+++ proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java
17 Feb 2002 03:00:16 -0000
@@ -165,4 +165,5 @@
return this.proxy ;
}
+ public void setId(String id) {}
}
Index: proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java,v
retrieving revision 1.1
diff -u -r1.1 Ant.java
--- proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java
11 Feb 2002 03:39:00 -0000 1.1
+++ proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java
17 Feb 2002 03:00:18 -0000
@@ -138,10 +138,8 @@
}
public void init() {
- newProject = new Project(project);
+ newProject = project.createSubProject();
newProject.setJavaVersionProperty();
-// newProject.addTaskDefinition("property",
-//
(Class)project.getTaskDefinitions().get("property"));
}
private void reinit() {
@@ -184,26 +182,6 @@
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;
Index:
proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java,v
retrieving revision 1.2
diff -u -r1.2 Antlib.java
--- proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java
11 Feb 2002 03:39:00 -0000 1.2
+++ proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java
17 Feb 2002 03:00:21 -0000
@@ -418,7 +418,7 @@
if (classpath != null) {
clspath.append(classpath);
}
- return project.getSymbols().addToLoader(loaderId, clspath);
+ return project.addToLoader(loaderId, clspath);
}
@@ -505,8 +505,6 @@
private int level = 0;
- private SymbolTable symbols = null;
-
private String name = null;
private String className = null;
private String adapter = null;
@@ -520,7 +518,6 @@
AntLibraryHandler(ClassLoader classloader, Properties als) {
this.classloader = classloader;
this.aliasMap = als;
- this.symbols = project.getSymbols();
}
/**
@@ -591,15 +588,15 @@
try {
if ("role".equals(tag)) {
- if (isRoleInUse(name)) {
+ if (project.isRoleDefined(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)));
+ project.addRoleDefinition(name, loadClass(className),
+ (adapter == null?
+ null : loadClass(adapter)));
return;
}
@@ -610,12 +607,12 @@
name = alias;
}
//catch an attempted override of an existing name
- if (!override && isInUse(tag, name)) {
+ if (!override && project.isDefinedOnRole(tag, name)) {
String msg = "Cannot override " + tag + ": " + name;
log(msg, Project.MSG_WARN);
return;
}
- symbols.add(tag, name, loadClass(className));
+ project.addDefinitionOnRole(tag, name, loadClass(className));
}
catch(BuildException be) {
throw new SAXParseException(be.getMessage(), locator, be);
@@ -650,26 +647,6 @@
throw new SAXParseException(msg, locator);
}
}
-
- /**
- * 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 isInUse(String role, String name) {
- return (symbols.get(role, name) != null);
- }
-
- /**
- * test for a role name being in use already
- *
- * @param name the name to test
- * @return true if it is a task or a datatype
- */
- private boolean isRoleInUse(String name) {
- return (symbols.getRole(name) != null);
- }
//end inner class AntLibraryHandler
}
Index:
proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java,v
retrieving revision 1.1
diff -u -r1.1 DataTypeAdapterTask.java
---
proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java
11 Feb 2002 07:00:06 -0000 1.1
+++
proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java
17 Feb 2002 03:00:22 -0000
@@ -66,6 +66,7 @@
public class DataTypeAdapterTask extends Task implements RoleAdapter {
Object proxy;
+ String id = null;
/**
* Checks a class, whether it is suitable to be adapted.
@@ -83,14 +84,27 @@
* Do the execution.
*/
public void execute() throws BuildException {
+ if (id != null) {
+ // Need to re-register this reference
+ // The container has register the Adapter instead
+ project.addReference(id, proxy);
+ }
+ }
+
+ /**
+ * Propagate configuration of Project
+ */
+ public void setProject(Project p) {
+ super.setProject(p);
+
// Check to see if the DataType has a setProject method to set
if (proxy instanceof ProjectComponent) {
- ((ProjectComponent)proxy).setProject(project);
+ ((ProjectComponent)proxy).setProject(p);
return;
}
// This may not be needed
- // We are trying to set project even it is was not declared
+ // We are trying to set project even if is was not declared
// just like TaskAdapter does for beans, this is not done
// by the original code
Method setProjectM = null;
@@ -99,7 +113,7 @@
setProjectM =
c.getMethod( "setProject", new Class[] {Project.class});
if(setProjectM != null) {
- setProjectM.invoke(proxy, new Object[] {project});
+ setProjectM.invoke(proxy, new Object[] {p});
}
} catch (NoSuchMethodException e) {
// ignore this if the class being used as a task does not have
@@ -122,4 +136,8 @@
return this.proxy ;
}
+ public void setId(String id) {
+ log("Setting adapter id to: " + id, Project.MSG_DEBUG);
+ this.id = id;
+ }
}
<<attachment: sandbox-antlib.zip>>
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>
