Return-Path: Mailing-List: contact dev-help@ant.apache.org; run by ezmlm Delivered-To: mailing list dev@ant.apache.org Received: (qmail 31941 invoked from network); 14 Apr 2003 23:47:48 -0000 Received: from mail05.svc.cra.dublin.eircom.net (159.134.118.21) by daedalus.apache.org with SMTP; 14 Apr 2003 23:47:48 -0000 Received: (qmail 81567 messnum 261600 invoked from network[194.125.184.222/p184-222.as1.snd.dublin.eircom.net]); 14 Apr 2003 23:47:52 -0000 Received: from p184-222.as1.snd.dublin.eircom.net (194.125.184.222) by mail05.svc.cra.dublin.eircom.net (qp 81567) with SMTP; 14 Apr 2003 23:47:52 -0000 Subject: RE: DynamicTag From: peter reilly To: dev@ant.apache.org Content-Type: multipart/mixed; boundary="=-0MXqqbMLZSwXFgPqllWC" X-Mailer: Ximian Evolution 1.0.8 (1.0.8-10) Date: 15 Apr 2003 00:50:20 +0100 Message-Id: <1050364223.2332.112.camel@woodford> Mime-Version: 1.0 X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N --=-0MXqqbMLZSwXFgPqllWC Content-Type: text/plain Content-Transfer-Encoding: 7bit Hi, I am on holidays, but have followed this thread. I have been playing with DynamicTag and it is quite neat, but there are some issues with it.. I have made some modifications (well rewrite...) 1) DynamicTag uses UnknownElement. This part of ant code is undergoing a number of changes at the moment, and currently DynamicTag does not work with cvs ant. I extracted the stuff that unknownelement does and placed them in a method, this is compatible with apr12 ant cvs and with ant 1.5.x (tested with 1.5.2). 2) I would like to have dynamic tag behavior on the the current task as well as nested elements. 3) I would like to use reflection in the same way as ant uses reflection for setters and creators. 4) I would like to support multiple dynamic types in the same class. I initially considered using only datatypes, but on consideration it does make sense to have tasks as well. The problems with this are: a) tasks that do not extend ant.task get wrapped by ant.taskadapter which may not be what is expected b) tasks have a more complex life cycle that datatypes (init, perform, execute methods) Attached are two files : DynamicElementHelper and DynamicElementTask. Tasks and Datatypes/Sub-elements extend DynamicElementTask. The signature dynamicElement(Type) is used to indicate the type that this method will handle. DynamicElementTask implements DynamicConfigurator, and calls DynamicElementHelper, which looks up the tag name in the projects tasks and datatypes, if found that, the method uses reflection to see if the calling object has a matching dynamicElement method for the type. DynamicElementHelper is written so that if this idea is considered useful, it can be used by IntrospectionHelper, It that case, there would be no need for DynamicElementTask. To do the original example: BuildPath.java extends DynamicElementTask and adds a dynamic element handler: // Creates a nested resolver public void dynamicElement(BuildPathResolver resolver) { if (this.resolver != null) throw new BuildException("resolver already defined"); this.resolver = resolver; } And the xml filters: and in xml chain: public void dynamicElement(XmlFilter filter) { filters.addElement(filter) } One may also nest the custom filters in the same way as dynamictag by writing a new class public class CustomFilters extends DynamicElementTask { private Vector filters = new Vector(); public void dynamicElement(XmlFilter filter) { filters.addElement(filter); } public Vector getFilters() { return filters;} } and in XmlChain: public void addCustomFilters(CustomFilters customFilters) { this.customFilters = customFilters; } Cheers, Peter Dominique Devienne wrote: >>Two comments: >>1) DynamicTag is fully Ant 1.5.x compatible. No need for 1.6. Just use it >>along side your own classes, and you're good to go. >>2) DynamicTag *relies* on or (you can declare your >>custom extension either way), which takes care of all the classloading, >>already has all the special stuff like loaderref. Any enhancement to these >>tasks automatically benefit DynamicTag. --=-0MXqqbMLZSwXFgPqllWC Content-Disposition: attachment; filename=DynamicElementHelper.java Content-Transfer-Encoding: quoted-printable Content-Type: text/x-java; name=DynamicElementHelper.java; charset=UTF-8 package net.sf.antcontrib.util; 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; /** *

* 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. *

*

* The dynamic element classes will be defined by <typedef/> * or (to be decided) by <taskdef/> *

*

* User classes (tasks or datatypes) have methods * dynamicElement(class or interface) *

*

* This class is currently used by DynamicElementTask, but * may in the future be used by ant core IntrospectionHelper. *

*

* An example: Suppose one had a task buildpath that resolved * paths using custom resolvers that implement a BuildPathResolver * interface. *

*
 *  <typedef name=3D"tahoeresolver"
 *            classname=3D"acme.resolvers.TahoeResolver"
 *            classpath=3D"acme.classpath"/>
 *   <buildpath id=3D"tahoebuildpath">
 *     <tahoeresolver destdir=3D"${destdir}"
 *                    dependencies=3D"${dependencies}"=20
 *   </buildpath>
 * 
* * BuildPath would look something like this. *
 * public class BuildPathTask
 *    extends DynamicElementTask
 * {
 *     ...
 *     BuildPathResolver resolver;
 *     public void dynamicElement(BuildPathResolver resolver) {
 *         if (resolver !=3D null)
 *             throw new BuildException();
 *         resolver =3D resolver;
 *     }
 *     ....
 *     public void execute() {
 *        if (resolver =3D=3D null)
 *           throw new BuildException();
 *        buildPath =3D resolver.getBuildPath();
 *     }
 * }
 * 
*

* Note: *

*

x * createDynamicMethod should possible throw different * exceptions for the various error conditions: *

*
  • name not found
  • *
  • name found but no matching dynamic element method
  • *
    * At the moment the method simply returns null for these * conditions. *

    * * @author Peter Reilly */ public class DynamicElementHelper { private Vector nestedDynamicMethods =3D new Vector(); /** * Constructor for DynamicElementHelper. * * @param clazz the class to reflect over */ public DynamicElementHelper(Class clazz) { extractDynamicElements(clazz.getMethods()); } /** * Create a dynamic nested element from either the * task definitions or the data types defined in * the project. * This method also invokes the correct dynamicelement * 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 =3D=3D null) { throw new BuildException( "Project is null for dynamicElementHelper"); } if (nestedDynamicMethods.size() =3D=3D 0) return null; =20 // is element in task definitions Class elementClass =3D (Class) project.getTaskDefinitions().get(elementName); boolean isTask =3D (elementClass !=3D null); if (elementClass !=3D null) { if (! (Task.class.isAssignableFrom(elementClass))) { elementClass =3D TaskAdapter.class; } } else { // is element in data type definitions elementClass =3D (Class) project.getDataTypeDefinitions().get(elementName); } =20 if (elementClass =3D=3D null) { return null; } Method method =3D getMatchingMethod(elementClass); if (method =3D=3D null) return null; Object nestedObject =3D (isTask ? project.createTask(elementName) : project.createDataType(elementName)); if (nestedObject =3D=3D null) return null; // invoke the dynamic element method try { method.invoke(object, new Object[]{nestedObject}); } catch (InvocationTargetException ex) { Throwable realException =3D ex.getTargetException(); if (realException instanceof BuildException) throw (BuildException) realException; } catch (Throwable t) { throw new BuildException(t); } // If this is a task call the init method on it if (isTask) { ((Task) nestedObject).init(); } return nestedObject; } =20 /** * 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 =3D 0; i < methods.length; ++i) { Method method =3D methods[i]; Class[] args =3D method.getParameterTypes(); if (args.length !=3D 1) continue loop; if (! method.getName().equals("dynamicElement")) continue loop; for (int m =3D 0; m < nestedDynamicMethods.size(); ++m) { Method current =3D (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 =3D 0; i < nestedDynamicMethods.size(); ++i) { Method method =3D (Method) nestedDynamicMethods.elementAt(i); if (method.getParameterTypes()[0].isAssignableFrom(paramClass)) return method; } return null; } } --=-0MXqqbMLZSwXFgPqllWC Content-Disposition: attachment; filename=DynamicElementTask.java Content-Transfer-Encoding: quoted-printable Content-Type: text/x-java; name=DynamicElementTask.java; charset=UTF-8 package net.sf.antcontrib.util; import java.util.Vector; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DynamicConfigurator; import org.apache.tools.ant.RuntimeConfigurable; import org.apache.tools.ant.Task; import org.apache.tools.ant.UnknownElement; import org.apache.tools.ant.types.EnumeratedAttribute; import org.apache.tools.ant.taskdefs.condition.Condition; /** * Abstract class to use DynamicElementHelper to * use refection to find methods called dynamicElement. * This class implements DynamicConfiguator. * The class may be used as a basis for a task or a * type. */ public abstract class DynamicElementTask extends Task implements DynamicConfigurator { =20 private DynamicElementHelper dynamicElementHelper =3D null; public Object createDynamicElement(String name) { if (dynamicElementHelper =3D=3D null) { dynamicElementHelper =3D new DynamicElementHelper( this.getClass()); } Object ret =3D dynamicElementHelper.createDynamicElement( getProject(), this, name); if (ret =3D=3D null) throw new BuildException("Unknown Element " + name); return ret; } =20 public void setDynamicAttribute(String name, String value) { throw new BuildException("Unknown attribute " + name); } } --=-0MXqqbMLZSwXFgPqllWC--