PR: 24411 git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@275617 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -3,10 +3,15 @@ | |||
| <head> | |||
| <meta http-equiv="Content-Language" content="en-us"></meta> | |||
| <title>PreSetDef Task</title> | |||
| <style type="text/css"> | |||
| <!-- | |||
| .code { background: #EFEFEF; margin-top: } | |||
| --> | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <h2><a name="presetdef">PreSetDef</a></h2> | |||
| <h3>Description</h3> | |||
| <p> | |||
| @@ -43,35 +48,67 @@ | |||
| This nested element can be any other type or task. The attributes | |||
| and elements that need to be preset are placed here. | |||
| </p> | |||
| <h3>Examples</h3> | |||
| <p> | |||
| The following fragment defines a javac task with the debug and deprecation | |||
| The following fragment defines a javac task with the debug, deprecation | |||
| srcdir and destdir | |||
| attributes set. It also has a src element to source files from a generated | |||
| directory. | |||
| </p> | |||
| <blockquote> | |||
| <pre> | |||
| <pre class="code"> | |||
| <presetdef name="my.javac"> | |||
| <javac debug="${debug}" deprecation="${deprecation}"> | |||
| <javac debug="${debug}" deprecation="${deprecation}" | |||
| srcdir="${src.dir}" destdir="${classes.dir}"> | |||
| <src path="${gen.dir}"/> | |||
| </javac> | |||
| </presetdef> | |||
| </pre> | |||
| </pre> | |||
| </blockquote> | |||
| <p> | |||
| This can be used as a normal javac task - example: | |||
| </p> | |||
| <blockquote> | |||
| <pre> | |||
| <my.javac src="${src.dir}" destdir="${classes.dir}"/> | |||
| </pre> | |||
| <pre class="code"> | |||
| <my.javac/> | |||
| </pre> | |||
| </blockquote> | |||
| <hr> | |||
| <p align="center">Copyright © 2003 Apache Software | |||
| Foundation. All rights Reserved.</p> | |||
| </body> | |||
| The attributes specified in the preset task may be overridden - i.e. | |||
| they may be seen as optional attributes - example: | |||
| <blockquote> | |||
| <pre class="code"> | |||
| <my.javac srcdir="${test.src}" deprecation="no"/> | |||
| </pre> | |||
| </blockquote> | |||
| One may put a presetdef definition in an antlib. | |||
| For example suppose the jar file antgoodies.jar has | |||
| the antlib.xml as follows: | |||
| <blockquote> | |||
| <pre class="code"> | |||
| <antlib> | |||
| <taskdef resource="com/acme/antgoodies/tasks.properties"/> | |||
| <!-- Implement the common use of the javac command --> | |||
| <presetdef name="javac"> | |||
| <javac deprecation="${deprecation}" debug="${debug}" | |||
| srcdir="src" destdir="classes"/> | |||
| </presetdef> | |||
| </antlib> | |||
| </pre> | |||
| </blockquote> | |||
| One may then use this in a build file as follows: | |||
| <blockquote> | |||
| <pre class="code"> | |||
| <project default="example" xmlns:antgoodies="antlib:com.acme.antgoodies"> | |||
| <target name="example"> | |||
| <!-- Compile source --> | |||
| <antgoodies:javac srcdir="src/main"/> | |||
| <!-- Compile test code --> | |||
| <antgoodies:javac srcdir="src/test"/> | |||
| </target> | |||
| </project> | |||
| </pre> | |||
| </blockquote> | |||
| <hr></hr> | |||
| <p align="center">Copyright © 2003 Apache Software | |||
| Foundation. All rights Reserved.</p> | |||
| </body> | |||
| </html> | |||
| @@ -1,20 +1,87 @@ | |||
| <project> | |||
| <path id="test-classes"> | |||
| <pathelement location="../../../../build/testcases" /> | |||
| <pathelement path="${java.class.path}" /> | |||
| </path> | |||
| <target name="simple"> | |||
| <presetdef name="my.echo"> | |||
| <echo message="Hello world"/> | |||
| </presetdef> | |||
| <my.echo/> | |||
| </target> | |||
| <target name="text"> | |||
| <presetdef name="my.echo"> | |||
| <echo>Inner Text</echo> | |||
| </presetdef> | |||
| <my.echo/> | |||
| </target> | |||
| <target name="uri"> | |||
| <presetdef name="echo" uri="abc"> | |||
| <echo message="Hello world"/> | |||
| </presetdef> | |||
| <x:echo xmlns:x="abc"/> | |||
| </target> | |||
| <target name="defaulttest"> | |||
| <taskdef name="defaulttest" | |||
| classname="org.apache.tools.ant.taskdefs.PreSetDefTest$DefaultTest" | |||
| classpathref="test-classes"/> | |||
| <presetdef name="d"> | |||
| <defaulttest attribute="true"/> | |||
| </presetdef> | |||
| <d attribute="false"/> | |||
| </target> | |||
| <target name="doubledefault"> | |||
| <taskdef name="defaulttest" | |||
| classname="org.apache.tools.ant.taskdefs.PreSetDefTest$DefaultTest" | |||
| classpathref="test-classes"/> | |||
| <presetdef name="d"> | |||
| <defaulttest attribute="true"/> | |||
| </presetdef> | |||
| <presetdef name="dd"> | |||
| <d attribute="false"/> | |||
| </presetdef> | |||
| <dd/> | |||
| <dd attribute="true"/> | |||
| </target> | |||
| <target name="text.optional"> | |||
| <presetdef name="echo.mytext"> | |||
| <echo>MyText</echo> | |||
| </presetdef> | |||
| <echo.mytext/> | |||
| <echo.mytext>override text</echo.mytext> | |||
| </target> | |||
| <target name="element.order"> | |||
| <presetdef name="el.order"> | |||
| <sequential> | |||
| <echo>Line 1</echo> | |||
| </sequential> | |||
| </presetdef> | |||
| <el.order> | |||
| <echo>Line 2</echo> | |||
| </el.order> | |||
| </target> | |||
| <target name="element.order2"> | |||
| <presetdef name="el.order"> | |||
| <sequential> | |||
| <echo>Line 1</echo> | |||
| </sequential> | |||
| </presetdef> | |||
| <presetdef name="el.order2"> | |||
| <el.order> | |||
| <echo>Line 2</echo> | |||
| </el.order> | |||
| </presetdef> | |||
| <el.order2> | |||
| <echo>Line 3</echo> | |||
| </el.order2> | |||
| </target> | |||
| </project> | |||
| @@ -65,6 +65,7 @@ import java.util.List; | |||
| import java.util.Locale; | |||
| import org.apache.tools.ant.types.EnumeratedAttribute; | |||
| import org.apache.tools.ant.types.Path; | |||
| import org.apache.tools.ant.taskdefs.PreSetDef; | |||
| /** | |||
| * Helper class that collects the methods a task or nested element | |||
| @@ -286,11 +287,14 @@ public final class IntrospectionHelper implements BuildListener { | |||
| if (nestedCreators.get(propName) == null) { | |||
| nestedTypes.put(propName, returnType); | |||
| nestedCreators.put(propName, new NestedCreator() { | |||
| public boolean isPolyMorphic() { | |||
| return false; | |||
| } | |||
| public Object getRealObject() { | |||
| return null; | |||
| } | |||
| public Class getElementClass() { | |||
| return null; | |||
| } | |||
| @@ -332,6 +336,10 @@ public final class IntrospectionHelper implements BuildListener { | |||
| return true; | |||
| } | |||
| public Object getRealObject() { | |||
| return null; | |||
| } | |||
| public Class getElementClass() { | |||
| return c.getDeclaringClass(); | |||
| } | |||
| @@ -387,6 +395,10 @@ public final class IntrospectionHelper implements BuildListener { | |||
| return true; | |||
| } | |||
| public Object getRealObject() { | |||
| return null; | |||
| } | |||
| public Class getElementClass() { | |||
| return c.getDeclaringClass(); | |||
| } | |||
| @@ -611,6 +623,10 @@ public final class IntrospectionHelper implements BuildListener { | |||
| return null; | |||
| } | |||
| public Object getRealObject() { | |||
| return null; | |||
| } | |||
| public Object create( | |||
| Project project, Object parent, Object ignore) { | |||
| return nestedElement; | |||
| @@ -1112,6 +1128,14 @@ public final class IntrospectionHelper implements BuildListener { | |||
| } | |||
| } | |||
| /** | |||
| * @return the real object (used currently only | |||
| * for preset def) | |||
| */ | |||
| public Object getRealObject() { | |||
| return nestedCreator.getRealObject(); | |||
| } | |||
| /** | |||
| * Stores the nested element object using a storage method | |||
| * determined by introspection. | |||
| @@ -1147,6 +1171,7 @@ public final class IntrospectionHelper implements BuildListener { | |||
| private interface NestedCreator { | |||
| boolean isPolyMorphic(); | |||
| Class getElementClass(); | |||
| Object getRealObject(); | |||
| Object create(Project project, Object parent, Object child) | |||
| throws InvocationTargetException, IllegalAccessException, InstantiationException; | |||
| void store(Object parent, Object child) | |||
| @@ -1252,8 +1277,14 @@ public final class IntrospectionHelper implements BuildListener { | |||
| if (addedObject == null) { | |||
| return null; | |||
| } | |||
| Object rObject = addedObject; | |||
| if (addedObject instanceof PreSetDef.PreSetDefinition) { | |||
| rObject = ((PreSetDef.PreSetDefinition) addedObject).createObject( | |||
| project); | |||
| } | |||
| final Method method = addMethod; | |||
| final Object nestedObject = addedObject; | |||
| final Object realObject = rObject; | |||
| return new NestedCreator() { | |||
| public boolean isPolyMorphic() { | |||
| @@ -1266,15 +1297,20 @@ public final class IntrospectionHelper implements BuildListener { | |||
| public Object create(Project project, Object parent, Object ignore) | |||
| throws InvocationTargetException, IllegalAccessException { | |||
| if (!method.getName().endsWith("Configured")) { | |||
| method.invoke(parent, new Object[]{nestedObject}); | |||
| method.invoke(parent, new Object[]{realObject}); | |||
| } | |||
| return nestedObject; | |||
| } | |||
| public Object getRealObject() { | |||
| return realObject; | |||
| } | |||
| public void store(Object parent, Object child) | |||
| throws InvocationTargetException, IllegalAccessException, | |||
| InstantiationException { | |||
| if (method.getName().endsWith("Configured")) { | |||
| method.invoke(parent, new Object[]{nestedObject}); | |||
| method.invoke(parent, new Object[]{realObject}); | |||
| } | |||
| } | |||
| }; | |||
| @@ -63,6 +63,7 @@ import java.util.Hashtable; | |||
| import java.util.List; | |||
| import java.util.Locale; | |||
| import java.util.Map; | |||
| import java.util.Iterator; | |||
| import org.apache.tools.ant.util.CollectionUtils; | |||
| import org.xml.sax.AttributeList; | |||
| @@ -222,7 +223,6 @@ public class RuntimeConfigurable implements Serializable { | |||
| * @return Attribute name to attribute value map | |||
| */ | |||
| public Hashtable getAttributeMap() { | |||
| // Nobody calls this method, maybe it could just be deleted? | |||
| if (attributeMap != null) { | |||
| return new Hashtable(attributeMap); | |||
| } else { | |||
| @@ -464,4 +464,46 @@ public class RuntimeConfigurable implements Serializable { | |||
| proxyConfigured = false; | |||
| maybeConfigure(p); | |||
| } | |||
| /** | |||
| * Apply presets, attributes and text are set if not currently set. | |||
| * nested elements are prepended. | |||
| * | |||
| * @param r a <code>RuntimeConfigurable</code> value | |||
| */ | |||
| public void applyPreSet(RuntimeConfigurable r) { | |||
| // Attributes | |||
| if (r.attributeMap != null) { | |||
| for (Iterator i = r.attributeMap.keySet().iterator(); i.hasNext();) { | |||
| String name = (String) i.next(); | |||
| if (attributeMap == null || attributeMap.get(name) == null) { | |||
| setAttribute(name, (String) r.attributeMap.get(name)); | |||
| } | |||
| } | |||
| } | |||
| // poly type | |||
| if (r.polyType != null && polyType == null) { | |||
| polyType = r.polyType; | |||
| } | |||
| // Children (this is a shadow of unknownElement#children) | |||
| if (r.children != null) { | |||
| List newChildren = new ArrayList(); | |||
| newChildren.addAll(r.children); | |||
| if (children != null) { | |||
| newChildren.addAll(children); | |||
| } | |||
| children = newChildren; | |||
| } | |||
| // Text | |||
| if (r.characters != null) { | |||
| if (characters == null | |||
| || characters.toString().trim().length() == 0) { | |||
| characters = | |||
| new StringBuffer(r.characters.toString()); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -58,6 +58,7 @@ import java.util.ArrayList; | |||
| import java.util.Iterator; | |||
| import java.util.List; | |||
| import java.io.IOException; | |||
| import org.apache.tools.ant.taskdefs.PreSetDef; | |||
| /** | |||
| * Wrapper class that holds all the information necessary to create a task | |||
| @@ -95,6 +96,9 @@ public class UnknownElement extends Task { | |||
| */ | |||
| private List/*<UnknownElement>*/ children = null; | |||
| /** Specifies if a predefined definition has been done */ | |||
| private boolean presetDefed = false; | |||
| /** | |||
| * Creates an UnknownElement for the given element name. | |||
| * | |||
| @@ -371,6 +375,31 @@ public class UnknownElement extends Task { | |||
| return ProjectHelper.genComponentName(getNamespace(), getTag()); | |||
| } | |||
| /** | |||
| * This is used then the realobject of the UE is a PreSetDefinition. | |||
| * This is also used when a presetdef is used on a presetdef | |||
| * The attributes, elements and text are applied to this | |||
| * UE. | |||
| * | |||
| * @param u an UnknownElement containing the attributes, elements and text | |||
| */ | |||
| public void applyPreSet(UnknownElement u) { | |||
| if (presetDefed) { | |||
| return; | |||
| } | |||
| // Do the runtime | |||
| getWrapper().applyPreSet(u.getWrapper()); | |||
| if (u.children != null) { | |||
| List newChildren = new ArrayList(); | |||
| newChildren.addAll(u.children); | |||
| if (children != null) { | |||
| newChildren.addAll(children); | |||
| } | |||
| children = newChildren; | |||
| } | |||
| presetDefed = true; | |||
| } | |||
| /** | |||
| * Creates a named task or data type. If the real object is a task, | |||
| * it is configured up to the init() stage. | |||
| @@ -386,9 +415,17 @@ public class UnknownElement extends Task { | |||
| getProject()); | |||
| String name = ue.getComponentName(); | |||
| Object o = helper.createComponent(ue, ue.getNamespace(), name); | |||
| if (o == null) { | |||
| throw getNotFoundException("task or type", name); | |||
| } | |||
| if (o instanceof PreSetDef.PreSetDefinition) { | |||
| PreSetDef.PreSetDefinition def = (PreSetDef.PreSetDefinition) o; | |||
| o = def.createObject(ue.getProject()); | |||
| ue.applyPreSet(def.getPreSets()); | |||
| } | |||
| if (o instanceof Task) { | |||
| Task task = (Task) o; | |||
| task.setOwningTarget(getOwningTarget()); | |||
| @@ -521,6 +558,12 @@ public class UnknownElement extends Task { | |||
| ih.getElementCreator(getProject(), parent, childName); | |||
| creator.setPolyType(childWrapper.getPolyType()); | |||
| Object realChild = creator.create(); | |||
| if (realChild instanceof PreSetDef.PreSetDefinition) { | |||
| PreSetDef.PreSetDefinition def = | |||
| (PreSetDef.PreSetDefinition) realChild; | |||
| realChild = creator.getRealObject(); | |||
| child.applyPreSet(def.getPreSets()); | |||
| } | |||
| childWrapper.setCreator(creator); | |||
| childWrapper.setProxy(realChild); | |||
| if (realChild instanceof Task) { | |||
| @@ -130,26 +130,52 @@ public class PreSetDef extends AntlibDefinition implements TaskContainer { | |||
| "Unable to find typedef " + componentName); | |||
| } | |||
| MyAntTypeDefinition newDef = new MyAntTypeDefinition(def, nestedTask); | |||
| PreSetDefinition newDef = new PreSetDefinition(def, nestedTask); | |||
| newDef.setName(name); | |||
| helper.addDataTypeDefinition(newDef); | |||
| } | |||
| private static class MyAntTypeDefinition extends AntTypeDefinition { | |||
| /** | |||
| * This class contains the unknown element and the object | |||
| * that is predefined. | |||
| * @see AntTypeDefinition | |||
| */ | |||
| public static class PreSetDefinition extends AntTypeDefinition { | |||
| private AntTypeDefinition parent; | |||
| private UnknownElement element; | |||
| public MyAntTypeDefinition(AntTypeDefinition parent, UnknownElement el) { | |||
| /** | |||
| * Creates a new <code>PresetDefinition</code> instance. | |||
| * | |||
| * @param parent The parent of this predefintion. | |||
| * @param el The predefined attributes, nested elements and text. | |||
| */ | |||
| public PreSetDefinition(AntTypeDefinition parent, UnknownElement el) { | |||
| if (parent instanceof PreSetDefinition) { | |||
| PreSetDefinition p = (PreSetDefinition) parent; | |||
| el.applyPreSet(p.element); | |||
| parent = p.parent; | |||
| } | |||
| this.parent = parent; | |||
| this.element = el; | |||
| } | |||
| /** | |||
| * Override so that it is not allowed | |||
| * | |||
| * @param clazz a <code>Class</code> value | |||
| */ | |||
| public void setClass(Class clazz) { | |||
| throw new BuildException("Not supported"); | |||
| } | |||
| /** | |||
| * Override so that it is not allowed | |||
| * | |||
| * @param className a <code>String</code> value | |||
| */ | |||
| public void setClassName(String className) { | |||
| throw new BuildException("Not supported"); | |||
| } | |||
| @@ -200,6 +226,7 @@ public class PreSetDef extends AntlibDefinition implements TaskContainer { | |||
| /** | |||
| * get the exposed class for this definition. | |||
| * @param project the current project | |||
| * @return the exposed class | |||
| */ | |||
| public Class getExposedClass(Project project) { | |||
| @@ -227,18 +254,37 @@ public class PreSetDef extends AntlibDefinition implements TaskContainer { | |||
| /** | |||
| * create an instance of the definition. | |||
| * The instance may be wrapped in a proxy class. | |||
| * This is a special version of create for IH and UE. | |||
| * @param project the current project | |||
| * @return the created object | |||
| */ | |||
| public Object create(Project project) { | |||
| public Object createObject(Project project) { | |||
| Object o = parent.create(project); | |||
| if (o == null) { | |||
| return null; | |||
| } | |||
| element.configure(o); | |||
| return o; | |||
| } | |||
| /** | |||
| * @return the predefined attributes, elements and text as | |||
| * a UnknownElement | |||
| */ | |||
| public UnknownElement getPreSets() { | |||
| return element; | |||
| } | |||
| /** | |||
| * Fake create an object, used by IH and UE to see that | |||
| * this is a predefined object. | |||
| * | |||
| * @param project the current project | |||
| * @return this object | |||
| */ | |||
| public Object create(Project project) { | |||
| return this; | |||
| } | |||
| /** | |||
| * Equality method for this definition | |||
| * | |||
| @@ -253,7 +299,7 @@ public class PreSetDef extends AntlibDefinition implements TaskContainer { | |||
| if (other.getClass() != getClass()) { | |||
| return false; | |||
| } | |||
| MyAntTypeDefinition otherDef = (MyAntTypeDefinition) other; | |||
| PreSetDefinition otherDef = (PreSetDefinition) other; | |||
| if (!parent.sameDefinition(otherDef.parent, project)) { | |||
| return false; | |||
| } | |||
| @@ -278,7 +324,7 @@ public class PreSetDef extends AntlibDefinition implements TaskContainer { | |||
| if (!other.getClass().getName().equals(getClass().getName())) { | |||
| return false; | |||
| } | |||
| MyAntTypeDefinition otherDef = (MyAntTypeDefinition) other; | |||
| PreSetDefinition otherDef = (PreSetDefinition) other; | |||
| if (!parent.similarDefinition(otherDef.parent, project)) { | |||
| return false; | |||
| } | |||
| @@ -55,6 +55,7 @@ | |||
| package org.apache.tools.ant.taskdefs; | |||
| import org.apache.tools.ant.BuildFileTest; | |||
| import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.Project; | |||
| import org.apache.tools.ant.Task; | |||
| @@ -82,5 +83,43 @@ public class PreSetDefTest extends BuildFileTest { | |||
| expectLog("uri", "Hello world"); | |||
| } | |||
| public void testDefaultTest() { | |||
| expectLog("defaulttest", "attribute is false"); | |||
| } | |||
| public void testDoubleDefault() { | |||
| expectLog("doubledefault", "attribute is falseattribute is true"); | |||
| } | |||
| public void testTextOptional() { | |||
| expectLog("text.optional", "MyTextoverride text"); | |||
| } | |||
| public void testElementOrder() { | |||
| expectLog("element.order", "Line 1Line 2"); | |||
| } | |||
| public void testElementOrder2() { | |||
| expectLog("element.order2", "Line 1Line 2Line 3"); | |||
| } | |||
| /** | |||
| * A test class to check default properties | |||
| */ | |||
| public static class DefaultTest extends Task { | |||
| boolean isSet = false; | |||
| boolean attribute = false; | |||
| public void setAttribute(boolean b) { | |||
| if (isSet) { | |||
| throw new BuildException("Attribute Already set"); | |||
| } | |||
| attribute = b; | |||
| isSet = true; | |||
| } | |||
| public void execute() { | |||
| getProject().log("attribute is " + attribute); | |||
| } | |||
| } | |||
| } | |||