Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 674   Methods: 27
NCLOC: 287   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
XmlProperty.java 83.3% 81.6% 63% 80%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2002-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant.taskdefs;
 56   
 
 57   
 import java.io.BufferedInputStream;
 58   
 import java.io.File;
 59   
 import java.io.FileInputStream;
 60   
 import java.io.IOException;
 61   
 import java.util.Hashtable;
 62   
 import java.util.Enumeration;
 63   
 import javax.xml.parsers.DocumentBuilder;
 64   
 import javax.xml.parsers.DocumentBuilderFactory;
 65   
 import javax.xml.parsers.ParserConfigurationException;
 66   
 import org.apache.tools.ant.BuildException;
 67   
 import org.apache.tools.ant.Project;
 68   
 import org.apache.tools.ant.types.Path;
 69   
 import org.apache.tools.ant.types.Reference;
 70   
 import org.apache.tools.ant.util.FileUtils;
 71   
 import org.w3c.dom.Element;
 72   
 import org.w3c.dom.NamedNodeMap;
 73   
 import org.w3c.dom.Node;
 74   
 import org.w3c.dom.NodeList;
 75   
 import org.xml.sax.SAXException;
 76   
 
 77   
 /**
 78   
  * Loads property values from a valid XML file, generating the
 79   
  * property names from the file's element and attribute names.
 80   
  *
 81   
  * <p>Example:</p>
 82   
  * <pre>
 83   
  *   &lt;root-tag myattr="true"&gt;
 84   
  *     &lt;inner-tag someattr="val"&gt;Text&lt;/inner-tag&gt;
 85   
  *     &lt;a2&gt;&lt;a3&gt;&lt;a4&gt;false&lt;/a4&gt;&lt;/a3&gt;&lt;/a2&gt;
 86   
  *     &lt;x&gt;x1&lt;/x&gt;
 87   
  *     &lt;x&gt;x2&lt;/x&gt;
 88   
  *   &lt;/root-tag&gt;
 89   
  *</pre>
 90   
  *
 91   
  * <p>this generates the following properties:</p>
 92   
  *
 93   
  * <pre>
 94   
  *  root-tag(myattr)=true
 95   
  *  root-tag.inner-tag=Text
 96   
  *  root-tag.inner-tag(someattr)=val
 97   
  *  root-tag.a2.a3.a4=false
 98   
  *  root-tag.x=x1,x2
 99   
  * </pre>
 100   
  *
 101   
  * <p>The <i>collapseAttributes</i> property of this task can be set
 102   
  * to true (the default is false) which will instead result in the
 103   
  * following properties (note the difference in names of properties
 104   
  * corresponding to XML attributes):</p>
 105   
  *
 106   
  * <pre>
 107   
  *  root-tag.myattr=true
 108   
  *  root-tag.inner-tag=Text
 109   
  *  root-tag.inner-tag.someattr=val
 110   
  *  root-tag.a2.a3.a4=false
 111   
  *  root-tag.x=x1,x2
 112   
  * </pre>
 113   
  *
 114   
  * <p>Optionally, to more closely mirror the abilities of the Property
 115   
  * task, a selected set of attributes can be treated specially.  To
 116   
  * enable this behavior, the "semanticAttributes" property of this task
 117   
  * must be set to true (it defaults to false).  If this attribute is
 118   
  * specified, the following attributes take on special meaning
 119   
  * (setting this to true implicitly sets collapseAttributes to true as
 120   
  * well):</p>
 121   
  *
 122   
  * <ul>
 123   
  *  <li><b>value</b>: Identifies a text value for a property.</li>
 124   
  *  <li><b>location</b>: Identifies a file location for a property.</li>
 125   
  *  <li><b>id</b>: Sets an id for a property</li>
 126   
  *  <li><b>refid</b>: Sets a property to the value of another property
 127   
  *       based upon the provided id</li>
 128   
  *  <li><b>pathid</b>: Defines a path rather than a property with
 129   
  *       the given id.</li>
 130   
  * </ul>
 131   
  *
 132   
  * <p>For example, with keepRoot = false, the following properties file:</p>
 133   
  *
 134   
  * <pre>
 135   
  * &lt;root-tag&gt;
 136   
  *   &lt;build&gt;
 137   
  *   &lt;build folder="build"&gt;
 138   
  *     &lt;classes id="build.classes" location="${build.folder}/classes"/&gt;
 139   
  *     &lt;reference refid="build.classes"/&gt;
 140   
  *   &lt;/build&gt;
 141   
  *   &lt;compile&gt;
 142   
  *     &lt;classpath pathid="compile.classpath"&gt;
 143   
  *       &lt;pathelement location="${build.classes}"/&gt;
 144   
  *     &lt;/classpath&gt;
 145   
  *   &lt;/compile&gt;
 146   
  *   &lt;run-time&gt;
 147   
  *     &lt;jars&gt;*.jar&lt;/jars&gt;
 148   
  *     &lt;classpath pathid="run-time.classpath"&gt;
 149   
  *       &lt;path refid="compile.classpath"/&gt;
 150   
  *       &lt;pathelement path="${run-time.jars}"/&gt;
 151   
  *     &lt;/classpath&gt;
 152   
  *   &lt;/run-time&gt;
 153   
  * &lt;/root-tag&gt;
 154   
  * </pre>
 155   
  *
 156   
  * <p>is equivalent to the following entries in a build file:</p>
 157   
  *
 158   
  * <pre>
 159   
  * &lt;property name="build" location="build"/&gt;
 160   
  * &lt;property name="build.classes" location="${build.location}/classes"/&gt;
 161   
  * &lt;property name="build.reference" refid="build.classes"/&gt;
 162   
  *
 163   
  * &lt;property name="run-time.jars" value="*.jar/&gt;
 164   
  *
 165   
  * &lt;classpath id="compile.classpath"&gt;
 166   
  *   &lt;pathelement location="${build.classes}"/&gt;
 167   
  * &lt;/classpath&gt;
 168   
  *
 169   
  * &lt;classpath id="run-time.classpath"&gt;
 170   
  *   &lt;path refid="compile.classpath"/&gt;
 171   
  *   &lt;pathelement path="${run-time.jars}"/&gt;
 172   
  * &lt;/classpath&gt;
 173   
  * </pre>
 174   
  *
 175   
  * <p> This task <i>requires</i> the following attributes:</p>
 176   
  *
 177   
  * <ul>
 178   
  * <li><b>file</b>: The name of the file to load.</li>
 179   
  * </ul>
 180   
  *
 181   
  * <p>This task supports the following attributes:</p>
 182   
  *
 183   
  * <ul>
 184   
  * <li><b>prefix</b>: Optionally specify a prefix applied to
 185   
  *     all properties loaded.  Defaults to an empty string.</li>
 186   
  * <li><b>keepRoot</b>: Indicate whether the root xml element
 187   
  *     is kept as part of property name.  Defaults to true.</li>
 188   
  * <li><b>validate</b>: Indicate whether the xml file is validated.
 189   
  *     Defaults to false.</li>
 190   
  * <li><b>collapseAttributes</b>: Indicate whether attributes are
 191   
  *     stored in property names with parens or with period
 192   
  *     delimiters.  Defaults to false, meaning properties
 193   
  *     are stored with parens (i.e., foo(attr)).</li>
 194   
  * <li><b>semanticAttributes</b>: Indicate whether attributes
 195   
  *     named "location", "value", "refid" and "path"
 196   
  *     are interpreted as ant properties.  Defaults
 197   
  *     to false.</li>
 198   
  * <li><b>rootDirectory</b>: Indicate the directory to use
 199   
  *     as the root directory for resolving location
 200   
  *     properties.  Defaults to the directory
 201   
  *     of the project using the task.</li>
 202   
  * <li><b>includeSemanticAttribute</b>: Indicate whether to include
 203   
  *     the semantic attribute ("location" or "value") as
 204   
  *     part of the property name.  Defaults to false.</li>
 205   
  * </ul>
 206   
  *
 207   
  * @author <a href="mailto:nicolaken@apache.org">Nicola Ken Barozzi</a>
 208   
  * @author Erik Hatcher
 209   
  * @author <a href="mailto:paul@priorartisans.com">Paul Christmann</a>
 210   
  *
 211   
  * @ant.task name="xmlproperty" category="xml"
 212   
  */
 213   
 
 214   
 public class XmlProperty extends org.apache.tools.ant.Task {
 215   
 
 216   
     private File src;
 217   
     private String prefix = "";
 218   
     private boolean keepRoot = true;
 219   
     private boolean validate = false;
 220   
     private boolean collapseAttributes = false;
 221   
     private boolean semanticAttributes = false;
 222   
     private boolean includeSemanticAttribute = false;
 223   
     private File rootDirectory = null;
 224   
     private FileUtils fileUtils = FileUtils.newFileUtils();
 225   
     private Hashtable addedAttributes = new Hashtable();
 226   
 
 227   
     private static final String ID = "id";
 228   
     private static final String REF_ID = "refid";
 229   
     private static final String LOCATION = "location";
 230   
     private static final String VALUE = "value";
 231   
     private static final String PATH = "path";
 232   
     private static final String PATHID = "pathid";
 233   
     private static final String[] ATTRIBUTES = new String[] {
 234   
         ID, REF_ID, LOCATION, VALUE, PATH, PATHID
 235   
     };
 236   
 
 237   
     /**
 238   
      * Constructor.
 239   
      */
 240  31
     public XmlProperty() {
 241  31
         super();
 242   
     }
 243   
 
 244   
     /**
 245   
      * Initializes the task.
 246   
      */
 247   
 
 248  1
     public void init() {
 249  1
         super.init();
 250   
     }
 251   
 
 252   
     /**
 253   
      * Run the task.
 254   
      * @throws BuildException The exception raised during task execution.
 255   
      * @todo validate the source file is valid before opening, print a better error message
 256   
      * @todo add a verbose level log message listing the name of the file being loaded
 257   
      */
 258  31
     public void execute()
 259   
             throws BuildException {
 260   
 
 261  31
         if (getFile() == null) {
 262  0
             String msg = "XmlProperty task requires a file attribute";
 263  0
             throw new BuildException(msg);
 264   
         }
 265   
 
 266  31
         BufferedInputStream configurationStream = null;
 267   
 
 268  31
         try {
 269  31
             log("Loading " + src.getAbsolutePath(), Project.MSG_VERBOSE);
 270   
            
 271  31
             if (src.exists()) {
 272   
 
 273  31
               configurationStream =
 274   
                       new BufferedInputStream(new FileInputStream(src));
 275   
 
 276  31
               DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 277   
 
 278  31
               factory.setValidating(validate);
 279  31
               factory.setNamespaceAware(false);
 280   
 
 281  31
               Element topElement = factory.newDocumentBuilder().parse(configurationStream).getDocumentElement();
 282   
 
 283   
               // Keep a hashtable of attributes added by this task.
 284   
               // This task is allow to override its own properties
 285   
               // but not other properties.  So we need to keep track
 286   
               // of which properties we've added.
 287  31
               addedAttributes = new Hashtable();
 288   
 
 289  31
               if (keepRoot) {
 290  10
                   addNodeRecursively(topElement, prefix, null);
 291   
               } else {
 292  21
                   NodeList topChildren = topElement.getChildNodes();
 293  21
                   int numChildren = topChildren.getLength();
 294  21
                   for (int i = 0; i < numChildren; i++) {
 295  131
                     addNodeRecursively(topChildren.item(i), prefix, null);
 296   
                   }
 297   
               }
 298   
 
 299   
             } else {
 300  0
                 log("Unable to find property file: " + src.getAbsolutePath(),
 301   
                     Project.MSG_VERBOSE);
 302   
             }
 303   
             
 304   
         } catch (SAXException sxe) {
 305   
             // Error generated during parsing
 306  0
             Exception x = sxe;
 307  0
             if (sxe.getException() != null)
 308  0
                 x = sxe.getException();
 309  0
             throw new BuildException(x);
 310   
 
 311   
         } catch (ParserConfigurationException pce) {
 312   
             // Parser with specified options can't be built
 313  0
             throw new BuildException(pce);
 314   
         } catch (IOException ioe) {
 315   
             // I/O error
 316  0
             throw new BuildException(ioe);
 317   
         } finally {
 318  31
             if (configurationStream != null) {
 319  31
                 try {
 320  31
                     configurationStream.close();
 321   
                 } catch (Exception e) {
 322   
                 }
 323   
             }
 324   
         }
 325   
     }
 326   
 
 327   
     /** Iterate through all nodes in the tree. */
 328  422
     private void addNodeRecursively(Node node, String prefix, 
 329   
                                     Object container) {
 330   
 
 331   
         // Set the prefix for this node to include its tag name.
 332  422
         String nodePrefix = prefix;
 333  422
         if (node.getNodeType() != Node.TEXT_NODE) {
 334  162
             if (prefix.trim().length() > 0) {
 335  97
                 nodePrefix += ".";
 336   
             }
 337  162
             nodePrefix += node.getNodeName();
 338   
         }
 339   
 
 340   
         // Pass the container to the processing of this node,
 341  422
         Object nodeObject = processNode(node, nodePrefix, container);
 342   
 
 343   
         // now, iterate through children.
 344  422
         if (node.hasChildNodes()) {
 345   
 
 346  133
             NodeList nodeChildren = node.getChildNodes();
 347  133
             int numChildren = nodeChildren.getLength();
 348   
 
 349  133
             for (int i = 0; i < numChildren; i++) {
 350   
                 // For each child, pass the object added by
 351   
                 // processNode to its children -- in other word, each
 352   
                 // object can pass information along to its children.
 353  281
                 addNodeRecursively(nodeChildren.item(i), nodePrefix, 
 354   
                                    nodeObject);
 355   
             }
 356   
         }
 357   
     }
 358   
 
 359  0
     void addNodeRecursively(org.w3c.dom.Node node, String prefix) {
 360  0
         addNodeRecursively(node, prefix, null);
 361   
     }
 362   
 
 363   
     /**
 364   
      * Process the given node, adding any required attributes from
 365   
      * this child node alone -- but <em>not</em> processing any
 366   
      * children.
 367   
      *
 368   
      * @param node the XML Node to parse
 369   
      * @param prefix A string to prepend to any properties that get
 370   
      * added by this node.
 371   
      * @param container Optionally, an object that a parent node
 372   
      * generated that this node might belong to.  For example, this
 373   
      * node could be within a node that generated a Path.
 374   
      * @return the Object created by this node.  Generally, this is
 375   
      * either a String if this node resulted in setting an attribute,
 376   
      * or a Path.
 377   
      */
 378  422
     public Object processNode (Node node, String prefix, Object container) {
 379   
 
 380   
         // Parse the attribute(s) and text of this node, adding
 381   
         // properties for each.
 382   
         // if the "path" attribute is specified, then return the created path
 383   
         // which will be passed to the children of this node.
 384  422
         Object addedPath = null;
 385   
 
 386   
         // The value of an id attribute of this node.
 387  422
         String id = null;
 388   
 
 389  422
         if (node.hasAttributes()) {
 390   
 
 391  63
             NamedNodeMap nodeAttributes = node.getAttributes();
 392   
 
 393   
             // Is there an id attribute?
 394  63
             Node idNode = nodeAttributes.getNamedItem(ID);
 395  63
             id = (semanticAttributes && idNode != null 
 396   
                   ? idNode.getNodeValue() : null);
 397   
 
 398   
             // Now, iterate through the attributes adding them.
 399  63
             for (int i = 0; i < nodeAttributes.getLength(); i++) {
 400   
 
 401  66
                 Node attributeNode = nodeAttributes.item(i);
 402   
 
 403  66
                 if (!semanticAttributes) {
 404  25
                     String attributeName = getAttributeName(attributeNode);
 405  25
                     String attributeValue = getAttributeValue(attributeNode);
 406  25
                     addProperty(prefix + attributeName, attributeValue, null);
 407   
                 } else {
 408   
 
 409  41
                     String nodeName = attributeNode.getNodeName();
 410  41
                     String attributeValue = getAttributeValue(attributeNode);
 411   
 
 412  41
                     Path containingPath = 
 413   
                         (container != null && container instanceof Path 
 414   
                          ? (Path) container : null );
 415   
 
 416   
                     /*
 417   
                      * The main conditional logic -- if the attribute
 418   
                      * is somehow "special" (i.e., it has known
 419   
                      * semantic meaning) then deal with it
 420   
                      * appropriately.
 421   
                      */
 422  41
                     if (nodeName.equals(ID)) {
 423   
                         // ID has already been found above.
 424  3
                         continue;
 425  38
                     } else if (containingPath != null 
 426   
                                && nodeName.equals(PATH)) {
 427   
                         // A "path" attribute for a node within a Path object.
 428  0
                         containingPath.setPath(attributeValue);
 429  38
                     } else if (container instanceof Path 
 430   
                                && nodeName.equals(REF_ID)) {
 431   
                         // A "refid" attribute for a node within a Path object.
 432  0
                         containingPath.setPath(attributeValue);
 433  38
                     } else if (container instanceof Path 
 434   
                                && nodeName.equals(LOCATION)) {
 435   
                         // A "location" attribute for a node within a
 436   
                         // Path object.
 437  0
                         containingPath.setLocation(resolveFile(attributeValue));
 438  38
                     } else if (nodeName.equals(PATHID)) {
 439   
                         // A node identifying a new path
 440  3
                         if (container != null) {
 441  0
                             throw new BuildException("XmlProperty does not "
 442   
                                                      + "support nested paths");
 443   
                         }
 444   
 
 445  3
                         addedPath = new Path(getProject());
 446  3
                         getProject().addReference(attributeValue, addedPath);
 447   
                     } else {
 448   
                         // An arbitrary attribute.
 449  35
                         String attributeName = getAttributeName(attributeNode);
 450  35
                         addProperty(prefix + attributeName, attributeValue, id);
 451   
                     }
 452   
                 }
 453   
             }
 454   
         }
 455   
 
 456  422
         if (node.getNodeType() == Node.TEXT_NODE) {
 457   
             // If the containing object was a String, then use it as the ID.
 458  260
             if (semanticAttributes && id == null 
 459   
                 && container instanceof String) {
 460  0
                 id = (String) container;
 461   
             }
 462   
             // For the text node, add a property.
 463  260
             String nodeText = getAttributeValue(node);
 464  260
             if (nodeText.trim().length() != 0) {
 465  78
                 addProperty(prefix, nodeText, id);
 466   
             }
 467   
         }
 468   
 
 469   
         // Return the Path we added or the ID of this node for
 470   
         // children to reference if needed.  Path objects are
 471   
         // definitely used by child path elements, and ID may be used
 472   
         // for a child text node.
 473  422
         return (addedPath != null ? addedPath : id);
 474   
     }
 475   
 
 476   
     /**
 477   
      * Actually add the given property/value to the project
 478   
      * after writing a log message.
 479   
      */
 480  138
     private void addProperty (String name, String value, String id) {
 481  138
         String msg = name + ":" + value;
 482  138
         if (id != null) {
 483  3
             msg += ("(id=" + id + ")");
 484   
         }
 485  138
         log(msg, Project.MSG_DEBUG);
 486   
 
 487  138
         if (addedAttributes.containsKey(name)) {
 488   
             // If this attribute was added by this task, then
 489   
             // we append this value to the existing value.
 490   
             // We use the setProperty method which will
 491   
             // forcibly override the property if it already exists.
 492   
             // We need to put these properties into the project
 493   
             // when we read them, though (instead of keeping them
 494   
             // outside of the project and batch adding them at the end)
 495   
             // to allow other properties to reference them.
 496  25
             value = (String)addedAttributes.get(name) + "," + value;
 497  25
             getProject().setProperty(name, value);
 498   
         } else {
 499  113
             getProject().setNewProperty(name, value);
 500   
         }
 501  138
         addedAttributes.put(name, value);
 502  138
         if (id != null) {
 503  3
             getProject().addReference(id, value);
 504   
         }
 505   
     }
 506   
 
 507   
     /**
 508   
      * Return a reasonable attribute name for the given node.
 509   
      * If we are using semantic attributes or collapsing
 510   
      * attributes, the returned name is ".nodename".
 511   
      * Otherwise, we return "(nodename)".  This is long-standing
 512   
      * (and default) &lt;xmlproperty&gt; behavior.
 513   
      */
 514  60
     private String getAttributeName (Node attributeNode) {
 515  60
         String attributeName = attributeNode.getNodeName();
 516   
 
 517  60
         if (semanticAttributes) {
 518   
             // Never include the "refid" attribute as part of the
 519   
             // attribute name.
 520  35
             if (attributeName.equals(REF_ID)) {
 521  3
                 return "";
 522   
             // Otherwise, return it appended unless property to hide it is set.
 523  32
             } else if (!isSemanticAttribute(attributeName) 
 524   
                        || includeSemanticAttribute) {
 525  18
                 return "." + attributeName;
 526   
             } else {
 527  14
                 return "";
 528   
             }
 529  25
         } else if (collapseAttributes) {
 530  10
             return "." + attributeName;
 531   
         } else {
 532  15
             return "(" + attributeName + ")";
 533   
         }
 534   
     }
 535   
 
 536   
     /**
 537   
      * Return whether the provided attribute name is recognized or not.
 538   
      */
 539  32
     private static boolean isSemanticAttribute (String attributeName) {
 540  32
         for (int i=0;i<ATTRIBUTES.length;i++) {
 541  159
             if (attributeName.equals(ATTRIBUTES[i])) {
 542  15
                 return true;
 543   
             }
 544   
         }
 545  17
         return false;
 546   
     }
 547   
 
 548   
     /**
 549   
      * Return the value for the given attribute.
 550   
      * If we are not using semantic attributes, its just the
 551   
      * literal string value of the attribute.
 552   
      *
 553   
      * <p>If we <em>are</em> using semantic attributes, then first
 554   
      * dependent properties are resolved (i.e., ${foo} is resolved
 555   
      * based on the foo property value), and then an appropriate data
 556   
      * type is used.  In particular, location-based properties are
 557   
      * resolved to absolute file names.  Also for refid values, look
 558   
      * up the referenced object from the project.</p>
 559   
      */
 560  326
     private String getAttributeValue (Node attributeNode) {
 561  326
         String nodeValue = attributeNode.getNodeValue().trim();
 562  326
         if (semanticAttributes) {
 563  174
             String attributeName = attributeNode.getNodeName();
 564  174
             nodeValue = getProject().replaceProperties(nodeValue);
 565  174
             if (attributeName.equals(LOCATION)) {
 566  3
                 File f = resolveFile(nodeValue);
 567  3
                 return f.getPath();
 568  171
             } else if (attributeName.equals(REF_ID)) {
 569  3
                 Object ref = getProject().getReference(nodeValue);
 570  3
                 if (ref != null) {
 571  3
                     return ref.toString();
 572   
                 }
 573   
             }
 574   
         }
 575  320
         return nodeValue;
 576   
     }
 577   
 
 578   
     /**
 579   
      * The XML file to parse; required.
 580   
      */
 581  31
     public void setFile(File src) {
 582  31
         this.src = src;
 583   
     }
 584   
 
 585   
     /**
 586   
      * the prefix to prepend to each property
 587   
      */
 588  0
     public void setPrefix(String prefix) {
 589  0
         this.prefix = prefix.trim();
 590   
     }
 591   
 
 592   
     /**
 593   
      * flag to include the xml root tag as a
 594   
      * first value in the property name; optional,
 595   
      * default is true
 596   
      */
 597  30
     public void setKeeproot(boolean keepRoot) {
 598  30
         this.keepRoot = keepRoot;
 599   
     }
 600   
 
 601   
     /**
 602   
      * flag to validate the XML file; optional, default false
 603   
      */
 604  0
     public void setValidate(boolean validate) {
 605  0
         this.validate = validate;
 606   
     }
 607   
 
 608   
     /**
 609   
      * flag to treat attributes as nested elements;
 610   
      * optional, default false
 611   
      */
 612  30
     public void setCollapseAttributes(boolean collapseAttributes) {
 613  30
         this.collapseAttributes = collapseAttributes;
 614   
     }
 615   
 
 616  30
     public void setSemanticAttributes (boolean semanticAttributes) {
 617  30
         this.semanticAttributes = semanticAttributes;
 618   
     }
 619   
 
 620  30
     public void setRootDirectory (File rootDirectory) {
 621  30
         this.rootDirectory = rootDirectory;
 622   
     }
 623   
 
 624  30
     public void setIncludeSemanticAttribute (boolean includeSemanticAttribute) {
 625  30
         this.includeSemanticAttribute = includeSemanticAttribute;
 626   
     }
 627   
 
 628   
     /* Expose members for extensibility */
 629   
 
 630  31
     protected File getFile () {
 631  31
         return this.src;
 632   
     }
 633   
 
 634  0
     protected String getPrefix () {
 635  0
         return this.prefix;
 636   
     }
 637   
 
 638  0
     protected boolean getKeeproot () {
 639  0
         return this.keepRoot;
 640   
     }
 641   
 
 642  0
     protected boolean getValidate () {
 643  0
         return this.validate;
 644   
     }
 645   
 
 646  0
     protected boolean getCollapseAttributes () {
 647  0
         return this.collapseAttributes;
 648   
     }
 649   
 
 650  0
     protected boolean getSemanticAttributes () {
 651  0
         return this.semanticAttributes;
 652   
     }
 653   
 
 654  0
     protected File getRootDirectory () {
 655  0
         return this.rootDirectory;
 656   
     }
 657   
 
 658  0
     protected boolean getIncludeSementicAttribute () {
 659  0
         return this.includeSemanticAttribute;
 660   
     }
 661   
 
 662   
     /**
 663   
      * Let project resolve the file - or do it ourselves if
 664   
      * rootDirectory has been set.
 665   
      */
 666  3
     private File resolveFile(String fileName) {
 667  3
         if (rootDirectory == null) {
 668  0
             return getProject().resolveFile(fileName);
 669   
         }
 670  3
         return fileUtils.resolveFile(rootDirectory, fileName);
 671   
     }
 672   
 
 673   
 }
 674