PR: 25633 git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@276490 13f79535-47bb-0310-9956-ffa450edef68master
@@ -131,6 +131,17 @@ | |||
</td> | |||
<td valign="top" align="center">No</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">implicit</td> | |||
<td valign="top"> | |||
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. <em>since ant 1.6.2</em> | |||
</td> | |||
<td valign="top" align="center">No</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">description</td> | |||
<td valign="top"> | |||
@@ -254,6 +265,45 @@ | |||
<linker refid="linker-libs"/> | |||
</cc-elements> | |||
</call-cc> | |||
</pre> | |||
</blockquote> | |||
<p> | |||
The following fragment shows <call-cc>, but this time | |||
using an implicit element and with the link and target.dir arguments | |||
having default values. | |||
</p> | |||
<blockquote> | |||
<pre class="code"> | |||
<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> | |||
</pre> | |||
</blockquote> | |||
<p> | |||
This then can be used as follows, note that <cc-elements> | |||
is not specified. | |||
</p> | |||
<blockquote> | |||
<pre class="code"> | |||
<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> | |||
</pre> | |||
</blockquote> | |||
<p> | |||
@@ -168,4 +168,59 @@ | |||
<d description="hello world"/> | |||
</target> | |||
<target name="implicit"> | |||
<macrodef name="implicit"> | |||
<element name="implicit" implicit="yes"/> | |||
<sequential> | |||
<echo>Before implicit</echo> | |||
<implicit/> | |||
<echo>After implicit</echo> | |||
</sequential> | |||
</macrodef> | |||
<implicit> | |||
<echo>In implicit</echo> | |||
</implicit> | |||
</target> | |||
<target name="implicit.notoptional"> | |||
<macrodef name="implicit"> | |||
<element name="implicit" implicit="yes"/> | |||
<sequential> | |||
<echo>Before implicit</echo> | |||
<implicit/> | |||
<echo>After implicit</echo> | |||
</sequential> | |||
</macrodef> | |||
<implicit> | |||
</implicit> | |||
</target> | |||
<target name="implicit.optional"> | |||
<macrodef name="implicit"> | |||
<element name="implicit" optional="yes" implicit="yes"/> | |||
<sequential> | |||
<echo>Before implicit</echo> | |||
<implicit/> | |||
<echo>After implicit</echo> | |||
</sequential> | |||
</macrodef> | |||
<implicit> | |||
</implicit> | |||
</target> | |||
<target name="implicit.explicit"> | |||
<macrodef name="implicit"> | |||
<element name="explicit" optional="yes"/> | |||
<element name="implicit" optional="yes" implicit="yes"/> | |||
<sequential> | |||
<implicit/> | |||
<explicit/> | |||
</sequential> | |||
</macrodef> | |||
</target> | |||
</project> |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
@@ -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. | |||
@@ -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); | |||
} | |||
} | |||
@@ -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();) { | |||
@@ -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"); | |||
} | |||
} | |||