diff --git a/WHATSNEW b/WHATSNEW index f80af061b..19594855b 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -112,6 +112,8 @@ Other changes: * Added length task to get strings' and files' lengths. +* Added clone task. + Changes from Ant 1.6.2 to current Ant 1.6 CVS version ===================================================== diff --git a/docs/manual/CoreTasks/clone.html b/docs/manual/CoreTasks/clone.html new file mode 100755 index 000000000..5f8293505 --- /dev/null +++ b/docs/manual/CoreTasks/clone.html @@ -0,0 +1,56 @@ + + + + +Clone Task + + + + +

Clone

+

Description

+

Clone a project reference (presumably for augmentation).

+

Parameters

+ + + + + + + + + + + +
AttributeDescriptionRequired
clonerefWhat to clone, given as a + reference to an + object with a publicly accessible clone() implementation. + Yes
+

+Assuming the clone operation is successful, the clone invocation supports +any attributes and nested elements supported by the cloned type +(the obvious exception is the "cloneref" attribute). +Please note that modifications to cloned objects may yield +unpredictable results depending on the internals of the cloned class. +

+

Examples

+

+Given a fileset foo: +

  <clone id="foo.txt" cloneref="foo">
+    <filename name="**/*.txt" />
+  </clone>
+  <clone id="foo.nontxt" cloneref="foo">
+    <filename name="**/*.txt" negate="true" />
+  </clone>
+
+Creates filesets foo.txt and foo.nontxt, which could be +put to such uses as filtering some files and not others when copying. +

+
+ +

Copyright © 2005 The Apache Software Foundation. All rights +Reserved.

+ + + + diff --git a/docs/manual/coretasklist.html b/docs/manual/coretasklist.html index dd35a5c6f..73067931a 100644 --- a/docs/manual/coretasklist.html +++ b/docs/manual/coretasklist.html @@ -27,6 +27,7 @@ BZip2
Checksum
Chmod
+Clone
Concat
Condition
  Supported conditions
diff --git a/src/etc/testcases/taskdefs/clone.xml b/src/etc/testcases/taskdefs/clone.xml new file mode 100644 index 000000000..f41f76095 --- /dev/null +++ b/src/etc/testcases/taskdefs/clone.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/RuntimeConfigurable.java b/src/main/org/apache/tools/ant/RuntimeConfigurable.java index 8a5ee5c8e..2d279a940 100644 --- a/src/main/org/apache/tools/ant/RuntimeConfigurable.java +++ b/src/main/org/apache/tools/ant/RuntimeConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2004 The Apache Software Foundation + * Copyright 2000-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -89,11 +89,10 @@ public class RuntimeConfigurable implements Serializable { * * @param proxy The element to configure. Must not be null. * @param elementTag The tag name generating this element. - * Should not be null. */ public RuntimeConfigurable(Object proxy, String elementTag) { setProxy(proxy); - this.elementTag = elementTag; + setElementTag(elementTag); // Most likely an UnknownElement if (proxy instanceof Task) { ((Task) proxy).setRuntimeConfigurableWrapper(this); @@ -105,7 +104,7 @@ public class RuntimeConfigurable implements Serializable { * * @param proxy The element to configure. Must not be null. */ - public void setProxy(Object proxy) { + public synchronized void setProxy(Object proxy) { wrappedObject = proxy; proxyConfigured = false; } @@ -116,7 +115,7 @@ public class RuntimeConfigurable implements Serializable { * * @param creator the creator object. */ - void setCreator(IntrospectionHelper.Creator creator) { + synchronized void setCreator(IntrospectionHelper.Creator creator) { this.creator = creator; } @@ -126,7 +125,7 @@ public class RuntimeConfigurable implements Serializable { * * @return the object whose configure is held by this instance. */ - public Object getProxy() { + public synchronized Object getProxy() { return wrappedObject; } @@ -134,7 +133,7 @@ public class RuntimeConfigurable implements Serializable { * Get the polymorphic type for this element. * @return the ant component type name, null if not set. */ - public String getPolyType() { + public synchronized String getPolyType() { return polyType; } @@ -142,7 +141,7 @@ public class RuntimeConfigurable implements Serializable { * Set the polymorphic type for this element. * @param polyType the ant component type name, null if not set. */ - public void setPolyType(String polyType) { + public synchronized void setPolyType(String polyType) { this.polyType = polyType; } @@ -153,7 +152,7 @@ public class RuntimeConfigurable implements Serializable { * @param attributes List of attributes defined in the XML for this * element. May be null. */ - public void setAttributes(AttributeList attributes) { + public synchronized void setAttributes(AttributeList attributes) { this.attributes = new AttributeListImpl(attributes); for (int i = 0; i < attributes.getLength(); i++) { setAttribute(attributes.getName(i), attributes.getValue(i)); @@ -166,7 +165,7 @@ public class RuntimeConfigurable implements Serializable { * @param name the name of the attribute. * @param value the attribute's value. */ - public void setAttribute(String name, String value) { + public synchronized void setAttribute(String name, String value) { if (name.equalsIgnoreCase(ProjectHelper.ANT_TYPE)) { this.polyType = value; } else { @@ -179,13 +178,22 @@ public class RuntimeConfigurable implements Serializable { } } + /** + * Delete an attribute. Not for the faint of heart. + * @param name the name of the attribute to be removed. + */ + public synchronized void removeAttribute(String name) { + attributeNames.remove(name); + attributeMap.remove(name); + } + /** * Return the attribute map. * * @return Attribute name to attribute value map. * @since Ant 1.6 */ - public Hashtable getAttributeMap() { + public synchronized Hashtable getAttributeMap() { return (attributeMap == null) ? EMPTY_HASHTABLE : new Hashtable(attributeMap); } @@ -197,7 +205,7 @@ public class RuntimeConfigurable implements Serializable { * @return An AttributeList representing the attributes defined in the * XML for this element. May be null. */ - public AttributeList getAttributes() { + public synchronized AttributeList getAttributes() { return attributes; } @@ -207,7 +215,7 @@ public class RuntimeConfigurable implements Serializable { * @param child The child element wrapper to add to this one. * Must not be null. */ - public void addChild(RuntimeConfigurable child) { + public synchronized void addChild(RuntimeConfigurable child) { children = (children == null) ? new ArrayList() : children; children.add(child); } @@ -220,7 +228,7 @@ public class RuntimeConfigurable implements Serializable { * @return The child wrapper at position index within the * list. */ - RuntimeConfigurable getChild(int index) { + synchronized RuntimeConfigurable getChild(int index) { return (RuntimeConfigurable) children.get(index); } @@ -229,7 +237,7 @@ public class RuntimeConfigurable implements Serializable { * @return an enumeration of the child wrappers. * @since Ant 1.6 */ - public Enumeration getChildren() { + public synchronized Enumeration getChildren() { return (children == null) ? new CollectionUtils.EmptyEnumeration() : Collections.enumeration(children); } @@ -240,7 +248,10 @@ public class RuntimeConfigurable implements Serializable { * @param data Text to add to the wrapped element. * Should not be null. */ - public void addText(String data) { + public synchronized void addText(String data) { + if (data.length() == 0) { + return; + } characters = (characters == null) ? new StringBuffer(data) : characters.append(data); } @@ -254,14 +265,12 @@ public class RuntimeConfigurable implements Serializable { * @param count The number of characters to read from the array. * */ - public void addText(char[] buf, int start, int count) { + public synchronized void addText(char[] buf, int start, int count) { if (count == 0) { return; } - if (characters == null) { - characters = new StringBuffer(count); - } - characters.append(buf, start, count); + characters = ((characters == null) + ? new StringBuffer(count) : characters).append(buf, start, count); } /** @@ -272,12 +281,16 @@ public class RuntimeConfigurable implements Serializable { * @return the text content of this element. * @since Ant 1.6 */ - public StringBuffer getText() { - if (characters != null) { - return characters; - } else { - return new StringBuffer(0); - } + public synchronized StringBuffer getText() { + return (characters == null) ? new StringBuffer(0) : characters; + } + + /** + * Set the element tag. + * @param elementTag The tag name generating this element. + */ + public synchronized void setElementTag(String elementTag) { + this.elementTag = elementTag; } /** @@ -286,7 +299,7 @@ public class RuntimeConfigurable implements Serializable { * @return The tag name of the wrapped element. This is unlikely * to be null, but may be. */ - public String getElementTag() { + public synchronized String getElementTag() { return elementTag; } @@ -330,7 +343,7 @@ public class RuntimeConfigurable implements Serializable { * to invalid attributes or children, or text being added to * an element which doesn't accept it. */ - public void maybeConfigure(Project p, boolean configureChildren) + public synchronized void maybeConfigure(Project p, boolean configureChildren) throws BuildException { String id = null; @@ -384,27 +397,28 @@ public class RuntimeConfigurable implements Serializable { Enumeration e = getChildren(); while (e.hasMoreElements()) { RuntimeConfigurable child = (RuntimeConfigurable) e.nextElement(); - if (child.wrappedObject instanceof Task) { - Task childTask = (Task) child.wrappedObject; - 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 - * tasks in TaskContainers. - * - * For TaskContainers, we simply skip configuration here. - */ - String tag = child.getElementTag().toLowerCase(Locale.US); - if (configureChildren && ih.supportsNestedElement(tag)) { - child.maybeConfigure(p); - ProjectHelper.storeChild(p, target, child.wrappedObject, tag); + synchronized (child) { + if (child.wrappedObject instanceof Task) { + Task childTask = (Task) child.wrappedObject; + 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 + * tasks in TaskContainers. + * + * For TaskContainers, we simply skip configuration here. + */ + String tag = child.getElementTag().toLowerCase(Locale.US); + if (configureChildren && ih.supportsNestedElement(tag)) { + child.maybeConfigure(p); + ProjectHelper.storeChild(p, target, child.wrappedObject, tag); + } } } diff --git a/src/main/org/apache/tools/ant/UnknownElement.java b/src/main/org/apache/tools/ant/UnknownElement.java index 19c7607bc..1d5acf6ac 100644 --- a/src/main/org/apache/tools/ant/UnknownElement.java +++ b/src/main/org/apache/tools/ant/UnknownElement.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2004 The Apache Software Foundation + * Copyright 2000-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -401,7 +401,6 @@ public class UnknownElement extends Task { 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()); @@ -412,7 +411,9 @@ public class UnknownElement extends Task { task.setTaskName(ue.getTaskName()); } } - + if (o instanceof UnknownElement) { + o = ((UnknownElement) o).makeObject((UnknownElement) o, w); + } if (o instanceof Task) { Task task = (Task) o; task.setOwningTarget(getOwningTarget()); @@ -639,10 +640,7 @@ public class UnknownElement extends Task { return true; } - private boolean equalsString(String a, String b) { - if (a == null) { - return b == null; - } - return a.equals(b); + private static boolean equalsString(String a, String b) { + return (a == null) ? (a == b) : a.equals(b); } } diff --git a/src/main/org/apache/tools/ant/taskdefs/Clone.java b/src/main/org/apache/tools/ant/taskdefs/Clone.java new file mode 100755 index 000000000..7240ad131 --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/Clone.java @@ -0,0 +1,97 @@ +/* + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.taskdefs; + +import java.lang.reflect.Method; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.UnknownElement; +import org.apache.tools.ant.RuntimeConfigurable; + +/** + * Clone an Object from a reference. + * @since Ant 1.7 + */ +public class Clone extends UnknownElement { + /** Task name. */ + public static final String TASK_NAME = "clone"; + + /** Clone reference attribute ID. */ + public static final String CLONE_REF = "cloneref"; + + private static final Class[] NO_ARGS = new Class[] {}; + + /** + * Create a new instance of the Clone task. + */ + public Clone() { + super(TASK_NAME); + } + + /** + * Creates a named task or data type. If the real object is a task, + * it is configured up to the init() stage. + * + * @param ue The UnknownElement to create the real object for. + * Not used in this implementation. + * @param w The RuntimeConfigurable containing the configuration + * information to pass to the cloned Object. + * + * @return the task or data type represented by the given unknown element. + */ + protected Object makeObject(UnknownElement ue, RuntimeConfigurable w) { + String cloneref = (String) (w.getAttributeMap().get(CLONE_REF)); + if (cloneref == null) { + throw new BuildException("cloneref attribute not set"); + } + Object ob = getProject().getReference(cloneref); + if (ob == null) { + throw new BuildException( + "reference \"" + cloneref + "\" not found"); + } + try { + log("Attempting to clone " + ob.toString() + " \"" + + cloneref + "\"", Project.MSG_VERBOSE); + Method m = ob.getClass().getMethod("clone", NO_ARGS); + try { + Object bo = m.invoke(ob, NO_ARGS); + if (bo == null) { + throw new BuildException(m.toString() + " returned null"); + } + w.removeAttribute(CLONE_REF); + w.setProxy(bo); + w.setElementTag(null); + setRuntimeConfigurableWrapper(w); + if (bo instanceof Task) { + ((Task) bo).setOwningTarget(getOwningTarget()); + ((Task) bo).init(); + } + return bo; + } catch (Exception e) { + throw new BuildException(e); + } + } catch (NoSuchMethodException e) { + throw new BuildException( + "Unable to locate public clone method for object \"" + + cloneref + "\""); + } + } + +} diff --git a/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/src/main/org/apache/tools/ant/taskdefs/defaults.properties index 55b6c2226..c1f7aa797 100644 --- a/src/main/org/apache/tools/ant/taskdefs/defaults.properties +++ b/src/main/org/apache/tools/ant/taskdefs/defaults.properties @@ -82,6 +82,7 @@ macrodef=org.apache.tools.ant.taskdefs.MacroDef nice=org.apache.tools.ant.taskdefs.Nice libraries=org.apache.tools.ant.taskdefs.repository.Libraries length=org.apache.tools.ant.taskdefs.Length +clone=org.apache.tools.ant.taskdefs.Clone # optional tasks image=org.apache.tools.ant.taskdefs.optional.image.Image diff --git a/src/testcases/org/apache/tools/ant/taskdefs/CloneTest.java b/src/testcases/org/apache/tools/ant/taskdefs/CloneTest.java new file mode 100755 index 000000000..df5f26471 --- /dev/null +++ b/src/testcases/org/apache/tools/ant/taskdefs/CloneTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.taskdefs; + +import org.apache.tools.ant.BuildFileTest; + +public class CloneTest extends BuildFileTest { + + public CloneTest(String name) { + super(name); + } + + public void setUp() { + configureProject("src/etc/testcases/taskdefs/clone.xml"); + } + + public void testClone1() { + executeTarget("testClone1"); + } + + public void testClone2() { + executeTarget("testClone2"); + } + + public void testClone3() { + executeTarget("testClone3"); + } + + public void testNoClone() { + expectBuildExceptionContaining("testNoClone", + "should fail because Object cannot be cloned", "public clone method"); + } + + public void testNoAttr() { + expectSpecificBuildException("testNoAttr", + "cloneref attribute not set", "cloneref attribute not set"); + } + + public void testNoRef() { + expectSpecificBuildException("testNoRef", "reference does not exist", + "reference \"thisreferencehasnotbeensetinthecurrentproject\" not found"); + } + +}