diff --git a/docs/manual/CoreTasks/macrodef.html b/docs/manual/CoreTasks/macrodef.html index da6de2409..595e7a168 100644 --- a/docs/manual/CoreTasks/macrodef.html +++ b/docs/manual/CoreTasks/macrodef.html @@ -131,6 +131,17 @@ No + + implicit + + If true this nested element is implicit. This means that + any nested elements of the macrodef instance will be placed + in the element indicated by the name of this element. + There can only be one element if an element is implicit. + The default value is false. since ant 1.6.2 + + No + description @@ -254,6 +265,45 @@ <linker refid="linker-libs"/> </cc-elements> </call-cc> + + +

+ The following fragment shows <call-cc>, but this time + using an implicit element and with the link and target.dir arguments + having default values. +

+
+
+<macrodef name="call-cc">
+   <attribute name="target"/>
+   <attribute name="link" default="executable"/>
+   <attribute name="target.dir" default="${build.bin.dir}"/>
+   <element name="cc-elements" implicit="yes"/>
+   <sequential>
+      <mkdir dir="${obj.dir}/@{target}"/>
+      <mkdir dir="@{target.dir}"/>
+         <cc link="@{link}" objdir="${obj.dir}/@{target}"
+             outfile="@{target.dir}/@{target}">
+            <compiler refid="compiler.options"/>
+            <cc-elements/>
+         </cc>
+      </sequential>
+</macrodef>
+
+
+

+ This then can be used as follows, note that <cc-elements> + is not specified. +

+
+
+<call-cc target="unittests"/>
+   <includepath location="${gen.dir}"/>
+   <includepath location="test"/>
+   <fileset dir="test/unittest" includes = "**/*.cpp"/>
+   <fileset dir="${gen.dir}" includes = "*.cpp"/>
+   <linker refid="linker-libs"/>
+</call-cc>
 

diff --git a/src/etc/testcases/taskdefs/macrodef.xml b/src/etc/testcases/taskdefs/macrodef.xml index b64418c72..fac274a1b 100644 --- a/src/etc/testcases/taskdefs/macrodef.xml +++ b/src/etc/testcases/taskdefs/macrodef.xml @@ -168,4 +168,59 @@ + + + + + Before implicit + + After implicit + + + + + In implicit + + + + + + + + Before implicit + + After implicit + + + + + + + + + + + + Before implicit + + After implicit + + + + + + + + + + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/DynamicAttribute.java b/src/main/org/apache/tools/ant/DynamicAttribute.java new file mode 100644 index 000000000..b1e33d663 --- /dev/null +++ b/src/main/org/apache/tools/ant/DynamicAttribute.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2004 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; + +/** + * Enables a task to control unknown attributes + * + * @since Ant 1.5 + */ +public interface DynamicAttribute { + + /** + * Set a named attribute to the given value + * + * @param name the name of the attribute + * @param value the new value of the attribute + * @throws BuildException when any error occurs + */ + void setDynamicAttribute(String name, String value) + throws BuildException; + +} diff --git a/src/main/org/apache/tools/ant/DynamicAttributeNS.java b/src/main/org/apache/tools/ant/DynamicAttributeNS.java new file mode 100644 index 000000000..27c8a8e6e --- /dev/null +++ b/src/main/org/apache/tools/ant/DynamicAttributeNS.java @@ -0,0 +1,40 @@ +/* + * Copyright 2004 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; + +/** + * Enables a task to control unknown attributes. + * + * @since Ant 1.7 + */ +public interface DynamicAttributeNS { + + /** + * Set a named attribute to the given value + * + * @param uri The namespace uri for this attribute, "" is + * used if there is no namespace uri. + * @param localName The localname of this attribute. + * @param qName The qualified name for this attribute + * @param value The value of this attribute. + * @throws BuildException when any error occurs + */ + void setDynamicAttribute( + String uri, String localName, String qName, String value) + throws BuildException; + +} diff --git a/src/main/org/apache/tools/ant/DynamicElement.java b/src/main/org/apache/tools/ant/DynamicElement.java new file mode 100644 index 000000000..30b84007f --- /dev/null +++ b/src/main/org/apache/tools/ant/DynamicElement.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2004 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; + +/** + * Enables a task to control unknown elements. + * + * @since Ant 1.5 + */ +public interface DynamicElement { + + /** + * Create an element with the given name + * + * @param name the element nbame + * @throws BuildException when any error occurs + * @return the element created + */ + Object createDynamicElement(String name) throws BuildException; +} diff --git a/src/main/org/apache/tools/ant/DynamicElementNS.java b/src/main/org/apache/tools/ant/DynamicElementNS.java new file mode 100644 index 000000000..3a27a3dee --- /dev/null +++ b/src/main/org/apache/tools/ant/DynamicElementNS.java @@ -0,0 +1,36 @@ +/* + * Copyright 2004 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; + +/** + * Enables a task to control unknown elements. + * + * @since Ant 1.7 + */ +public interface DynamicElementNS { + /** + * Create an element with the given name + * + * @param uri The namespace uri for this attribute. + * @param localName The localname of this attribute. + * @param qName The qualified name for this element. + * @throws BuildException when any error occurs + * @return the element created for this element. + */ + Object createDynamicElement( + String uri, String localName, String qName) throws BuildException; +} diff --git a/src/main/org/apache/tools/ant/IntrospectionHelper.java b/src/main/org/apache/tools/ant/IntrospectionHelper.java index 63661ebee..aa50722a8 100644 --- a/src/main/org/apache/tools/ant/IntrospectionHelper.java +++ b/src/main/org/apache/tools/ant/IntrospectionHelper.java @@ -489,8 +489,8 @@ public final class IntrospectionHelper implements BuildListener { = (AttributeSetter) attributeSetters.get( attributeName.toLowerCase(Locale.US)); if (as == null) { - if (element instanceof DynamicConfiguratorNS) { - DynamicConfiguratorNS dc = (DynamicConfiguratorNS) element; + if (element instanceof DynamicAttributeNS) { + DynamicAttributeNS dc = (DynamicAttributeNS) element; String uriPlusPrefix = ProjectHelper.extractUriFromComponentName(attributeName); String uri = @@ -502,8 +502,8 @@ public final class IntrospectionHelper implements BuildListener { dc.setDynamicAttribute(uri, localName, qName, value); return; - } else if (element instanceof DynamicConfigurator) { - DynamicConfigurator dc = (DynamicConfigurator) element; + } else if (element instanceof DynamicAttribute) { + DynamicAttribute dc = (DynamicAttribute) element; dc.setDynamicAttribute(attributeName.toLowerCase(Locale.US), value); return; } else { @@ -611,8 +611,8 @@ public final class IntrospectionHelper implements BuildListener { if (nc == null) { nc = createAddTypeCreator(project, parent, elementName); } - if (nc == null && parent instanceof DynamicConfiguratorNS) { - DynamicConfiguratorNS dc = (DynamicConfiguratorNS) parent; + if (nc == null && parent instanceof DynamicElementNS) { + DynamicElementNS dc = (DynamicElementNS) parent; String qName = (child == null ? name : child.getQName()); final Object nestedElement = dc.createDynamicElement( @@ -640,8 +640,8 @@ public final class IntrospectionHelper implements BuildListener { }; } } - if (nc == null && parent instanceof DynamicConfigurator) { - DynamicConfigurator dc = (DynamicConfigurator) parent; + if (nc == null && parent instanceof DynamicElement) { + DynamicElement dc = (DynamicElement) parent; final Object nestedElement = dc.createDynamicElement(name.toLowerCase(Locale.US)); if (nestedElement != null) { @@ -749,8 +749,8 @@ public final class IntrospectionHelper implements BuildListener { */ public boolean supportsNestedElement(String elementName) { return nestedCreators.containsKey(elementName.toLowerCase(Locale.US)) - || DynamicConfigurator.class.isAssignableFrom(bean) - || DynamicConfiguratorNS.class.isAssignableFrom(bean) + || DynamicElement.class.isAssignableFrom(bean) + || DynamicElementNS.class.isAssignableFrom(bean) || addTypeMethods.size() != 0; } @@ -776,8 +776,8 @@ public final class IntrospectionHelper implements BuildListener { return ( nestedCreators.containsKey(name.toLowerCase(Locale.US)) && (uri.equals(parentUri))) // || uri.equals(""))) - || DynamicConfigurator.class.isAssignableFrom(bean) - || DynamicConfiguratorNS.class.isAssignableFrom(bean) + || DynamicElement.class.isAssignableFrom(bean) + || DynamicElementNS.class.isAssignableFrom(bean) || addTypeMethods.size() != 0; } diff --git a/src/main/org/apache/tools/ant/UnknownElement.java b/src/main/org/apache/tools/ant/UnknownElement.java index e8986cb10..c75fe9b4f 100644 --- a/src/main/org/apache/tools/ant/UnknownElement.java +++ b/src/main/org/apache/tools/ant/UnknownElement.java @@ -71,6 +71,13 @@ public class UnknownElement extends Task { this.elementName = elementName; } + /** + * @return the list of nested UnknownElements for this UnknownElement. + */ + public List getChildren() { + return children; + } + /** * Returns the name of the XML element which generated this unknown * element. diff --git a/src/main/org/apache/tools/ant/taskdefs/MacroDef.java b/src/main/org/apache/tools/ant/taskdefs/MacroDef.java index a25f3c923..38cc97544 100644 --- a/src/main/org/apache/tools/ant/taskdefs/MacroDef.java +++ b/src/main/org/apache/tools/ant/taskdefs/MacroDef.java @@ -46,6 +46,7 @@ public class MacroDef extends AntlibDefinition { private Map elements = new HashMap(); private String textName = null; private Text text = null; + private boolean hasImplicitElement = false; /** * Name of the definition @@ -254,6 +255,12 @@ public class MacroDef extends AntlibDefinition { "the element " + element.getName() + " has already been specified"); } + if (hasImplicitElement + || (element.isImplicit() && elements.size() != 0)) { + throw new BuildException( + "Only one element allowed when using implicit elements"); + } + hasImplicitElement = element.isImplicit(); elements.put(element.getName(), element); } @@ -507,6 +514,7 @@ public class MacroDef extends AntlibDefinition { public static class TemplateElement { private String name; private boolean optional = false; + private boolean implicit = false; private String description; /** @@ -546,6 +554,23 @@ public class MacroDef extends AntlibDefinition { return optional; } + /** + * is this element implicit ? + * + * @param implicit if true this element may be left out, default + * is false. + */ + public void setImplicit(boolean implicit) { + this.implicit = implicit; + } + + /** + * @return the implicit attribute + */ + public boolean isImplicit() { + return implicit; + } + /** * @param desc Description of the element. * @since ant 1.6.1 @@ -584,14 +609,15 @@ public class MacroDef extends AntlibDefinition { } else if (!name.equals(other.name)) { return false; } - return optional == other.optional; + return optional == other.optional && implicit == other.implicit; } /** * @return a hash code value for this object. */ public int hashCode() { - return objectHashCode(name) + (optional ? 1 : 0); + return objectHashCode(name) + + (optional ? 1 : 0) + (implicit ? 1 : 0); } } diff --git a/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java b/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java index 452b7b88f..b944a25c3 100644 --- a/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java +++ b/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java @@ -29,7 +29,7 @@ import java.util.Hashtable; import java.util.Enumeration; import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.DynamicConfigurator; +import org.apache.tools.ant.DynamicAttribute; import org.apache.tools.ant.ProjectHelper; import org.apache.tools.ant.RuntimeConfigurable; import org.apache.tools.ant.Target; @@ -44,13 +44,15 @@ import org.apache.tools.ant.UnknownElement; * the parameter values in attributes and text. * @since Ant 1.6 */ -public class MacroInstance extends Task implements DynamicConfigurator { +public class MacroInstance extends Task implements DynamicAttribute, TaskContainer { private MacroDef macroDef; private Map map = new HashMap(); private Map nsElements = null; private Map presentElements = new HashMap(); private Hashtable localProperties = new Hashtable(); private String text = null; + private String implicitTag = null; + private List unknownElements = new ArrayList(); /** * Called from MacroDef.MyAntTypeDefinition#create() @@ -79,22 +81,14 @@ public class MacroInstance extends Task implements DynamicConfigurator { } /** - * Add an element. - * @param name the name of the element - * @return an inner Element type - * @throws BuildException if the name is not known or if this element - * has already been seen + * Method present for BC purposes. + * @param name not used + * @return nothing + * @deprecated + * @throws BuildException always */ public Object createDynamicElement(String name) throws BuildException { - if (getNsElements().get(name) == null) { - throw new BuildException("unsupported element " + name); - } - if (presentElements.get(name) != null) { - throw new BuildException("Element " + name + " already present"); - } - Element ret = new Element(); - presentElements.put(name, ret); - return ret; + throw new BuildException("Not implemented any more"); } private Map getNsElements() { @@ -105,11 +99,43 @@ public class MacroInstance extends Task implements DynamicConfigurator { Map.Entry entry = (Map.Entry) i.next(); nsElements.put((String) entry.getKey(), entry.getValue()); + MacroDef.TemplateElement te = (MacroDef.TemplateElement) + entry.getValue(); + if (te.isImplicit()) { + implicitTag = te.getName(); + } } } return nsElements; } + /** + * Add a unknownElement for the macro instances nested elements. + * + * @param nestedTask a nested element. + */ + public void addTask(Task nestedTask) { + unknownElements.add(nestedTask); + } + + private void processTasks() { + if (implicitTag != null) { + return; + } + for (Iterator i = unknownElements.iterator(); i.hasNext();) { + UnknownElement ue = (UnknownElement) i.next(); + String name = ProjectHelper.extractNameFromComponentName( + ue.getTag()).toLowerCase(Locale.US); + if (getNsElements().get(name) == null) { + throw new BuildException("unsupported element " + name); + } + if (presentElements.get(name) != null) { + throw new BuildException("Element " + name + " already present"); + } + presentElements.put(name, ue.getChildren()); + } + } + /** * Embedded element in macro instance */ @@ -255,9 +281,21 @@ public class MacroInstance extends Task implements DynamicConfigurator { UnknownElement child = copy(unknownElement); rc.addChild(child.getWrapper()); ret.addChild(child); + } else if (templateElement.isImplicit()) { + if (unknownElements.size() == 0 && !templateElement.isOptional()) { + throw new BuildException( + "Missing nested elements for implicit element " + + templateElement.getName()); + } + for (Iterator i = unknownElements.iterator(); + i.hasNext();) { + UnknownElement child = (UnknownElement) i.next(); + rc.addChild(child.getWrapper()); + ret.addChild(child); + } } else { - Element element = (Element) presentElements.get(tag); - if (element == null) { + List list = (List) presentElements.get(tag); + if (list == null) { if (!templateElement.isOptional()) { throw new BuildException( "Required nested element " @@ -265,7 +303,7 @@ public class MacroInstance extends Task implements DynamicConfigurator { } continue; } - for (Iterator i = element.getUnknownElements().iterator(); + for (Iterator i = list.iterator(); i.hasNext();) { UnknownElement child = (UnknownElement) i.next(); rc.addChild(child.getWrapper()); @@ -283,6 +321,8 @@ public class MacroInstance extends Task implements DynamicConfigurator { * */ public void execute() { + getNsElements(); + processTasks(); localProperties = new Hashtable(); Set copyKeys = new HashSet(map.keySet()); for (Iterator i = macroDef.getAttributes().iterator(); i.hasNext();) { diff --git a/src/testcases/org/apache/tools/ant/taskdefs/MacroDefTest.java b/src/testcases/org/apache/tools/ant/taskdefs/MacroDefTest.java index baa0e84c0..793ad25c4 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/MacroDefTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/MacroDefTest.java @@ -108,5 +108,25 @@ public class MacroDefTest extends BuildFileTest { "attribute.description", "description is hello world"); } + public void testImplicit() { + expectLog( + "implicit", "Before implicitIn implicitAfter implicit"); + } + public void testImplicitNotOptional() { + expectSpecificBuildException( + "implicit.notoptional", + "Missing nested elements for implicit element implicit", + "Missing nested elements for implicit element implicit"); + } + public void testImplicitOptional() { + expectLog( + "implicit.optional", "Before implicitAfter implicit"); + } + public void testImplicitExplicit() { + expectSpecificBuildException( + "implicit.explicit", + "Only one element allowed when using implicit elements", + "Only one element allowed when using implicit elements"); + } }