* change nested element name from param to attribute (now the same as scriptdef) * expermintal testing for @attribute notation - controlled by an attributeStyle attribute * checking if correct attribute/element names are used * samedefinition method overloaded git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@275105 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -11,7 +11,7 @@ | |||||
| <h3>Description</h3> | <h3>Description</h3> | ||||
| <p> | <p> | ||||
| This defines a new task using a <sequential> or <parallel> | This defines a new task using a <sequential> or <parallel> | ||||
| nested task as a template. Nested elements <param> and | |||||
| nested task as a template. Nested elements <attribute> and | |||||
| <element> are used to specify attributes and elements of | <element> are used to specify attributes and elements of | ||||
| the new task. These get substituted into the <sequential> | the new task. These get substituted into the <sequential> | ||||
| or <parallel> task when the new task is run. | or <parallel> task when the new task is run. | ||||
| @@ -28,7 +28,7 @@ | |||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| <td valign="top">name</td> | <td valign="top">name</td> | ||||
| <td valign="top">the name of the new definition</td> | |||||
| <td valign="top">The name of the new definition</td> | |||||
| <td valign="top" align="center">Yes</td> | <td valign="top" align="center">Yes</td> | ||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| @@ -38,9 +38,20 @@ | |||||
| </td> | </td> | ||||
| <td valign="top" align="center">No</td> | <td valign="top" align="center">No</td> | ||||
| </tr> | </tr> | ||||
| <tr> | |||||
| <td valign="top">attributestyle</td> | |||||
| <td valign="top"> | |||||
| <em>Temporary</em> | |||||
| this attribute specifies if the attribute is in ant style | |||||
| (i.e. ${attributeName}) or xpath style (i.e @attributeName). | |||||
| Valid values are "ant" and "xpath". The default value | |||||
| is "ant". | |||||
| </td> | |||||
| <td valign="top" align="center">No</td> | |||||
| </tr> | |||||
| </table> | </table> | ||||
| <h3>Parameters specified as nested elements</h3> | <h3>Parameters specified as nested elements</h3> | ||||
| <h4>param</h4> | |||||
| <h4>attribute</h4> | |||||
| <p> | <p> | ||||
| This is used to specify attributes of the new task. The values | This is used to specify attributes of the new task. The values | ||||
| of the attributes get substituted into the templated task. | of the attributes get substituted into the templated task. | ||||
| @@ -52,6 +63,11 @@ | |||||
| task using the ant property notation - ${attribute name}. | task using the ant property notation - ${attribute name}. | ||||
| Note that is not an actual ant property. | Note that is not an actual ant property. | ||||
| </p> | </p> | ||||
| <p> | |||||
| If the attribute style is set to "xpath", the attribute is | |||||
| specified in the body of the template task by prefixing the | |||||
| name with a "@". | |||||
| </p> | |||||
| <h3>Parameters</h3> | <h3>Parameters</h3> | ||||
| <table border="1" cellpadding="2" cellspacing="0"> | <table border="1" cellpadding="2" cellspacing="0"> | ||||
| <tr> | <tr> | ||||
| @@ -61,13 +77,13 @@ | |||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| <td valign="top">name</td> | <td valign="top">name</td> | ||||
| <td valign="top">the name of the new attribute</td> | |||||
| <td valign="top">The name of the new attribute</td> | |||||
| <td valign="top" align="center">Yes</td> | <td valign="top" align="center">Yes</td> | ||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| <td valign="top">default</td> | <td valign="top">default</td> | ||||
| <td valign="top"> | <td valign="top"> | ||||
| the default value of the attribute. | |||||
| The default value of the attribute. | |||||
| </td> | </td> | ||||
| <td valign="top" align="center">No</td> | <td valign="top" align="center">No</td> | ||||
| </tr> | </tr> | ||||
| @@ -87,13 +103,13 @@ | |||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| <td valign="top">name</td> | <td valign="top">name</td> | ||||
| <td valign="top">the name of the new attribute</td> | |||||
| <td valign="top">The name of the new attribute</td> | |||||
| <td valign="top" align="center">Yes</td> | <td valign="top" align="center">Yes</td> | ||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| <td valign="top">optional</td> | <td valign="top">optional</td> | ||||
| <td valign="top"> | <td valign="top"> | ||||
| if true this nested element is optional. Default is | |||||
| If true this nested element is optional. Default is | |||||
| false - i.e the nested element is required in | false - i.e the nested element is required in | ||||
| the new task. | the new task. | ||||
| </td> | </td> | ||||
| @@ -109,7 +125,7 @@ | |||||
| <blockquote> | <blockquote> | ||||
| <pre> | <pre> | ||||
| <macrodef name="testing"> | <macrodef name="testing"> | ||||
| <param name="v" default="NOT SET"/> | |||||
| <attribute name="v" default="NOT SET"/> | |||||
| <element name="some-tasks" optional="yes"/> | <element name="some-tasks" optional="yes"/> | ||||
| <sequential> | <sequential> | ||||
| <echo>v is ${v}</echo> | <echo>v is ${v}</echo> | ||||
| @@ -124,6 +140,23 @@ | |||||
| </testing> | </testing> | ||||
| </pre> | </pre> | ||||
| </blockquote> | </blockquote> | ||||
| <p> | |||||
| The following fragment sets the attribute style to "xpath" | |||||
| for the macro definition <testing> and calls the | |||||
| macro. The fragment should output "attribute is this is a test". | |||||
| </p> | |||||
| <blockquote> | |||||
| <pre> | |||||
| <macrodef name="testing" attributestyle="xpath"> | |||||
| <attribute name="abc"/> | |||||
| <sequential> | |||||
| <echo>attribute is @abc</echo> | |||||
| </sequential> | |||||
| </macrodef> | |||||
| <testing abc="this is a test"/> | |||||
| </pre> | |||||
| </blockquote> | |||||
| <p> | <p> | ||||
| The following fragment defines a task called <call-cc> which | The following fragment defines a task called <call-cc> which | ||||
| take the attributes "target", "link" and "target.dir" and the | take the attributes "target", "link" and "target.dir" and the | ||||
| @@ -134,9 +167,9 @@ | |||||
| <blockquote> | <blockquote> | ||||
| <pre> | <pre> | ||||
| <macrodef name="call-cc"> | <macrodef name="call-cc"> | ||||
| <param name="target"/> | |||||
| <param name="link"/> | |||||
| <param name="target.dir"/> | |||||
| <attribute name="target"/> | |||||
| <attribute name="link"/> | |||||
| <attribute name="target.dir"/> | |||||
| <element name="cc-elements"/> | <element name="cc-elements"/> | ||||
| <sequential> | <sequential> | ||||
| <mkdir dir="${obj.dir}/${target}"/> | <mkdir dir="${obj.dir}/${target}"/> | ||||
| @@ -2,7 +2,7 @@ | |||||
| <target name="simple"> | <target name="simple"> | ||||
| <macrodef name="my.echo"> | <macrodef name="my.echo"> | ||||
| <param name="text"/> | |||||
| <attribute name="text"/> | |||||
| <sequential> | <sequential> | ||||
| <echo message="${text}"/> | <echo message="${text}"/> | ||||
| </sequential> | </sequential> | ||||
| @@ -12,7 +12,7 @@ | |||||
| <target name="text"> | <target name="text"> | ||||
| <macrodef name="my.echo"> | <macrodef name="my.echo"> | ||||
| <param name="text"/> | |||||
| <attribute name="text"/> | |||||
| <sequential> | <sequential> | ||||
| <echo>${text}</echo> | <echo>${text}</echo> | ||||
| </sequential> | </sequential> | ||||
| @@ -22,7 +22,7 @@ | |||||
| <target name="uri"> | <target name="uri"> | ||||
| <macrodef name="echo" uri="abc"> | <macrodef name="echo" uri="abc"> | ||||
| <param name="text"/> | |||||
| <attribute name="text"/> | |||||
| <sequential> | <sequential> | ||||
| <echo message="${text}"/> | <echo message="${text}"/> | ||||
| </sequential> | </sequential> | ||||
| @@ -44,4 +44,15 @@ | |||||
| </nested> | </nested> | ||||
| </nested> | </nested> | ||||
| </target> | </target> | ||||
| <target name="xpathstyle"> | |||||
| <macrodef name="testing" attributestyle="xpath"> | |||||
| <attribute name="abc"/> | |||||
| <sequential> | |||||
| <echo>attribute is @abc@abc</echo> | |||||
| </sequential> | |||||
| </macrodef> | |||||
| <testing abc="this is a test"/> | |||||
| </target> | |||||
| </project> | </project> | ||||
| @@ -68,6 +68,8 @@ import org.apache.tools.ant.Task; | |||||
| import org.apache.tools.ant.TaskContainer; | import org.apache.tools.ant.TaskContainer; | ||||
| import org.apache.tools.ant.UnknownElement; | import org.apache.tools.ant.UnknownElement; | ||||
| import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
| /** | /** | ||||
| * Describe class <code>MacroDef</code> here. | * Describe class <code>MacroDef</code> here. | ||||
| * | * | ||||
| @@ -78,9 +80,10 @@ public class MacroDef extends Task implements AntlibInterface, TaskContainer { | |||||
| private UnknownElement nestedTask; | private UnknownElement nestedTask; | ||||
| private String name; | private String name; | ||||
| private String componentName; | private String componentName; | ||||
| private List params = new ArrayList(); | |||||
| private List attributes = new ArrayList(); | |||||
| private Map elements = new HashMap(); | private Map elements = new HashMap(); | ||||
| private String uri; | |||||
| private String uri; | |||||
| private int attributeStyle = AttributeStyle.ANT; | |||||
| /** | /** | ||||
| * Name of the definition | * Name of the definition | ||||
| @@ -105,6 +108,46 @@ public class MacroDef extends Task implements AntlibInterface, TaskContainer { | |||||
| this.uri = uri; | this.uri = uri; | ||||
| } | } | ||||
| /** | |||||
| * Enumerated type for attributeStyle attribute | |||||
| * | |||||
| * @see EnumeratedAttribute | |||||
| */ | |||||
| public static class AttributeStyle extends EnumeratedAttribute { | |||||
| /** Enumerated values */ | |||||
| public static final int ANT = 0, XPATH = 1; | |||||
| /** | |||||
| * get the values | |||||
| * @return an array of the allowed values for this attribute. | |||||
| */ | |||||
| public String[] getValues() { | |||||
| return new String[] {"ant", "xpath"}; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * <em>Expermential</em> | |||||
| * I am uncertain at the moment how to encode attributes | |||||
| * using ant style ${attribute} or xpath style @attribute. | |||||
| * The first may get mixed up with ant properties and | |||||
| * the second may get mixed up with xpath. | |||||
| * The default at the moment is ant s | |||||
| * | |||||
| * @param style an <code>AttributeStyle</code> value | |||||
| */ | |||||
| public void setAttributeStyle(AttributeStyle style) { | |||||
| attributeStyle = style.getIndex(); | |||||
| } | |||||
| /** | |||||
| * <em>Expermential</em> | |||||
| * @return the attribute style | |||||
| */ | |||||
| public int getAttributeStyle() { | |||||
| return attributeStyle; | |||||
| } | |||||
| /** | /** | ||||
| * Set the class loader. | * Set the class loader. | ||||
| * Not used | * Not used | ||||
| @@ -139,10 +182,10 @@ public class MacroDef extends Task implements AntlibInterface, TaskContainer { | |||||
| } | } | ||||
| /** | /** | ||||
| * @return the nested Params | |||||
| * @return the nested Attributes | |||||
| */ | */ | ||||
| public List getParams() { | |||||
| return params; | |||||
| public List getAttributes() { | |||||
| return attributes; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -153,16 +196,46 @@ public class MacroDef extends Task implements AntlibInterface, TaskContainer { | |||||
| } | } | ||||
| /** | /** | ||||
| * Add a param element. | |||||
| * Check if a character is a valid character for an element or | |||||
| * attribute name | |||||
| * @param c the character to check | |||||
| * @return true if the character is a letter or digit or '.' or '-' | |||||
| * attribute name | |||||
| */ | |||||
| public static boolean isValidNameCharacter(char c) { | |||||
| // ? is there an xml api for this ? | |||||
| return Character.isLetterOrDigit(c) || c == '.' || c == '-'; | |||||
| } | |||||
| /** | |||||
| * Check if a string is a valid name for an element or | |||||
| * attribute | |||||
| * @param name the string to check | |||||
| * @return true if the name consists of valid name characters | |||||
| */ | |||||
| private static boolean isValidName(String name) { | |||||
| if (name.length() == 0) { | |||||
| return false; | |||||
| } | |||||
| for (int i = 0; i < name.length(); ++i) { | |||||
| if (!isValidNameCharacter(name.charAt(i))) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * Add an attribute element. | |||||
| * | * | ||||
| * @param param a param nested element. | |||||
| * @param attribute an attribute nested element. | |||||
| */ | */ | ||||
| public void addConfiguredParam(Param param) { | |||||
| if (param.getName() == null) { | |||||
| public void addConfiguredAttribute(Attribute attribute) { | |||||
| if (attribute.getName() == null) { | |||||
| throw new BuildException( | throw new BuildException( | ||||
| "the param nested element needed a \"name\" attribute"); | |||||
| "the attribute nested element needed a \"name\" attribute"); | |||||
| } | } | ||||
| params.add(param); | |||||
| attributes.add(attribute); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -207,20 +280,24 @@ public class MacroDef extends Task implements AntlibInterface, TaskContainer { | |||||
| * A nested element for the MacroDef task. | * A nested element for the MacroDef task. | ||||
| * | * | ||||
| */ | */ | ||||
| public static class Param { | |||||
| public static class Attribute { | |||||
| private String name; | private String name; | ||||
| private String defaultValue; | private String defaultValue; | ||||
| /** | /** | ||||
| * The name of the parameter. | |||||
| * The name of the attribute. | |||||
| * | * | ||||
| * @param name the name of the parameter | |||||
| * @param name the name of the attribute | |||||
| */ | */ | ||||
| public void setName(String name) { | public void setName(String name) { | ||||
| if (!isValidName(name)) { | |||||
| throw new BuildException( | |||||
| "Illegal name [" + name + "] for attribute"); | |||||
| } | |||||
| this.name = name; | this.name = name; | ||||
| } | } | ||||
| /** | /** | ||||
| * @return the name of the parameter. | |||||
| * @return the name of the attribute | |||||
| */ | */ | ||||
| public String getName() { | public String getName() { | ||||
| return name; | return name; | ||||
| @@ -257,6 +334,10 @@ public class MacroDef extends Task implements AntlibInterface, TaskContainer { | |||||
| * @param name the name of the element. | * @param name the name of the element. | ||||
| */ | */ | ||||
| public void setName(String name) { | public void setName(String name) { | ||||
| if (!isValidName(name)) { | |||||
| throw new BuildException( | |||||
| "Illegal name [" + name + "] for attribute"); | |||||
| } | |||||
| this.name = name; | this.name = name; | ||||
| } | } | ||||
| @@ -316,5 +397,17 @@ public class MacroDef extends Task implements AntlibInterface, TaskContainer { | |||||
| ((MacroInstance) o).setTemplate(template); | ((MacroInstance) o).setTemplate(template); | ||||
| return o; | return o; | ||||
| } | } | ||||
| /** | |||||
| * Equality method for this definition | |||||
| * This only checks for pointer equality. | |||||
| * | |||||
| * @param other another definition | |||||
| * @param project the current project | |||||
| * @return true if the definitions are the same | |||||
| */ | |||||
| public boolean sameDefinition(AntTypeDefinition other, Project project) { | |||||
| return this == other; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -146,7 +146,7 @@ public class MacroInstance extends Task implements DynamicConfigurator { | |||||
| } | } | ||||
| } | } | ||||
| private static String macroSubs(String s, Map macroMapping) { | |||||
| private String macroSubsAnt(String s, Map macroMapping) { | |||||
| StringBuffer ret = new StringBuffer(); | StringBuffer ret = new StringBuffer(); | ||||
| StringBuffer macroName = new StringBuffer(); | StringBuffer macroName = new StringBuffer(); | ||||
| boolean inMacro = false; | boolean inMacro = false; | ||||
| @@ -179,6 +179,59 @@ public class MacroInstance extends Task implements DynamicConfigurator { | |||||
| return ret.toString(); | return ret.toString(); | ||||
| } | } | ||||
| private String macroSubsXPath(String s, Map macroMapping) { | |||||
| StringBuffer ret = new StringBuffer(); | |||||
| StringBuffer macroName = new StringBuffer(); | |||||
| boolean inMacro = false; | |||||
| for (int i = 0; i < s.length(); ++i) { | |||||
| char c = s.charAt(i); | |||||
| if (!inMacro) { | |||||
| if (c == '@') { | |||||
| inMacro = true; | |||||
| } else { | |||||
| ret.append(c); | |||||
| } | |||||
| } else { | |||||
| if (MacroDef.isValidNameCharacter(c)) { | |||||
| macroName.append(c); | |||||
| } else { | |||||
| inMacro = false; | |||||
| String name = macroName.toString(); | |||||
| String value = (String) macroMapping.get(name); | |||||
| if (value == null) { | |||||
| ret.append("@" + name); | |||||
| } else { | |||||
| ret.append(value); | |||||
| } | |||||
| if (c == '@') { | |||||
| inMacro = true; | |||||
| } else { | |||||
| ret.append(c); | |||||
| } | |||||
| macroName = new StringBuffer(); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (inMacro) { | |||||
| String name = macroName.toString(); | |||||
| String value = (String) macroMapping.get(name); | |||||
| if (value == null) { | |||||
| ret.append("@" + name); | |||||
| } else { | |||||
| ret.append(value); | |||||
| } | |||||
| } | |||||
| return ret.toString(); | |||||
| } | |||||
| private String macroSubs(String s, Map macroMapping) { | |||||
| if (template.getAttributeStyle() == MacroDef.AttributeStyle.ANT) { | |||||
| return macroSubsAnt(s, macroMapping); | |||||
| } else { | |||||
| return macroSubsXPath(s, macroMapping); | |||||
| } | |||||
| } | |||||
| private UnknownElement copy(UnknownElement ue) { | private UnknownElement copy(UnknownElement ue) { | ||||
| UnknownElement ret = new UnknownElement(ue.getTag()); | UnknownElement ret = new UnknownElement(ue.getTag()); | ||||
| ret.setNamespace(ue.getNamespace()); | ret.setNamespace(ue.getNamespace()); | ||||
| @@ -234,25 +287,26 @@ public class MacroInstance extends Task implements DynamicConfigurator { | |||||
| /** | /** | ||||
| * Execute the templates instance. | * Execute the templates instance. | ||||
| * Copies the unknown element, substitutes the parameters, | |||||
| * Copies the unknown element, substitutes the attributes, | |||||
| * and calls perform on the unknown element. | * and calls perform on the unknown element. | ||||
| * | * | ||||
| */ | */ | ||||
| public void execute() { | public void execute() { | ||||
| localProperties = new Hashtable(); | localProperties = new Hashtable(); | ||||
| Set copyKeys = new HashSet(map.keySet()); | Set copyKeys = new HashSet(map.keySet()); | ||||
| for (int i = 0; i < template.getParams().size(); ++i) { | |||||
| MacroDef.Param param = (MacroDef.Param) template.getParams().get(i); | |||||
| String value = (String) map.get(param.getName()); | |||||
| for (int i = 0; i < template.getAttributes().size(); ++i) { | |||||
| MacroDef.Attribute attribute = | |||||
| (MacroDef.Attribute) template.getAttributes().get(i); | |||||
| String value = (String) map.get(attribute.getName()); | |||||
| if (value == null) { | if (value == null) { | ||||
| value = param.getDefault(); | |||||
| value = attribute.getDefault(); | |||||
| } | } | ||||
| if (value == null) { | if (value == null) { | ||||
| throw new BuildException( | throw new BuildException( | ||||
| "required parameter " + param.getName() + " not set"); | |||||
| "required attribute " + attribute.getName() + " not set"); | |||||
| } | } | ||||
| localProperties.put(param.getName(), value); | |||||
| copyKeys.remove(param.getName()); | |||||
| localProperties.put(attribute.getName(), value); | |||||
| copyKeys.remove(attribute.getName()); | |||||
| } | } | ||||
| if (copyKeys.size() != 0) { | if (copyKeys.size() != 0) { | ||||
| throw new BuildException( | throw new BuildException( | ||||
| @@ -85,5 +85,9 @@ public class MacroDefTest extends BuildFileTest { | |||||
| public void testNested() { | public void testNested() { | ||||
| expectLog("nested", "A nested element"); | expectLog("nested", "A nested element"); | ||||
| } | } | ||||
| public void testXPathStyle() { | |||||
| expectLog("xpathstyle", "attribute is this is a testthis is a test"); | |||||
| } | |||||
| } | } | ||||