On Friday 25 April 2003 12:24, Jose Alberto Fernandez wrote:
> Peter,
>
> this is exactly my point. For every new thingy that we add we now need to
> go and modify IntrospectionHelper or something to make special allowances
> for it.
The dynamicelement addition to IntrospectionHelper is general and new thingies
can be added without affecting core ant.
>
> It is bloating the core like mad and in my opinion it is crazy. We need a
> unified way to treat this things no matter what the things are. Ant's
> engine core should not need to know anything about anything.
Dynamicelement has the potential to remove code (e.g. all the
add<NAME> methods to conditionbase, selectorbase, and filterchain). The
only problem is name clashes.
>
> In an ideal world, we should have an engine core with no reference to any
> task/type or its implementing classes and a core-antlib which provides the
> classes and definintions for all the
> task/types/conditions/selectors/mappers that define core java.
This can be done with dynamicelement, except for name clashes and mapper
(which does not use sub-elements for different filenamemappers).
Peter.
Ps: I am including mods to IntrospectionHelper.java and
DynamicElementHelper.java
Index: IntrospectionHelper.java
===================================================================
RCS file: /home/cvspublic/ant/src/main/org/apache/tools/ant/IntrospectionHelper.java,v
retrieving revision 1.55
diff -u -r1.55 IntrospectionHelper.java
--- IntrospectionHelper.java 15 Apr 2003 17:23:15 -0000 1.55
+++ IntrospectionHelper.java 25 Apr 2003 12:14:09 -0000
@@ -61,6 +61,7 @@
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;
+import org.apache.tools.ant.helper.DynamicElementHelper;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.Path;
@@ -114,6 +115,11 @@
private Class bean;
/**
+ * The dynamic elements implemented by this class
+ */
+ private DynamicElementHelper dynamicElementHelper = null;
+
+ /**
* Helper instances we've already created (Class to IntrospectionHelper).
*/
private static Hashtable helpers = new Hashtable();
@@ -199,9 +205,10 @@
nestedTypes = new Hashtable();
nestedCreators = new Hashtable();
nestedStorers = new Hashtable();
+ dynamicElementHelper = new DynamicElementHelper(bean);
this.bean = bean;
-
+
Method[] methods = bean.getMethods();
for (int i = 0; i < methods.length; i++) {
final Method m = methods[i];
@@ -534,6 +541,15 @@
public Object createElement(Project project, Object parent,
String elementName) throws BuildException {
NestedCreator nc = (NestedCreator) nestedCreators.get(elementName);
+
+ if (nc == null) {
+ Object nestedElement = dynamicElementHelper.createDynamicElement(
+ project, parent, elementName);
+ if (nestedElement != null) {
+ return nestedElement;
+ }
+ }
+
if (nc == null && parent instanceof DynamicConfigurator) {
DynamicConfigurator dc = (DynamicConfigurator) parent;
Object nestedElement = dc.createDynamicElement(elementName);
@@ -578,7 +594,8 @@
*/
public boolean supportsNestedElement(String elementName) {
return nestedCreators.containsKey(elementName) ||
- DynamicConfigurator.class.isAssignableFrom(bean);
+ DynamicConfigurator.class.isAssignableFrom(bean) ||
+ dynamicElementHelper.hasDynamicMethods();
}
/**
// {ANT Apache Software License}
package org.apache.tools.ant.helper;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DynamicConfigurator;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.RuntimeConfigurable;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskAdapter;
import org.apache.tools.ant.UnknownElement;
/**
* <p>
* This class is used to help in handling dynamic elements.
* The idea is to allow easy custom extensions
* and to extend the traditional ant bean reflection to call setters
* methods, or add/create methods, with all the magic type conversion
* it does.
* </p>
* <p>
* The dynamic element classes will be defined by <typedef/>
* or <b>(to be decided)</b> by <taskdef/>
* </p>
* <p>
* User classes (tasks or datatypes) have methods
* <code>dynamicElement(class or interface)</code>
* </p>
* <p>
* This class is currently used by DynamicElementTask, but
* may in the future be used by ant core IntrospectionHelper.
* </p>
* <p>
* An example: Suppose one had a task buildpath that resolved
* paths using custom resolvers that implement a BuildPathResolver
* interface.
* </p>
* <pre>
* <typedef name="tahoeresolver"
* classname="acme.resolvers.TahoeResolver"
* classpath="acme.classpath"/>
* <buildpath id="tahoebuildpath">
* <tahoeresolver destdir="${destdir}"
* dependencies="${dependencies}"
* </buildpath>
* </pre>
*
* BuildPath would look something like this.
* <pre>
* public class BuildPathTask
* extends DynamicElementTask
* {
* ...
* BuildPathResolver resolver;
* public void dynamicElement(BuildPathResolver resolver) {
* if (resolver != null)
* throw new BuildException();
* resolver = resolver;
* }
* ....
* public void execute() {
* if (resolver == null)
* throw new BuildException();
* buildPath = resolver.getBuildPath();
* }
* }
* </pre>
* <p>
* <b>Note:</b>
* </p>
* <p>x
* createDynamicMethod should possible throw different
* exceptions for the various error conditions:
* <dl>
* <li>name not found</li>
* <li>name found but no matching dynamic element method</li>
* </dl>
* At the moment the method simply returns null for these
* conditions.
* </p>
*
* @author Peter Reilly
*/
public class DynamicElementHelper {
private Vector nestedDynamicMethods = new Vector();
/**
* Constructor for DynamicElementHelper.
*
* @param clazz the class to reflect over
*/
public DynamicElementHelper(Class clazz) {
extractDynamicElements(clazz.getMethods());
}
public boolean hasDynamicMethods() {
return nestedDynamicMethods.size() > 0;
}
/**
* Create a dynamic nested element from either the
* task definitions or the data types defined in
* the project.
* This method also invokes the correct <code>dynamicelement</code>
* method in object.
* @param project the project the task or datatype is in
* @param object the task or data type instance
* @param elementName the xml tag
* @return the created object.
*/
public Object createDynamicElement(
Project project, Object object, String elementName)
{
if (project == null) {
throw new BuildException(
"Project is null for dynamicElementHelper");
}
if (nestedDynamicMethods.size() == 0)
return null;
// is element in task definitions
Class elementClass =
(Class) project.getTaskDefinitions().get(elementName);
boolean isTask = (elementClass != null);
if (elementClass != null) {
if (! (Task.class.isAssignableFrom(elementClass))) {
elementClass = TaskAdapter.class;
}
} else {
// is element in data type definitions
elementClass =
(Class) project.getDataTypeDefinitions().get(elementName);
}
if (elementClass == null) {
return null;
}
Method method = getMatchingMethod(elementClass);
if (method == null)
return null;
Object nestedObject = (isTask
? project.createTask(elementName)
: project.createDataType(elementName));
if (nestedObject == null)
return null;
// invoke the dynamic element method
try {
method.invoke(object, new Object[]{nestedObject});
}
catch (InvocationTargetException ex) {
Throwable realException = ex.getTargetException();
if (realException instanceof BuildException)
throw (BuildException) realException;
}
catch (Throwable t) {
throw new BuildException(t);
}
project.setProjectReference(nestedObject);
// If this is a task call the init method on it
if (isTask) {
((Task) nestedObject).init();
}
return nestedObject;
}
/**
* Search the array of methods to find method names
* of "dynamicElement" with one parameter. Sort the
* methods so that derived classes are placed before
* their parents.
*/
private void extractDynamicElements(Method[] methods) {
loop:
for (int i = 0; i < methods.length; ++i) {
Method method = methods[i];
Class[] args = method.getParameterTypes();
if (args.length != 1)
continue loop;
if (! method.getName().equals("dynamicElement"))
continue loop;
for (int m = 0; m < nestedDynamicMethods.size(); ++m) {
Method current = (Method) nestedDynamicMethods.elementAt(m);
if (current.getParameterTypes()[0].isAssignableFrom(
method.getParameterTypes()[0]))
{
nestedDynamicMethods.insertElementAt(method, m);
continue loop;
}
}
nestedDynamicMethods.addElement(method);
}
}
/**
* Search the list of methods to find the first method
* that has a parameter that accepts the dynamic element object
*/
private Method getMatchingMethod(Class paramClass) {
for (int i = 0; i < nestedDynamicMethods.size(); ++i) {
Method method = (Method) nestedDynamicMethods.elementAt(i);
if (method.getParameterTypes()[0].isAssignableFrom(paramClass))
return method;
}
return null;
}
}