ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bode...@apache.org
Subject cvs commit: jakarta-ant/src/testcases/org/apache/tools/ant/taskdefs XmlPropertyTest.java
Date Tue, 05 Nov 2002 14:51:52 GMT
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>
    *   &lt;root-tag myattr="true"&gt;
    *     &lt;inner-tag someattr="val"&gt;Text&lt;/inner-tag&gt;
    *     &lt;a2&gt;&lt;a3&gt;&lt;a4&gt;false&lt;/a4&gt;&lt;/a3&gt;&lt;/a2&gt;
  + *     &lt;x&gt;x1&lt;/x&gt;
  + *     &lt;x&gt;x2&lt;/x&gt;
    *   &lt;/root-tag&gt;
    *</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>
  + * &lt;root-tag&gt;
  + *   &lt;build location="build"&gt;
  + *     &lt;classes id="build.classes" location="${build.location}/classes"/&gt;
  + *     &lt;reference refid="build.location"/&gt;
  + *   &lt;/build&gt;
  + *   &lt;compile&gt;
  + *     &lt;classpath pathid="compile.classpath"&gt;
  + *       &lt;pathelement location="${build.classes}"/&gt;
  + *     &lt;/classpath&gt;
  + *   &lt;/compile&gt;
  + *   &lt;run-time&gt;
  + *     &lt;jars&gt;*.jar&lt;/jars&gt;
  + *     &lt;classpath pathid="run-time.classpath"&gt;
  + *       &lt;path refid="compile.classpath"/&gt;
  + *       &lt;pathelement path="${run-time.jars}"/&gt;
  + *     &lt;/classpath&gt;
  + *   &lt;/run-time&gt;
  + * &lt;/root-tag&gt;
    * </pre>
  + *
  + * <p>is equivalent to the following entries in a build file:</p>
  + *
  + * <pre>
  + * &lt;property name="build.location" location="build"/&gt;
  + * &lt;property name="build.classes.location" location="${build.location}/classes"/&gt;
  + * &lt;property name="build.reference" refid="build.location"/&gt;
  + *
  + * &lt;property name="run-time.jars" value="*.jar/&gt;
  + *
  + * &lt;classpath id="compile.classpath"&gt;
  + *   &lt;pathelement location="${build.classes}"/&gt;
  + * &lt;/classpath&gt;
  + *
  + * &lt;classpath id="run-time.classpath"&gt;
  + *   &lt;path refid="compile.classpath"/&gt;
  + *   &lt;pathelement path="${run-time.jars}"/&gt;
  + * &lt;/classpath&gt;
  + * </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:nicolaken@apache.org">Nicola Ken Barozzi</a>
    * @author Erik Hatcher
  + * @author <a href="mailto:paul@priorartisans.com">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) &lt;xmlproperty&gt; 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:paul@priorartisans.com">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:ant-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:ant-dev-help@jakarta.apache.org>


Mime
View raw message