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 &lt;typedef/&gt;
 * or <b>(to be decided)</b> by &lt;taskdef/&gt;
 * </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>
 *  &lt;typedef name="tahoeresolver"
 *            classname="acme.resolvers.TahoeResolver"
 *            classpath="acme.classpath"/&gt;
 *   &lt;buildpath id="tahoebuildpath"&gt;
 *     &lt;tahoeresolver destdir="${destdir}"
 *                    dependencies="${dependencies}" 
 *   &lt;/buildpath&gt;
 * </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;
    }
}

Reply via email to