bodewig 2002/11/05 06:51:52
Modified: . WHATSNEW
src/main/org/apache/tools/ant/taskdefs XmlProperty.java
src/testcases/org/apache/tools/ant/taskdefs
XmlPropertyTest.java
Added: src/etc/testcases/taskdefs/xmlproperty/goldfiles
keeproot-collapse-input1.properties
keeproot-collapse-override.properties
keeproot-nocollapse-input1.properties
keeproot-semantic-include.properties
keeproot-semantic-input1.properties
keeproot-semantic-override.properties
nokeeproot-collapse-input1.properties
nokeeproot-nocollapse-input1.properties
nokeeproot-nocollapse-multi.properties
nokeeproot-semantic-include-input1.properties
nokeeproot-semantic-input1.properties
nokeeproot-semantic-locations.properties
nokeeproot-semantic-paths.properties
nokeeproot-semantic-references.properties
src/etc/testcases/taskdefs/xmlproperty/inputs input1.xml
locations.xml multi.xml override.xml paths.xml
references.xml
Log:
Enhancements for <xmlproperty>: you can now expand ${properties},
define ids or paths and use Ant's location magic for filename
resolutions in the XML file.
PR: 11321, 12045
Submitted by: Paul Christmann <paul at priorartisans.com>
Revision Changes Path
1.304 +4 -0 jakarta-ant/WHATSNEW
Index: WHATSNEW
===================================================================
RCS file: /home/cvs/jakarta-ant/WHATSNEW,v
retrieving revision 1.303
retrieving revision 1.304
diff -u -r1.303 -r1.304
--- WHATSNEW 31 Oct 2002 14:30:06 -0000 1.303
+++ WHATSNEW 5 Nov 2002 14:51:51 -0000 1.304
@@ -63,6 +63,10 @@
* <arg> has a new attribute pathref that can be used to reference
previously defined paths.
+* <xmlproperty> has been improved, you can now expand ${properties},
+ define ids or paths and use Ant's location magic for filename resolutions
+ in the XML file.
+
Changes from Ant 1.5.1Beta1 to 1.5.1
====================================
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-input1.properties
Index: keeproot-collapse-input1.properties
===================================================================
properties.root=foo,bar
properties.a.b.c=d
properties.a.b=e
properties.foo.bar=quux,quux1
properties.foo.quux=bar
properties.tag.value=foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-override.properties
Index: keeproot-collapse-override.properties
===================================================================
# Match value hardwired in code, NOT in the input...
override.property.test=foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-nocollapse-input1.properties
Index: keeproot-nocollapse-input1.properties
===================================================================
properties.root=foo,bar
properties.a.b(c)=d
properties.a.b=e
properties.foo(bar)=quux
properties.foo.bar=quux1
properties.foo.quux=bar
properties.tag(value)=foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-include.properties
Index: keeproot-semantic-include.properties
===================================================================
properties.root=foo,bar
properties.a.b.c=d
properties.a.b=e
properties.foo.bar=quux,quux1
properties.foo.quux=bar
properties.tag.value=foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-input1.properties
Index: keeproot-semantic-input1.properties
===================================================================
properties.root=foo,bar
properties.a.b.c=d
properties.a.b=e
properties.foo.bar=quux,quux1
properties.foo.quux=bar
properties.tag=foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-override.properties
Index: keeproot-semantic-override.properties
===================================================================
# Match value hardwired in code, NOT in the input...
override.property.test=foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-collapse-input1.properties
Index: nokeeproot-collapse-input1.properties
===================================================================
root=foo,bar
a.b.c=d
a.b=e
foo.bar=quux,quux1
foo.quux=bar
tag.value=foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-input1.properties
Index: nokeeproot-nocollapse-input1.properties
===================================================================
root=foo,bar
a.b(c)=d
a.b=e
foo(bar)=quux
foo.bar=quux1
foo.quux=bar
tag(value)=foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-multi.properties
Index: nokeeproot-nocollapse-multi.properties
===================================================================
foo.bar=1,2,3,4
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-include-input1.properties
Index: nokeeproot-semantic-include-input1.properties
===================================================================
root=foo,bar
a.b.c=d
a.b=e
foo.bar=quux,quux1
foo.quux=bar
tag.value=foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-input1.properties
Index: nokeeproot-semantic-input1.properties
===================================================================
root=foo,bar
a.b.c=d
a.b=e
foo.bar=quux,quux1
foo.quux=bar
tag=foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-locations.properties
Index: nokeeproot-semantic-locations.properties
===================================================================
file=FILE.foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-paths.properties
Index: nokeeproot-semantic-paths.properties
===================================================================
foo=ID.path
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-references.properties
Index: nokeeproot-semantic-references.properties
===================================================================
property=foo
foo.bar=foo
foo.quux=foo
foo.thunk=foo
foo.property=ID.foo
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/inputs/input1.xml
Index: input1.xml
===================================================================
<properties>
<root>foo</root>
<root>bar</root>
<a><b c="d">e</b></a>
<foo bar="quux">
<bar>quux1</bar>
<quux>bar</quux>
</foo>
<tag value="foo"/>
</properties>
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/inputs/locations.xml
Index: locations.xml
===================================================================
<locations>
<file location="foo"/>
</locations>
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/inputs/multi.xml
Index: multi.xml
===================================================================
<properties>
<foo>
<bar>1</bar>
<bar>2</bar>
<bar>3</bar>
<bar>4</bar>
</foo>
</properties>
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/inputs/override.xml
Index: override.xml
===================================================================
<root>
<override>
<property test="bar"/>
</override>
</root>
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/inputs/paths.xml
Index: paths.xml
===================================================================
<paths>
<classpath pathid="foo">
<path value="bar"/>
</classpath>
</paths>
1.1
jakarta-ant/src/etc/testcases/taskdefs/xmlproperty/inputs/references.xml
Index: references.xml
===================================================================
<references>
<property value="foo" id="foo.property"/>
<foo bar="${property}">
<quux refid="foo.property"/>
<thunk>${property}</thunk>
</foo>
</references>
1.8 +429 -38
jakarta-ant/src/main/org/apache/tools/ant/taskdefs/XmlProperty.java
Index: XmlProperty.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/XmlProperty.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- XmlProperty.java 25 Jul 2002 15:21:06 -0000 1.7
+++ XmlProperty.java 5 Nov 2002 14:51:52 -0000 1.8
@@ -58,36 +58,155 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Enumeration;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
- * Loads property values from a valid XML file,
- * generating the property names from the file's element and attribute names.
+ * Loads property values from a valid XML file, generating the
+ * property names from the file's element and attribute names.
*
- * Example:
+ * <p>Example:</p>
* <pre>
* <root-tag myattr="true">
* <inner-tag someattr="val">Text</inner-tag>
* <a2><a3><a4>false</a4></a3></a2>
+ * <x>x1</x>
+ * <x>x2</x>
* </root-tag>
*</pre>
- * this generates
+ *
+ * <p>this generates the following properties:</p>
+ *
* <pre>
* root-tag(myattr)=true
* root-tag.inner-tag=Text
* root-tag.inner-tag(someattr)=val
* root-tag.a2.a3.a4=false
+ * root-tag.x=x1,x2
+ * </pre>
+ *
+ * <p>The <i>collapseAttributes</i> property of this task can be set
+ * to true (the default is false) which will instead result in the
+ * following properties (note the difference in names of properties
+ * corresponding to XML attributes):</p>
+ *
+ * <pre>
+ * root-tag.myattr=true
+ * root-tag.inner-tag=Text
+ * root-tag.inner-tag.someattr=val
+ * root-tag.a2.a3.a4=false
+ * root-tag.x=x1,x2
+ * </pre>
+ *
+ * <p>Optionally, to more closely mirror the abilities of the Property
+ * task, a selected set of attributes can be treated specially. To
+ * enable this behavior, the "semanticAttribute" property of this task
+ * must be set to true (it defaults to false). If this attribute is
+ * specified, the following attributes take on special meaning
+ * (setting this to true implicitly sets collapseAttributes to true as
+ * well):</p>
+ *
+ * <ul>
+ * <li><b>value</b>: Identifies a text value for a property.</li>
+ * <li><b>location</b>: Identifies a file location for a property.</li>
+ * <li><b>id</b>: Sets an id for a property</li>
+ * <li><b>refid</b>: Sets a property to the value of another property
+ * based upon the provided id</li>
+ * <li><b>pathid</b>: Defines a path rather than a property with
+ * the given id.</li>
+ * </ul>
+ *
+ * <p>For example, with keepRoot = false, the following properties file:</p>
+ *
+ * <pre>
+ * <root-tag>
+ * <build location="build">
+ * <classes id="build.classes"
location="${build.location}/classes"/>
+ * <reference refid="build.location"/>
+ * </build>
+ * <compile>
+ * <classpath pathid="compile.classpath">
+ * <pathelement location="${build.classes}"/>
+ * </classpath>
+ * </compile>
+ * <run-time>
+ * <jars>*.jar</jars>
+ * <classpath pathid="run-time.classpath">
+ * <path refid="compile.classpath"/>
+ * <pathelement path="${run-time.jars}"/>
+ * </classpath>
+ * </run-time>
+ * </root-tag>
* </pre>
+ *
+ * <p>is equivalent to the following entries in a build file:</p>
+ *
+ * <pre>
+ * <property name="build.location" location="build"/>
+ * <property name="build.classes.location"
location="${build.location}/classes"/>
+ * <property name="build.reference" refid="build.location"/>
+ *
+ * <property name="run-time.jars" value="*.jar/>
+ *
+ * <classpath id="compile.classpath">
+ * <pathelement location="${build.classes}"/>
+ * </classpath>
+ *
+ * <classpath id="run-time.classpath">
+ * <path refid="compile.classpath"/>
+ * <pathelement path="${run-time.jars}"/>
+ * </classpath>
+ * </pre>
+ *
+ * <p> This task <i>requires</i> the following attributes:</p>
+ *
+ * <ul>
+ * <li><b>file</b>: The name of the file to load.</li>
+ * </ul>
+ *
+ * <p>This task supports the following attributes:</p>
+ *
+ * <ul>
+ * <li><b>prefix</b>: Optionally specify a prefix applied to
+ * all properties loaded. Defaults to an empty string.</li>
+ * <li><b>keepRoot</b>: Indicate whether the root xml element
+ * is kept as part of property name. Defaults to true.</li>
+ * <li><b>validate</b>: Indicate whether the xml file is validated.
+ * Defaults to false.</li>
+ * <li><b>collapseAttributes</b>: Indicate whether attributes are
+ * stored in property names with parens or with period
+ * delimiters. Defaults to false, meaning properties
+ * are stored with parens (i.e., foo(attr)).</li>
+ * <li><b>semanticAttributes</b>: Indicate whether attributes
+ * named "location", "value", "refid" and "path"
+ * are interpreted as ant properties. Defaults
+ * to true.</li>
+ * <li><b>rootDirectory</b>: Indicate the directory to use
+ * as the root directory for resolving location
+ * properties. Defaults to the directory
+ * of the project using the task.</li>
+ * <li><b>includeSemanticAttribute</b>: Indicate whether to include
+ * the semanticAttribute ("location" or "value") as
+ * part of the property name. Defaults to false.</li>
+ * </ul>
+ *
* @author <a href="mailto:[EMAIL PROTECTED]">Nicola Ken Barozzi</a>
* @author Erik Hatcher
+ * @author <a href="mailto:[EMAIL PROTECTED]">Paul Christmann</a>
+ *
* @ant.task name="xmlproperty" category="xml"
*/
@@ -98,7 +217,21 @@
private boolean keepRoot = true;
private boolean validate = false;
private boolean collapseAttributes = false;
- private org.w3c.dom.Document document;
+ private boolean semanticAttributes = false;
+ private boolean includeSemanticAttribute = false;
+ private File rootDirectory = null;
+ private FileUtils fileUtils = FileUtils.newFileUtils();
+ private Hashtable addedAttributes = new Hashtable();
+
+ private static final String ID = "id";
+ private static final String REF_ID = "refid";
+ private static final String LOCATION = "location";
+ private static final String VALUE = "value";
+ private static final String PATH = "path";
+ private static final String PATHID = "pathid";
+ private static final String[] ATTRIBUTES = new String[] {
+ ID, REF_ID, LOCATION, VALUE, PATH, PATHID
+ };
/**
* Constructor.
@@ -124,6 +257,11 @@
public void execute()
throws BuildException {
+ if (getFile() == null) {
+ String msg = "XmlProperty task requires a file attribute";
+ throw new BuildException(msg);
+ }
+
BufferedInputStream configurationStream = null;
try {
@@ -135,21 +273,17 @@
factory.setValidating(validate);
factory.setNamespaceAware(false);
- DocumentBuilder builder = factory.newDocumentBuilder();
- document = builder.parse(configurationStream);
-
- Element topElement = document.getDocumentElement();
- NodeList topChildren = topElement.getChildNodes();
- int numChildren = topChildren.getLength();
+ Element topElement =
factory.newDocumentBuilder().parse(configurationStream).getDocumentElement();
- log("Using prefix: \"" + prefix + "\"", Project.MSG_DEBUG);
+ addedAttributes = new Hashtable();
if (keepRoot) {
- addNodeRecursively(topElement, prefix);
- }
- else {
+ addNodeRecursively(topElement, prefix, null);
+ } else {
+ NodeList topChildren = topElement.getChildNodes();
+ int numChildren = topChildren.getLength();
for (int i = 0; i < numChildren; i++) {
- addNodeRecursively(topChildren.item(i), prefix);
+ addNodeRecursively(topChildren.item(i), prefix, null);
}
}
@@ -176,52 +310,252 @@
}
}
+ /** Iterate through all nodes in the tree. */
+ private void addNodeRecursively(Node node, String prefix,
+ Object container) {
+
+ // Set the prefix for this node to include its tag name.
+ String nodePrefix = prefix;
+ if (node.getNodeType() != Node.TEXT_NODE) {
+ if (prefix.trim().length() > 0) {
+ nodePrefix += ".";
+ }
+ nodePrefix += node.getNodeName();
+ }
+
+ // Pass the container to the processing of this node,
+ Object nodeObject = processNode(node, nodePrefix, container);
+
+ // now, iterate through children.
+ if (node.hasChildNodes()) {
+
+ NodeList nodeChildren = node.getChildNodes();
+ int numChildren = nodeChildren.getLength();
+
+ for (int i = 0; i < numChildren; i++) {
+ // For each child, pass the object added by
+ // processNode to its children -- in other word, each
+ // object can pass information along to its children.
+ addNodeRecursively(nodeChildren.item(i), nodePrefix,
+ nodeObject);
+ }
+ }
+ }
+
+ void addNodeRecursively(org.w3c.dom.Node node, String prefix) {
+ addNodeRecursively(node, prefix, null);
+ }
+
/**
- * add all attributes of a node, and its inner text, and then
recursively add all nested elements
+ * Process the given node, adding any required attributes from
+ * this child node alone -- but <em>not</em> processing any
+ * children.
+ *
+ * @param node the XML Node to parse
+ * @param prefix A string to prepend to any properties that get
+ * added by this node.
+ * @param container Optionally, an object that a parent node
+ * generated that this node might belong to. For example, this
+ * node could be within a node that generated a Path.
+ * @return the Object created by this node. Generally, this is
+ * either a String if this node resulted in setting an attribute,
+ * or a Path.
*/
+ public Object processNode (Node node, String prefix, Object container) {
- void addNodeRecursively(org.w3c.dom.Node node, String prefix) {
+ // Parse the attribute(s) and text of this node, adding
+ // properties for each.
+ // if the "path" attribute is specified, then return the created path
+ // which will be passed to the children of this node.
+ Object addedPath = null;
+
+ // The value of an id attribute of this node.
+ String id = null;
if (node.hasAttributes()) {
- org.w3c.dom.NamedNodeMap nodeAttributes = node.getAttributes();
+
+ NamedNodeMap nodeAttributes = node.getAttributes();
+
+ // Is there an id attribute?
+ Node idNode = nodeAttributes.getNamedItem(ID);
+ id = (semanticAttributes && idNode != null
+ ? idNode.getNodeValue() : null);
+
+ // Now, iterate through the attributes adding them.
for (int i = 0; i < nodeAttributes.getLength(); i++) {
+
Node attributeNode = nodeAttributes.item(i);
- String attributeName;
- if(collapseAttributes){
- attributeName = prefix + (prefix.trim().equals("")?"":".")
+ node.getNodeName() + "." + attributeNode.getNodeName();
+ if (!semanticAttributes) {
+ String attributeName = getAttributeName(attributeNode);
+ String attributeValue = getAttributeValue(attributeNode);
+ addProperty(prefix + attributeName, attributeValue,
null);
+ } else {
+
+ String nodeName = attributeNode.getNodeName();
+ String attributeValue = getAttributeValue(attributeNode);
+
+ Path containingPath =
+ (container != null && container instanceof Path
+ ? (Path) container : null );
+
+ /*
+ * The main conditional logic -- if the attribute
+ * is somehow "special" (i.e., it has known
+ * semantic meaning) then deal with it
+ * appropriately.
+ */
+ if (nodeName.equals(ID)) {
+ // ID has already been found above.
+ continue;
+ } else if (containingPath != null
+ && nodeName.equals(PATH)) {
+ // A "path" attribute for a node within a Path
object.
+ containingPath.setPath(attributeValue);
+ } else if (container instanceof Path
+ && nodeName.equals(REF_ID)) {
+ // A "refid" attribute for a node within a Path
object.
+ containingPath.setPath(attributeValue);
+ } else if (container instanceof Path
+ && nodeName.equals(LOCATION)) {
+ // A "location" attribute for a node within a
+ // Path object.
+
containingPath.setLocation(resolveFile(attributeValue));
+ } else if (nodeName.equals(PATHID)) {
+ // A node identifying a new path
+ if (container != null) {
+ throw new BuildException("XmlProperty does not "
+ + "support nested
paths");
+ }
+
+ addedPath = new Path(getProject());
+ getProject().addReference(attributeValue, addedPath);
+ } else {
+ // An arbitrary attribute.
+ String attributeName =
getAttributeName(attributeNode);
+ addProperty(prefix + attributeName, attributeValue,
id);
+ }
}
- else{
- attributeName = prefix + (prefix.trim().equals("")?"":".")
+ node.getNodeName() + "(" + attributeNode.getNodeName() + ")";
- }
-
- String attributeValue = attributeNode.getNodeValue();
- log(attributeName + ":" + attributeValue, Project.MSG_DEBUG);
- getProject().setNewProperty(attributeName, attributeValue);
}
}
if (node.getNodeType() == Node.TEXT_NODE) {
- String nodeText = node.getNodeValue();
+ // If the containing object was a String, then use it as the ID.
+ if (semanticAttributes && id == null
+ && container instanceof String) {
+ id = (String) container;
+ }
+ // For the text node, add a property.
+ String nodeText = getAttributeValue(node);
if (nodeText.trim().length() != 0) {
- log(prefix + ":" + nodeText, Project.MSG_DEBUG);
- getProject().setNewProperty(prefix, nodeText);
+ addProperty(prefix, nodeText, id);
}
}
- if (node.hasChildNodes()) {
- prefix += ((prefix.trim().equals("")?"":".") +
node.getNodeName());
+ // Return the Path we added or the ID of this node for
+ // children to reference if needed. Path objects are
+ // definitely used by child path elements, and ID may be used
+ // for a child text node.
+ return (addedPath != null ? addedPath : id);
+ }
- org.w3c.dom.NodeList nodeChildren = node.getChildNodes();
- int numChildren = nodeChildren.getLength();
+ /**
+ * Actually add the given property/value to the project
+ * after writing a log message.
+ */
+ private void addProperty (String name, String value, String id) {
+ String msg = name + ":" + value;
+ if (id != null) {
+ msg += ("(id=" + id + ")");
+ }
+ log(msg, Project.MSG_DEBUG);
- for (int i = 0; i < numChildren; i++) {
- addNodeRecursively(nodeChildren.item(i), prefix);
+ if (addedAttributes.containsKey(name)) {
+ // If this attribute was added by this task, then
+ // we append this value to the existing value.
+ value = (String)addedAttributes.get(name) + "," + value;
+ getProject().setProperty(name, value);
+ } else {
+ getProject().setNewProperty(name, value);
+ }
+ addedAttributes.put(name, value);
+ if (id != null) {
+ getProject().addReference(id, value);
+ }
+ }
+
+ /**
+ * Return a reasonable attribute name for the given node.
+ * If we are using semantic attributes or collapsing
+ * attributes, the returned name is ".nodename".
+ * Otherwise, we return "(nodename)". This is long-standing
+ * (and default) <xmlproperty> behavior.
+ */
+ private String getAttributeName (Node attributeNode) {
+ String attributeName = attributeNode.getNodeName();
+
+ if (semanticAttributes) {
+ // Never include the "refid" attribute as part of the
+ // attribute name.
+ if (attributeName.equals(REF_ID)) {
+ return "";
+ // Otherwise, return it appended unless property to hide it is
set.
+ } else if (!isSemanticAttribute(attributeName)
+ || includeSemanticAttribute) {
+ return "." + attributeName;
+ } else {
+ return "";
}
+ } else if (collapseAttributes) {
+ return "." + attributeName;
+ } else {
+ return "(" + attributeName + ")";
}
}
/**
+ * Return whether the provided attribute name is recognized or not.
+ */
+ private static boolean isSemanticAttribute (String attributeName) {
+ for (int i=0;i<ATTRIBUTES.length;i++) {
+ if (attributeName.equals(ATTRIBUTES[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the value for the given attribute.
+ * If we are not using semantic attributes, its just the
+ * literal string value of the attribute.
+ *
+ * <p>If we <em>are</em> using semantic attributes, then first
+ * dependent properties are resolved (i.e., ${foo} is resolved
+ * based on the foo property value), and then an appropriate data
+ * type is used. In particular, location-based properties are
+ * resolved to absolute file names. Also for refid values, look
+ * up the referenced object from the project.</p>
+ */
+ private String getAttributeValue (Node attributeNode) {
+ String nodeValue = attributeNode.getNodeValue().trim();
+ if (semanticAttributes) {
+ String attributeName = attributeNode.getNodeName();
+ nodeValue = getProject().replaceProperties(nodeValue);
+ if (attributeName.equals(LOCATION)) {
+ File f = resolveFile(nodeValue);
+ return f.getPath();
+ } else if (attributeName.equals(REF_ID)) {
+ Object ref = getProject().getReference(nodeValue);
+ if (ref != null) {
+ return ref.toString();
+ }
+ }
+ }
+ return nodeValue;
+ }
+
+ /**
* The XML file to parse; required.
*/
public void setFile(File src) {
@@ -257,6 +591,63 @@
*/
public void setCollapseAttributes(boolean collapseAttributes) {
this.collapseAttributes = collapseAttributes;
+ }
+
+ public void setSemanticAttributes (boolean semanticAttributes) {
+ this.semanticAttributes = semanticAttributes;
+ }
+
+ public void setRootDirectory (File rootDirectory) {
+ this.rootDirectory = rootDirectory;
+ }
+
+ public void setIncludeSemanticAttribute (boolean
includeSemanticAttribute) {
+ this.includeSemanticAttribute = includeSemanticAttribute;
+ }
+
+ /* Expose members for extensibility */
+
+ protected File getFile () {
+ return this.src;
+ }
+
+ protected String getPrefix () {
+ return this.prefix;
+ }
+
+ protected boolean getKeeproot () {
+ return this.keepRoot;
+ }
+
+ protected boolean getValidate () {
+ return this.validate;
+ }
+
+ protected boolean getCollapseAttributes () {
+ return this.collapseAttributes;
+ }
+
+ protected boolean getSemanticAttributes () {
+ return this.semanticAttributes;
+ }
+
+ protected File getRootDirectory () {
+ return this.rootDirectory;
+ }
+
+ protected boolean getIncludeSementicAttribute () {
+ return this.includeSemanticAttribute;
+ }
+
+ /**
+ * Let project resolve the file - or do it ourselves if
+ * rootDirectory has been set.
+ */
+ private File resolveFile(String fileName) {
+ if (rootDirectory == null) {
+ return getProject().resolveFile(fileName);
+ }
+ return fileUtils.resolveFile(rootDirectory, fileName);
}
}
1.2 +294 -6
jakarta-ant/src/testcases/org/apache/tools/ant/taskdefs/XmlPropertyTest.java
Index: XmlPropertyTest.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/testcases/org/apache/tools/ant/taskdefs/XmlPropertyTest.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- XmlPropertyTest.java 19 Apr 2002 19:51:29 -0000 1.1
+++ XmlPropertyTest.java 5 Nov 2002 14:51:52 -0000 1.2
@@ -54,12 +54,26 @@
package org.apache.tools.ant.taskdefs;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+
import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
/**
* @author Erik Hatcher
+ * @author <a href="mailto:[EMAIL PROTECTED]">Paul Christmann</a>
*/
public class XmlPropertyTest extends BuildFileTest {
+ private static FileUtils fileUtils = FileUtils.newFileUtils();
public XmlPropertyTest(String name) {
super(name);
@@ -69,14 +83,288 @@
configureProject("src/etc/testcases/taskdefs/xmlproperty.xml");
}
-
public void testProperties() {
executeTarget("test");
-// System.out.println(this.getFullLog());
- assertEquals("true", project.getProperty("root-tag(myattr)"));
- assertEquals("Text", project.getProperty("root-tag.inner-tag"));
- assertEquals("val",
project.getProperty("root-tag.inner-tag(someattr)"));
- assertEquals("false", project.getProperty("root-tag.a2.a3.a4"));
+ assertEquals("true", getProject().getProperty("root-tag(myattr)"));
+ assertEquals("Text", getProject().getProperty("root-tag.inner-tag"));
+ assertEquals("val",
+
getProject().getProperty("root-tag.inner-tag(someattr)"));
+ assertEquals("false", getProject().getProperty("root-tag.a2.a3.a4"));
+ }
+
+ public void testNone () {
+ doTest("testNone", false, false, false, false, false);
+ }
+
+ public void testKeeproot() {
+ doTest("testKeeproot", true, false, false, false, false);
+ }
+
+ public void testCollapse () {
+ doTest("testCollapse", false, true, false, false, false);
+ }
+
+ public void testSemantic () {
+ doTest("testSemantic", false, false, true, false, false);
+ }
+
+ public void testKeeprootCollapse () {
+ doTest("testKeeprootCollapse", true, true, false, false, false);
+ }
+
+ public void testKeeprootSemantic () {
+ doTest("testKeeprootSemantic", true, false, true, false, false);
+ }
+
+ public void testCollapseSemantic () {
+ doTest("testCollapseSemantic", false, true, true, false, false);
+ }
+
+ public void testKeeprootCollapseSemantic () {
+ doTest("testKeeprootCollapseSemantic", true, true, true, false,
false);
+ }
+
+ public void testInclude () {
+ doTest("testInclude", false, false, false, true, false);
+ }
+
+ public void testSemanticInclude () {
+ doTest("testSemanticInclude", false, false, true, true, false);
+ }
+
+ public void testSemanticLocal () {
+ doTest("testSemanticInclude", false, false, true, false, true);
+ }
+
+ /**
+ * Actually run a test, finding all input files (and corresponding
+ * goldfile)
+ */
+ private void doTest(String msg, boolean keepRoot, boolean collapse,
+ boolean semantic, boolean include, boolean
localRoot) {
+ Enumeration iter =
+ getFiles(new
File("src/etc/testcases/taskdefs/xmlproperty/inputs"));
+ while (iter.hasMoreElements()) {
+ File inputFile = (File) iter.nextElement();
+ // What's the working directory? If local, then its the
+ // folder of the input file. Otherwise, its the "current" dir..
+ File workingDir;
+ if ( localRoot ) {
+ workingDir = fileUtils.getParentFile(inputFile);
+ } else {
+ workingDir = fileUtils.resolveFile(new File("."), ".");
+ }
+
+ try {
+
+ File propertyFile = getGoldfile(inputFile, keepRoot,
collapse,
+ semantic, include,
localRoot);
+ if (!propertyFile.exists()) {
+// System.out.println("Skipping as "
+// + propertyFile.getAbsolutePath()
+// + ") doesn't exist.");
+ continue;
+ }
+
+ // System.out.println(msg + " (" +
propertyFile.getName() + ") in (" + workingDir + ")");
+
+ Project project = new Project();
+
+ XmlProperty xmlproperty = new XmlProperty();
+ xmlproperty.setProject(project);
+ xmlproperty.setFile(inputFile);
+
+ xmlproperty.setKeeproot(keepRoot);
+ xmlproperty.setCollapseAttributes(collapse);
+ xmlproperty.setSemanticAttributes(semantic);
+ xmlproperty.setIncludeSemanticAttribute(include);
+ xmlproperty.setRootDirectory(workingDir);
+
+ project.setNewProperty("override.property.test", "foo");
+ xmlproperty.execute();
+
+ Properties props = new Properties();
+ props.load(new FileInputStream(propertyFile));
+
+ //printProperties(project.getProperties());
+ ensureProperties(msg, inputFile, workingDir, project, props);
+ ensureReferences(msg, inputFile, project.getReferences());
+
+ } catch (IOException ex) {
+ fail(ex.toString());
+ }
+ }
+ }
+
+ /**
+ * Make sure every property loaded from the goldfile was also
+ * read from the XmlProperty. We could try and test the other way,
+ * but some other properties may get set in the XmlProperty due
+ * to generic Project/Task configuration.
+ */
+ private static void ensureProperties (String msg, File inputFile,
+ File workingDir, Project project,
+ Properties properties) {
+ Hashtable xmlproperties = project.getProperties();
+ // Every key identified by the Properties must have been loaded.
+ Enumeration propertyKeyEnum = properties.propertyNames();
+ while(propertyKeyEnum.hasMoreElements()){
+ String currentKey = propertyKeyEnum.nextElement().toString();
+ String assertMsg = msg + "-" + inputFile.getName()
+ + " Key=" + currentKey;
+
+ String propertyValue = properties.getProperty(currentKey);
+
+ String xmlValue = (String)xmlproperties.get(currentKey);
+
+ if ( propertyValue.indexOf("ID.") == 0 ) {
+ // The property is an id's thing -- either a property
+ // or a path. We need to make sure
+ // that the object was created with the given id.
+ // We don't have an adequate way of testing the actual
+ // *value* of the Path object, though...
+ String id = currentKey;
+ Object obj = project.getReferences().get(id);
+
+ if ( obj == null ) {
+ fail(assertMsg + " Object ID does not exist.");
+ }
+
+ // What is the property supposed to be?
+ propertyValue =
+ propertyValue.substring(3, propertyValue.length());
+ if (propertyValue.equals("path")) {
+ if (!(obj instanceof Path)) {
+ fail(assertMsg + " Path ID is a "
+ + obj.getClass().getName());
+ }
+ } else {
+ assertEquals(assertMsg, propertyValue, obj.toString());
+ }
+
+ } else {
+
+ if (propertyValue.indexOf("FILE.") == 0) {
+ // The property is the name of a file. We are testing
+ // a location attribute, so we need to resolve the given
+ // file name in the provided folder.
+ String fileName =
+ propertyValue.substring(5, propertyValue.length());
+ File f = new File(workingDir, fileName);
+ propertyValue = f.getAbsolutePath();
+ }
+
+ assertEquals(assertMsg, propertyValue, xmlValue);
+ }
+
+ }
}
+ /**
+ * Debugging method to print the properties in the given hashtable
+ */
+ private static void printProperties(Hashtable xmlproperties) {
+ Enumeration keyEnum = xmlproperties.keys();
+ while (keyEnum.hasMoreElements()) {
+ String currentKey = keyEnum.nextElement().toString();
+ System.out.println(currentKey + " = "
+ + xmlproperties.get(currentKey));
+ }
+ }
+
+ /**
+ * Ensure all references loaded by the project are valid.
+ */
+ private static void ensureReferences (String msg, File inputFile,
+ Hashtable references) {
+ Enumeration referenceKeyEnum = references.keys();
+ while(referenceKeyEnum.hasMoreElements()){
+ String currentKey = referenceKeyEnum.nextElement().toString();
+ Object currentValue = references.get(currentKey);
+
+ if (currentValue instanceof Path) {
+ } else if (currentValue instanceof String) {
+ } else {
+ fail(msg + "-" + inputFile.getName() + " Key="
+ + currentKey + " is not a recognized type.");
+ }
+ }
+ }
+
+ /**
+ * Munge the name of the input file to find an appropriate goldfile,
+ * based on hardwired naming conventions.
+ */
+ private static File getGoldfile (File input, boolean keepRoot,
+ boolean collapse, boolean semantic,
+ boolean include, boolean localRoot) {
+ // Substitute .xml with .properties
+ String baseName = input.getName().toLowerCase();
+ if (baseName.endsWith(".xml")) {
+ baseName = baseName.substring(0, baseName.length() - 4)
+ + ".properties";
+ }
+
+ File dir = fileUtils.getParentFile(fileUtils.getParentFile(input));
+
+ String goldFileFolder = "goldfiles/";
+
+ if (keepRoot) {
+ goldFileFolder += "keeproot-";
+ } else {
+ goldFileFolder += "nokeeproot-";
+ }
+
+ if (semantic) {
+ goldFileFolder += "semantic-";
+ if (include) {
+ goldFileFolder += "include-";
+ }
+ } else {
+ if (collapse) {
+ goldFileFolder += "collapse-";
+ } else {
+ goldFileFolder += "nocollapse-";
+ }
+ }
+
+ return new File(dir, goldFileFolder + baseName);
+ }
+
+ /**
+ * Retrieve a list of xml files in the specified folder
+ * and below.
+ */
+ private static Enumeration getFiles (final File startingDir) {
+ Vector result = new Vector();
+ getFiles(startingDir, result);
+ return result.elements();
+ }
+
+ /**
+ * Collect a list of xml files in the specified folder
+ * and below.
+ */
+ private static void getFiles (final File startingDir, Vector collect) {
+ FileFilter filter = new FileFilter() {
+ public boolean accept (File file) {
+ if (file.isDirectory()) {
+ return true;
+ } else {
+ return (file.getPath().indexOf("taskdefs") > 0 &&
+ file.getPath().toLowerCase().endsWith(".xml") );
+ }
+ }
+ };
+
+ File[] files = startingDir.listFiles(filter);
+ for (int i=0;i<files.length;i++) {
+ File f = files[i];
+ if (!f.isDirectory()) {
+ collect.addElement(f);
+ } else {
+ getFiles(f, collect);
+ }
+ }
+ }
}
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>