Return-Path: Delivered-To: apmail-ant-dev-archive@ant.apache.org Received: (qmail 35634 invoked by uid 500); 23 Jul 2003 12:06:06 -0000 Mailing-List: contact dev-help@ant.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Ant Developers List" Reply-To: "Ant Developers List" Delivered-To: mailing list dev@ant.apache.org Received: (qmail 35580 invoked by uid 500); 23 Jul 2003 12:06:06 -0000 Received: (qmail 35535 invoked from network); 23 Jul 2003 12:06:06 -0000 Received: from icarus.apache.org (208.185.179.13) by daedalus.apache.org with SMTP; 23 Jul 2003 12:06:06 -0000 Received: (qmail 85470 invoked by uid 1539); 23 Jul 2003 12:06:04 -0000 Date: 23 Jul 2003 12:06:04 -0000 Message-ID: <20030723120604.85469.qmail@icarus.apache.org> From: peterreilly@apache.org To: ant-cvs@apache.org Subject: cvs commit: ant/src/testcases/org/apache/tools/ant/types PolyTest.java X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N peterreilly 2003/07/23 05:06:04 Modified: src/main/org/apache/tools/ant IntrospectionHelper.java RuntimeConfigurable.java UnknownElement.java Added: src/etc/testcases/types poly.xml src/testcases/org/apache/tools/ant/types PolyTest.java Log: This commit implements: - addConfigured(Type) to introspection rules - ant-type magic polymorhic attribute - allow types that have Project as a constructor to be used in addName(Type) and addConfiguredName(Type) methods - allow addName and addConfiguredName methods to have higher presedence that createName() methods. PR: 19897 Revision Changes Path 1.61 +300 -84 ant/src/main/org/apache/tools/ant/IntrospectionHelper.java Index: IntrospectionHelper.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/IntrospectionHelper.java,v retrieving revision 1.60 retrieving revision 1.61 diff -u -r1.60 -r1.61 --- IntrospectionHelper.java 18 Jul 2003 12:45:54 -0000 1.60 +++ IntrospectionHelper.java 23 Jul 2003 12:06:03 -0000 1.61 @@ -70,10 +70,12 @@ * Helper class that collects the methods a task or nested element * holds to set attributes, create nested elements or hold PCDATA * elements. + * The class is final as it has a private constructor. * * @author Stefan Bodewig + * @author Peter Reilly */ -public class IntrospectionHelper implements BuildListener { +public final class IntrospectionHelper implements BuildListener { /** * Map from attribute names to attribute types @@ -205,7 +207,6 @@ attributeSetters = new Hashtable(); nestedTypes = new Hashtable(); nestedCreators = new Hashtable(); - nestedStorers = new Hashtable(); addTypeMethods = new ArrayList(); this.bean = bean; @@ -220,7 +221,7 @@ // check of add[Configured](Class) pattern if (args.length == 1 && java.lang.Void.TYPE.equals(returnType) - && (name.equals("add") /*|| name.equals("addConfigured")*/)) { + && (name.equals("add") || name.equals("addConfigured"))) { insertAddTypeMethod(m); continue; } @@ -286,19 +287,31 @@ && args.length == 0) { String propName = getPropertyName(name, "create"); - nestedTypes.put(propName, returnType); - nestedCreators.put(propName, new NestedCreator() { + // Check if a create of this property is already present + // add takes preference over create for CB purposes + if (nestedCreators.get(propName) == null) { + nestedTypes.put(propName, returnType); + nestedCreators.put(propName, new NestedCreator() { + + public boolean isPolyMorphic() { + return false; + } + + public Class getElementClass() { + return null; + } - public Object create(Object parent) + public Object create( + Project project, Object parent, Object ignore) throws InvocationTargetException, IllegalAccessException { - return m.invoke(parent, new Object[] {}); } + public void store(Object parent, Object child) { + } }); - nestedStorers.remove(propName); - + } } else if (name.startsWith("addConfigured") && java.lang.Void.TYPE.equals(returnType) && args.length == 1 @@ -307,24 +320,45 @@ && !args[0].isPrimitive()) { try { - final Constructor c = - args[0].getConstructor(new Class[] {}); + Constructor constructor = null; + try { + constructor = + args[0].getConstructor(new Class[] {}); + } catch (NoSuchMethodException ex) { + constructor = + args[0].getConstructor(new Class[] { + Project.class}); + } + final Constructor c = constructor; String propName = getPropertyName(name, "addConfigured"); nestedTypes.put(propName, args[0]); nestedCreators.put(propName, new NestedCreator() { - public Object create(Object parent) - throws InvocationTargetException, IllegalAccessException, InstantiationException { + public boolean isPolyMorphic() { + return true; + } - Object o = c.newInstance(new Object[] {}); - return o; + public Class getElementClass() { + return c.getDeclaringClass(); } - }); - nestedStorers.put(propName, new NestedStorer() { + public Object create( + Project project, Object parent, Object child) + throws InvocationTargetException, + IllegalAccessException, InstantiationException { + if (child != null) { + return child; + } else if (c.getParameterTypes().length == 0) { + return c.newInstance(new Object[] {}); + } else { + return c.newInstance(new Object[] { + project}); + } + } public void store(Object parent, Object child) - throws InvocationTargetException, IllegalAccessException, InstantiationException { + throws InvocationTargetException, + IllegalAccessException, InstantiationException { m.invoke(parent, new Object[] {child}); } @@ -341,22 +375,50 @@ && !args[0].isPrimitive()) { try { - final Constructor c = - args[0].getConstructor(new Class[] {}); + Constructor constructor = null; + try { + constructor = + args[0].getConstructor(new Class[] {}); + } catch (NoSuchMethodException ex) { + constructor = + args[0].getConstructor(new Class[] { + Project.class}); + } + final Constructor c = constructor; String propName = getPropertyName(name, "add"); nestedTypes.put(propName, args[0]); nestedCreators.put(propName, new NestedCreator() { - public Object create(Object parent) - throws InvocationTargetException, IllegalAccessException, InstantiationException { + public boolean isPolyMorphic() { + return true; + } + + public Class getElementClass() { + return c.getDeclaringClass(); + } + + public Object create( + Project project, Object parent, Object child) + throws InvocationTargetException, + IllegalAccessException, InstantiationException { + if (child != null) { + // ignore + } else if (c.getParameterTypes().length == 0) { + child = c.newInstance(new Object[] {}); + } else { + child = c.newInstance(new Object[] { + project}); + } + m.invoke(parent, new Object[] {child}); + return child; + } + public void store(Object parent, Object child) + throws InvocationTargetException, + IllegalAccessException, InstantiationException { - Object o = c.newInstance(new Object[] {}); - m.invoke(parent, new Object[] {o}); - return o; } }); - nestedStorers.remove(name); } catch (NoSuchMethodException nse) { // ignore } @@ -535,6 +597,40 @@ throw new BuildException(msg); } + private NestedCreator getNestedCreator(Project project, Object parent, + String elementName) throws BuildException { + + NestedCreator nc = (NestedCreator) nestedCreators.get(elementName); + if (nc == null) { + nc = createAddTypeCreator(project, parent, elementName); + } + if (nc == null && parent instanceof DynamicConfigurator) { + DynamicConfigurator dc = (DynamicConfigurator) parent; + final Object nestedElement = dc.createDynamicElement(elementName); + if (nestedElement != null) { + nc = new NestedCreator() { + public boolean isPolyMorphic() { + return false; + } + public Class getElementClass() { + return null; + } + + public Object create( + Project project, Object parent, Object ignore) { + return nestedElement; + } + public void store(Object parent, Object child) { + } + }; + } + } + if (nc == null) { + throwNotSupported(project, parent, elementName); + } + return nc; + } + /** * Creates a named nested element. Depending on the results of the * initial introspection, either a method in the given parent instance @@ -558,32 +654,9 @@ */ public Object createElement(Project project, Object parent, String elementName) throws BuildException { - NestedCreator nc = (NestedCreator) nestedCreators.get(elementName); - if (nc == null && addTypeMethods.size() > 0) { - Object nestedElement = createAddTypeElement( - project, parent, elementName); - if (nestedElement != null) { - if (project != null) { - project.setProjectReference(nestedElement); - } - return nestedElement; - } - } - if (nc == null && parent instanceof DynamicConfigurator) { - DynamicConfigurator dc = (DynamicConfigurator) parent; - Object nestedElement = dc.createDynamicElement(elementName); - if (nestedElement != null) { - if (project != null) { - project.setProjectReference(nestedElement); - } - return nestedElement; - } - } - if (nc == null) { - throwNotSupported(project, parent, elementName); - } + NestedCreator nc = getNestedCreator(project, parent, elementName); try { - Object nestedElement = nc.create(parent); + Object nestedElement = nc.create(project, parent, null); if (project != null) { project.setProjectReference(nestedElement); } @@ -604,6 +677,23 @@ } /** + * returns an object that creates and stores an object + * for an element of a parent. + * + * @param project Project to which the parent object belongs. + * @param parent Parent object used to create the creator object to + * create and store and instance of a subelement. + * @param elementName Name of the element to create an instance of. + * @return a creator object to create and store the element instance. + */ + + public Creator getElementCreator( + Project project, Object parent, String elementName) { + NestedCreator nc = getNestedCreator(project, parent, elementName); + return new Creator(project, parent, nc); + } + + /** * Indicate if this element supports a nested element of the * given name. * @@ -642,7 +732,7 @@ if (elementName == null) { return; } - NestedStorer ns = (NestedStorer) nestedStorers.get(elementName); + NestedCreator ns = (NestedCreator) nestedCreators.get(elementName); if (ns == null) { return; } @@ -876,7 +966,8 @@ return new AttributeSetter() { public void set(Project p, Object parent, String value) - throws InvocationTargetException, IllegalAccessException, BuildException { + throws InvocationTargetException, + IllegalAccessException, BuildException { try { Object attribute = c.newInstance(new String[] {value}); if (p != null) { @@ -934,23 +1025,138 @@ } /** - * Internal interface used to create nested elements. Not documented - * in detail for reasons of source code readability. - */ - private interface NestedCreator { - Object create(Object parent) - throws InvocationTargetException, IllegalAccessException, InstantiationException; + * creator - allows use of create/store external + * to IntrospectionHelper. + * The class is final as it has a private constructor. + */ + public static final class Creator { + private NestedCreator nestedCreator; + private Object parent; + private Project project; + private Object nestedObject; + private String polyType; + + /** + * Creates a new Creator instance. + * This object is given to the UnknownElement to create + * objects for sub-elements. UnknownElement calls + * create to create an object, the object then gets + * configured and then UnknownElement calls store. + * SetPolyType may be used to override the type used + * to create the object with. SetPolyType gets called + * before create. + * + * @param project the current project + * @param parent the parent object to create the object in + * @param nestedCreator the nested creator object to use + */ + private Creator( + Project project, Object parent, NestedCreator nestedCreator) { + this.project = project; + this.parent = parent; + this.nestedCreator = nestedCreator; + } + + /** + * Used to override the class used to create the object. + * + * @param polyType a ant component type name + */ + public void setPolyType(String polyType) { + this.polyType = polyType; + } + + /** + * Create an object using this creator, which is determined + * by introspection. + * + * @return the created object + */ + public Object create() { + if (polyType != null) { + if (!nestedCreator.isPolyMorphic()) { + throw new BuildException( + "Not allowed to use the polymorhic form" + + " for this element"); + } + Class elementClass = nestedCreator.getElementClass(); + ComponentHelper helper = + ComponentHelper.getComponentHelper(project); + nestedObject = ComponentHelper.getComponentHelper(project) + .createComponent(polyType); + if (nestedObject == null) { + throw new BuildException( + "Unable to create object of type " + polyType); + } + } + try { + nestedObject = nestedCreator.create( + project, parent, nestedObject); + if (project != null) { + project.setProjectReference(nestedObject); + } + return nestedObject; + } catch (IllegalAccessException ex) { + throw new BuildException(ex); + } catch (InstantiationException ex) { + throw new BuildException(ex); + } catch (IllegalArgumentException ex) { + if (polyType != null) { + throw new BuildException( + "Invalid type used " + polyType); + } + throw ex; + } catch (InvocationTargetException ex) { + Throwable t = ex.getTargetException(); + if (t instanceof BuildException) { + throw (BuildException) t; + } + throw new BuildException(t); + } + } + + /** + * Stores the nested elemtnt object using a storage method + * detimined by introspection. + * + */ + public void store() { + try { + nestedCreator.store(parent, nestedObject); + } catch (IllegalAccessException ex) { + throw new BuildException(ex); + } catch (InstantiationException ex) { + throw new BuildException(ex); + } catch (IllegalArgumentException ex) { + if (polyType != null) { + throw new BuildException( + "Invalid type used " + polyType); + } + throw ex; + } catch (InvocationTargetException ex) { + Throwable t = ex.getTargetException(); + if (t instanceof BuildException) { + throw (BuildException) t; + } + throw new BuildException(t); + } + } } /** - * Internal interface used to storing nested elements. Not documented + * Internal interface used to create nested elements. Not documented * in detail for reasons of source code readability. */ - private interface NestedStorer { + private interface NestedCreator { + boolean isPolyMorphic(); + Class getElementClass(); + Object create(Project project, Object parent, Object child) + throws InvocationTargetException, IllegalAccessException, InstantiationException; void store(Object parent, Object child) throws InvocationTargetException, IllegalAccessException, InstantiationException; } + /** * Internal interface used to setting element attributes. Not documented * in detail for reasons of source code readability. @@ -1023,21 +1229,20 @@ public void messageLogged(BuildEvent event) { } + /** - * Check if the parent accepts a typed nested element - * and if so, create the object, call the parents - * addmethod. - * This method is part of the initial support - * for add(Type) and addConfigured(Type). - * AddConfigured(Type) will be done later. + * */ - - private Object createAddTypeElement( - Project project, Object parent, String elementName) { + private NestedCreator createAddTypeCreator( + Project project, Object parent, String elementName) + throws BuildException { + if (addTypeMethods.size() == 0) { + return null; + } ComponentHelper helper = ComponentHelper.getComponentHelper(project); + Object addedObject = null; Method addMethod = null; - Class clazz = helper.getComponentClass(elementName); if (clazz == null) { return null; @@ -1050,21 +1255,32 @@ if (addedObject == null) { return null; } + final Method method = addMethod; + final Object nestedObject = addedObject; - try { - addMethod.invoke(parent, new Object[] {addedObject}); - } catch (IllegalAccessException ex) { - throw new BuildException(ex); - } catch (InvocationTargetException ex) { - Throwable t = ex.getTargetException(); - if (t instanceof BuildException) { - throw (BuildException) t; + return new NestedCreator() { + public boolean isPolyMorphic() { + return false; } - throw new BuildException(t); - } catch (Throwable t) { - throw new BuildException(t); - } - return addedObject; + + public Class getElementClass() { + return null; + } + public Object create(Project project, Object parent, Object ignore) + throws InvocationTargetException, IllegalAccessException { + if (!method.getName().endsWith("Configured")) { + method.invoke(parent, new Object[]{nestedObject}); + } + return nestedObject; + } + public void store(Object parent, Object child) + throws InvocationTargetException, IllegalAccessException, + InstantiationException { + if (method.getName().endsWith("Configured")) { + method.invoke(parent, new Object[]{nestedObject}); + } + } + }; } /** 1.41 +42 -6 ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java Index: RuntimeConfigurable.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java,v retrieving revision 1.40 retrieving revision 1.41 diff -u -r1.40 -r1.41 --- RuntimeConfigurable.java 18 Jul 2003 12:45:55 -0000 1.40 +++ RuntimeConfigurable.java 23 Jul 2003 12:06:03 -0000 1.41 @@ -77,6 +77,9 @@ */ public class RuntimeConfigurable implements Serializable { + /** Polymorphic attribute (May be XML NS attribute later) */ + private static final String ANT_TYPE = "ant-type"; + /** Name of the element to configure. */ private String elementTag = null; @@ -88,7 +91,10 @@ */ private transient Object wrappedObject = null; - /** + /** the creator used to make the wrapped object */ + private transient IntrospectionHelper.Creator creator; + + /** * @deprecated * XML attributes for the element. */ @@ -112,6 +118,9 @@ /** Indicates if the wrapped object has been configured */ private boolean proxyConfigured = false; + /** the polymorphic type */ + private String polyType = null; + /** * Sole constructor creating a wrapper for the specified object. * @@ -140,6 +149,16 @@ } /** + * Sets the creator of the element to be configured + * used to store the element in the parent; + * + * @param creator the creator object + */ + void setCreator(IntrospectionHelper.Creator creator) { + this.creator = creator; + } + + /** * Get the object for which this RuntimeConfigurable holds the configuration * information * @@ -150,6 +169,13 @@ } /** + * get the polymorphic type for this element + */ + public String getPolyType() { + return polyType; + } + + /** * Sets the attributes for the wrapped element. * * @deprecated @@ -170,12 +196,16 @@ * @param value the attribute's value. */ public void setAttribute(String name, String value) { - if (attributeNames == null) { - attributeNames = new ArrayList(); - attributeMap = new HashMap(); + if (name.equalsIgnoreCase(ANT_TYPE)) { + this.polyType = value; + } else { + if (attributeNames == null) { + attributeNames = new ArrayList(); + attributeMap = new HashMap(); + } + attributeNames.add(name); + attributeMap.put(name, value); } - attributeNames.add(name); - attributeMap.put(name, value); } /** Return the attribute map. @@ -389,6 +419,12 @@ childTask.setRuntimeConfigurableWrapper(child); } + + if ((child.creator != null) && configureChildren) { + child.maybeConfigure(p); + child.creator.store(); + continue; + } /* * backwards compatibility - element names of nested * elements have been all lower-case in Ant, except for 1.58 +15 -12 ant/src/main/org/apache/tools/ant/UnknownElement.java Index: UnknownElement.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/UnknownElement.java,v retrieving revision 1.57 retrieving revision 1.58 diff -u -r1.57 -r1.58 --- UnknownElement.java 22 Jul 2003 11:58:55 -0000 1.57 +++ UnknownElement.java 23 Jul 2003 12:06:03 -0000 1.58 @@ -312,17 +312,13 @@ Class parentClass = parent.getClass(); IntrospectionHelper ih = IntrospectionHelper.getHelper(parentClass); + if (children != null) { Iterator it = children.iterator(); for (int i = 0; it.hasNext(); i++) { RuntimeConfigurable childWrapper = parentWrapper.getChild(i); UnknownElement child = (UnknownElement) it.next(); - - // backwards compatibility - element names of nested - // elements have been all lower-case in Ant, except for - // TaskContainers - if (!handleChild(ih, parent, child, - child.getTag().toLowerCase(Locale.US), + if (!handleChild(ih, parent, child, childWrapper)) { if (!(parent instanceof TaskContainer)) { ih.throwNotSupported(getProject(), parent, @@ -475,17 +471,24 @@ */ private boolean handleChild(IntrospectionHelper ih, Object parent, UnknownElement child, - String childTag, RuntimeConfigurable childWrapper) { - if (ih.supportsNestedElement(childTag)) { - Object realChild - = ih.createElement(getProject(), parent, childTag); + // backwards compatibility - element names of nested + // elements have been all lower-case in Ant, except for + // TaskContainers + String childName = child.getTag().toLowerCase(Locale.US); + if (ih.supportsNestedElement(childName)) { + IntrospectionHelper.Creator creator = + ih.getElementCreator(getProject(), parent, childName); + creator.setPolyType(childWrapper.getPolyType()); + Object realChild=creator.create(); + childWrapper.setCreator(creator); childWrapper.setProxy(realChild); if (realChild instanceof Task) { Task childTask = (Task) realChild; childTask.setRuntimeConfigurableWrapper(childWrapper); - childTask.setTaskName(childTag); - childTask.setTaskType(childTag); + childTask.setTaskName(childName); + childTask.setTaskType(childName); + childTask.setLocation(child.getLocation()); } child.handleChildren(realChild, childWrapper); return true; 1.1 ant/src/etc/testcases/types/poly.xml Index: poly.xml =================================================================== 1.1 ant/src/testcases/org/apache/tools/ant/types/PolyTest.java Index: PolyTest.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 2002 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "Ant" and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ package org.apache.tools.ant.types; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildFileTest; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.taskdefs.condition.Condition; public class PolyTest extends BuildFileTest { public PolyTest(String name) { super(name); } public void setUp() { configureProject("src/etc/testcases/types/poly.xml"); } public void testFileSet() { expectLogContaining("fileset", "types.FileSet"); } public void testFileSetAntType() { expectLogContaining("fileset-ant-type", "types.PolyTest$MyFileSet"); } public void testPath() { expectLogContaining("path", "types.Path"); } public void testPathAntType() { expectLogContaining("path-ant-type", "types.PolyTest$MyPath"); } public static class MyFileSet extends FileSet {} public static class MyPath extends Path { public MyPath(Project project) { super(project); } } public static class MyTask extends Task { public void addPath(Path path) { log("class of path is " + path.getClass()); } public void addFileset(FileSet fileset) { log("class of fileset is " + fileset.getClass()); } } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org For additional commands, e-mail: dev-help@ant.apache.org