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); | |||
} | |||
} | |||
} | |||