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