After much head scratching, I came up with a solution to my own little Ant extension problem (about which I complained about a few months back). Basically, I first had to use Parametrizable to enable the extension point, like other Ant types:
<buildpath ident="buildpath" resolverClassname="....TahoeBuildPathResolver"> <param name="destdir" value="${destdir}" /> <param name="dependencies" value="${dependencies}" /> </buildpath> The solution was the introduction of a DynamicTag class which uses both DynamicConfigurator and UnknownElement. I can now define my extension point (which implements an interface/extension point only my datatype cares about) using a <typedef>, and then use it (and configure it as any other task) like so: <buildpath id="tahoebuildpath"> <resolver> <tahoeresolver destdir="${destdir}" dependencies="${dependencies}" </resolver> </buildpath> BuildPath.java adds a nested creator: /** * Creates a nested resolver element to contain a user-defined * resolver dynamically parameterized thanks to DynamicTag. */ public Object createResolver() { return _dynaResolver = new DynamicTag(BuildPathResolver.class); } And uses it like so when declared: if (_dynaResolver != null) { BuildPathResolver resolver; resolver = (BuildPathResolver)_dynaResolver.getTag(0); return resolver.getBuildPath(); } DynamicTag does the magic, i.e. ensure it contains as many nested elements as specified (default to requiring exactly one element), and also ensures that these elements are of the correct type (i.e. assigneable to the specified class/interface). This always the extension point to be configured like any other Ant task/type, including proper conversion to File, int, etc... My only concern is the lack of namespaces (not the XML type) for all the declarations of tasks/types/custom extension points. I initially coded a role manager class, but would have had to modify Ant core in many places to use it, so reverted to subverting the existing type system of Ant. This extension point mechanism could be used in place of Peter's patch to enable custom selector/mappers/etc... I believe. Let me all know what you think of it. --DD // vim:ts=4:sw=4 package com.lgc.buildmagic; import java.util.List; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; import org.apache.tools.ant.Task; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.UnknownElement; import org.apache.tools.ant.RuntimeConfigurable; import org.apache.tools.ant.DynamicConfigurator; /** * Tag allowing creation of arbitrary bean/type/tasks registered with Ant * using either a taskdef or typedef. * * @author <a href="mailto:[EMAIL PROTECTED]">Dominique Devienne</a> * @version Mar 2003 - Copyright (c) 2002, Landmark Graphics Corp. */ public class DynamicTag extends Task implements DynamicConfigurator { /** List of tags actually instantiated. */ private List _tags; /** Class(es) this/these dynamic tag(s) must be compatible with. */ private List _types; /** Minimum number of dynamic tags to create. Defaults to 1. */ private int _atLeast = 1; /** Maximum number of dynamic tags to create. Defaults to 1. */ private int _atMost = 1; /** * Instantiates a new dynamic tag that accepts only one arbitrary Object. */ public DynamicTag() { this(null); } /** * Instantiates a new dynamic tag that accepts only one given Object * assignable to the given required type. * * @param requiredType the class this dynamic tag's only child must * be compatible with, in the [EMAIL PROTECTED] Class#isAssignableWith} * sense. Can be <code>null</code>. */ public DynamicTag(Class requiredType) { if (requiredType != null) { _types = new ArrayList(1); _types.add(requiredType); } } /** * Instantiates a new dynamic tag that accepts Objects * assignable to the given required types. * * @param requiredTypes the classes this dynamic tag's only child must * be compatible with, in the [EMAIL PROTECTED] Class#isAssignableWith} * sense. Can be <code>null</code>. * @param atLeast the minimum number of beans/types/tasks this * dynamic element should contain. Must not be negative, * or strictly greater than <code>atMost</code>. * Use <code>0</code> (zero) for no lower bound. * @param atMost the maximum number of beans/types/tasks this * dynamic element should contain. Must not be negative, zero, * or strictly smaller than <code>atLeast</code>. * Use [EMAIL PROTECTED] Integer#MAX_INT} for no upper bound. */ public DynamicTag(Class[] requiredTypes, int atLeast, int atMost) { if (atLeast < 0 || atLeast > atMost || atMost == 0) { throw new IllegalArgumentException("Invalid min/max: " + atLeast + '/' + atMost); } if (requiredTypes != null && requiredTypes.length > 0) { _types = Arrays.asList(requiredTypes); } _atLeast = atLeast; _atMost = atMost; } // Implements DynamicConfigurator#setDynamicAttribute public void setDynamicAttribute(String name, String value) throws BuildException { throw new BuildException("Unknown attribute: " + name); } // Implements DynamicConfigurator#createDynamicElement public Object createDynamicElement(String name) throws BuildException { return new MyUnknownElement(name); } /** * Gets the instantiated bean/type/task at the given. * * @param index the index requested. * @throws IndexOutOfBoundsException if no tags were instantiated; * or the given <code>index</code> is invalid. */ public Object getTag(int index) { if (_tags == null) { throw new IndexOutOfBoundsException("no tags"); } assert _tags.size() >= _atLeast; assert _tags.size() <= _atMost; return _tags.get(index); } /** * Gets all the tags instantiated within this dynamic tag. * * @return the unmodifiable, and possibly empty, list of tags. */ public List getTags() { if (_tags == null) { return Collections.EMPTY_LIST; } return Collections.unmodifiableList(_tags); } /** * Checks to see whether the instantiated bean/type/tag is valid. * <p> * This particular implementation checks the instantiated bean/type/tag * is assigment-compatible with the classes provided as arguments to * this dynamic tag's constructors. * <p> * Derived classes should augment instead of bypass the checks performed * by this method, by first calling <code>super.assertValidTag(tag);</code> * * @param tag the bean/type/tag to check. * @throws BuildException if the bean/type/tag is invalid. */ protected void assertValidTag(Object tag) throws BuildException { if (_types == null || _types.size() < 1) { return; } Class tagClass = tag.getClass(); for (int i = 0; i < _types.size(); ++i) { Class type = (Class)_types.get(i); if (!type.isAssignableFrom(tagClass)) { throw new BuildException(tagClass + " not assignment-compatible with " + type); } } } /** * Adds the given instantiated bean/type/task to this dynamic tag. */ private void addTag(Object realThing) { assertValidTag(realThing); if (_tags == null) { _tags = new ArrayList(Math.min(16, _atMost)); } _tags.add(realThing); } // Intercept maybe configure to check min/max bounds. public void maybeConfigure() throws BuildException { super.maybeConfigure(); if (_atLeast > 0 && (_tags == null || _tags.size() < 1)) { throw new BuildException("Too few elements: <" + _atLeast); } if (_tags.size() > _atMost) { throw new BuildException("Too many elements: >" + _atMost); } } /** * UnknownElement extension to intercept the bean/type/task * actually instantiated by UnknownElement. * * @author <a href="mailto:[EMAIL PROTECTED]">Dominique Devienne</a> * @version Mar 2003 - Copyright (c) 2002, Landmark Graphics Corp. */ private class MyUnknownElement extends UnknownElement { private MyUnknownElement(String name) { super(name); } protected Object makeObject(UnknownElement ue, RuntimeConfigurable w) { Object realThing = super.makeObject(ue, w); DynamicTag.this.addTag(realThing); return realThing; } } // END class DynamicTag.MyUnknownElement } // END class DynamicTag -----Original Message----- From: peter reilly [mailto:[EMAIL PROTECTED] Sent: Wednesday, March 26, 2003 12:25 PM To: Ant Developers List Subject: Re: Artima SuiteRunner Task I would include filters, mappers, conditions and selectors to the list. A relatively simple mod to the core ant makes this possible (bugzilla 17199) basically get ConditionBase.java, AbstractFileSet, FilterChain implement DynamicConfigurator. and get UnknownElement (bugzilla 18312) call setProject earlier on created children. Mappers are a little different, I implemented a new attribute to handle this. It this is done, then non ant core projects may implement their own whacky types. (like this to convert to upper case: <filterchain> <tokenfilter> <scriptfilter language="beanshell"> self.setToken(self.getToken().toUpperCase()) </scriptfilter> </tokenfilter> </filterchain> Peter On Wednesday 26 March 2003 17:46, Costin Manolache wrote: > Dominique Devienne wrote: > > Hummmm, not totally. If the AntLib also uses types, you need another > > <typedef>, which should also probably needs a loaderref. Since you now > > use twice the classpath, if needs to be outside and refid'd. > > In ant1.6 the difference between tasks and types is very small. It would be > trivial to load both of them at once. > > In any case, I don't quite agree with Stefan: the simpler solution is: > > <path id="..." /> > > <taskdef resource=".. " classpathref=".." /> > > I would love to completely remove the different treatment of types - > i.e. a task is just like a type with an execute() method, and nothing else, > and <taskdef> loades both kinds. > > That would be simpler than the current syntax that also require you to > do a: > <typedef resource="..." classpathref="..." /> > > Costin > > > And what about the <junit> task? I'd like to not have setup my classpath > > outside of Ant and build.xml, and avoid having to dump everything in > > AntLib. > > > > I believe it can and should be easier and more flexible. --DD > > > > -----Original Message----- > > From: Stefan Bodewig [mailto:[EMAIL PROTECTED] > > Sent: Wednesday, March 26, 2003 10:27 AM > > To: [EMAIL PROTECTED] > > Subject: Re: Artima SuiteRunner Task > > > > On Wed, 26 Mar 2003, Dominique Devienne <[EMAIL PROTECTED]> wrote: > >> That said (one more ;-), if Ant ever comes up with an easier way to > >> integrate third party tasks > > > > Easier than <taskdef resource="..."><classpath .../></taskdef>? > > > > Almost impossible. > > > > Stefan > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: [EMAIL PROTECTED] --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]