001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs;
020:
021: import java.io.File;
022: import java.io.IOException;
023: import java.util.Hashtable;
024: import javax.xml.parsers.DocumentBuilderFactory;
025: import javax.xml.parsers.DocumentBuilder;
026: import javax.xml.parsers.ParserConfigurationException;
027: import org.apache.tools.ant.BuildException;
028: import org.apache.tools.ant.Project;
029: import org.apache.tools.ant.types.Path;
030: import org.apache.tools.ant.types.Resource;
031: import org.apache.tools.ant.types.ResourceCollection;
032: import org.apache.tools.ant.types.XMLCatalog;
033: import org.apache.tools.ant.types.resources.FileResource;
034: import org.apache.tools.ant.util.FileUtils;
035: import org.w3c.dom.Document;
036: import org.w3c.dom.Element;
037: import org.w3c.dom.NamedNodeMap;
038: import org.w3c.dom.Node;
039: import org.w3c.dom.NodeList;
040: import org.xml.sax.SAXException;
041: import org.xml.sax.EntityResolver;
042:
043: /**
044: * Loads property values from a valid XML file, generating the
045: * property names from the file's element and attribute names.
046: *
047: * <p>Example:</p>
048: * <pre>
049: * <root-tag myattr="true">
050: * <inner-tag someattr="val">Text</inner-tag>
051: * <a2><a3><a4>false</a4></a3></a2>
052: * <x>x1</x>
053: * <x>x2</x>
054: * </root-tag>
055: *</pre>
056: *
057: * <p>this generates the following properties:</p>
058: *
059: * <pre>
060: * root-tag(myattr)=true
061: * root-tag.inner-tag=Text
062: * root-tag.inner-tag(someattr)=val
063: * root-tag.a2.a3.a4=false
064: * root-tag.x=x1,x2
065: * </pre>
066: *
067: * <p>The <i>collapseAttributes</i> property of this task can be set
068: * to true (the default is false) which will instead result in the
069: * following properties (note the difference in names of properties
070: * corresponding to XML attributes):</p>
071: *
072: * <pre>
073: * root-tag.myattr=true
074: * root-tag.inner-tag=Text
075: * root-tag.inner-tag.someattr=val
076: * root-tag.a2.a3.a4=false
077: * root-tag.x=x1,x2
078: * </pre>
079: *
080: * <p>Optionally, to more closely mirror the abilities of the Property
081: * task, a selected set of attributes can be treated specially. To
082: * enable this behavior, the "semanticAttributes" property of this task
083: * must be set to true (it defaults to false). If this attribute is
084: * specified, the following attributes take on special meaning
085: * (setting this to true implicitly sets collapseAttributes to true as
086: * well):</p>
087: *
088: * <ul>
089: * <li><b>value</b>: Identifies a text value for a property.</li>
090: * <li><b>location</b>: Identifies a file location for a property.</li>
091: * <li><b>id</b>: Sets an id for a property</li>
092: * <li><b>refid</b>: Sets a property to the value of another property
093: * based upon the provided id</li>
094: * <li><b>pathid</b>: Defines a path rather than a property with
095: * the given id.</li>
096: * </ul>
097: *
098: * <p>For example, with keepRoot = false, the following properties file:</p>
099: *
100: * <pre>
101: * <root-tag>
102: * <build>
103: * <build folder="build">
104: * <classes id="build.classes" location="${build.folder}/classes"/>
105: * <reference refid="build.classes"/>
106: * </build>
107: * <compile>
108: * <classpath pathid="compile.classpath">
109: * <pathelement location="${build.classes}"/>
110: * </classpath>
111: * </compile>
112: * <run-time>
113: * <jars>*.jar</jars>
114: * <classpath pathid="run-time.classpath">
115: * <path refid="compile.classpath"/>
116: * <pathelement path="${run-time.jars}"/>
117: * </classpath>
118: * </run-time>
119: * </root-tag>
120: * </pre>
121: *
122: * <p>is equivalent to the following entries in a build file:</p>
123: *
124: * <pre>
125: * <property name="build" location="build"/>
126: * <property name="build.classes" location="${build.location}/classes"/>
127: * <property name="build.reference" refid="build.classes"/>
128: *
129: * <property name="run-time.jars" value="*.jar/>
130: *
131: * <classpath id="compile.classpath">
132: * <pathelement location="${build.classes}"/>
133: * </classpath>
134: *
135: * <classpath id="run-time.classpath">
136: * <path refid="compile.classpath"/>
137: * <pathelement path="${run-time.jars}"/>
138: * </classpath>
139: * </pre>
140: *
141: * <p> This task <i>requires</i> the following attributes:</p>
142: *
143: * <ul>
144: * <li><b>file</b>: The name of the file to load.</li>
145: * </ul>
146: *
147: * <p>This task supports the following attributes:</p>
148: *
149: * <ul>
150: * <li><b>prefix</b>: Optionally specify a prefix applied to
151: * all properties loaded. Defaults to an empty string.</li>
152: * <li><b>keepRoot</b>: Indicate whether the root xml element
153: * is kept as part of property name. Defaults to true.</li>
154: * <li><b>validate</b>: Indicate whether the xml file is validated.
155: * Defaults to false.</li>
156: * <li><b>collapseAttributes</b>: Indicate whether attributes are
157: * stored in property names with parens or with period
158: * delimiters. Defaults to false, meaning properties
159: * are stored with parens (i.e., foo(attr)).</li>
160: * <li><b>semanticAttributes</b>: Indicate whether attributes
161: * named "location", "value", "refid" and "path"
162: * are interpreted as ant properties. Defaults
163: * to false.</li>
164: * <li><b>rootDirectory</b>: Indicate the directory to use
165: * as the root directory for resolving location
166: * properties. Defaults to the directory
167: * of the project using the task.</li>
168: * <li><b>includeSemanticAttribute</b>: Indicate whether to include
169: * the semantic attribute ("location" or "value") as
170: * part of the property name. Defaults to false.</li>
171: * </ul>
172: *
173: * @ant.task name="xmlproperty" category="xml"
174: */
175:
176: public class XmlProperty extends org.apache.tools.ant.Task {
177:
178: private Resource src;
179: private String prefix = "";
180: private boolean keepRoot = true;
181: private boolean validate = false;
182: private boolean collapseAttributes = false;
183: private boolean semanticAttributes = false;
184: private boolean includeSemanticAttribute = false;
185: private File rootDirectory = null;
186: private Hashtable addedAttributes = new Hashtable();
187: private XMLCatalog xmlCatalog = new XMLCatalog();
188:
189: private static final String ID = "id";
190: private static final String REF_ID = "refid";
191: private static final String LOCATION = "location";
192: private static final String VALUE = "value";
193: private static final String PATH = "path";
194: private static final String PATHID = "pathid";
195: private static final String[] ATTRIBUTES = new String[] { ID,
196: REF_ID, LOCATION, VALUE, PATH, PATHID };
197: private static final FileUtils FILE_UTILS = FileUtils
198: .getFileUtils();
199:
200: /**
201: * Constructor.
202: */
203: public XmlProperty() {
204: super ();
205: }
206:
207: /**
208: * Initializes the task.
209: */
210:
211: public void init() {
212: super .init();
213: xmlCatalog.setProject(getProject());
214: }
215:
216: /**
217: * @return the xmlCatalog as the entityresolver.
218: */
219: protected EntityResolver getEntityResolver() {
220: return xmlCatalog;
221: }
222:
223: /**
224: * Run the task.
225: * @throws BuildException The exception raised during task execution.
226: * @todo validate the source file is valid before opening, print a better error message
227: * @todo add a verbose level log message listing the name of the file being loaded
228: */
229: public void execute() throws BuildException {
230:
231: Resource r = getResource();
232:
233: if (r == null) {
234: String msg = "XmlProperty task requires a source resource";
235: throw new BuildException(msg);
236: }
237:
238: try {
239: log("Loading " + src, Project.MSG_VERBOSE);
240:
241: if (r.isExists()) {
242:
243: DocumentBuilderFactory factory = DocumentBuilderFactory
244: .newInstance();
245: factory.setValidating(validate);
246: factory.setNamespaceAware(false);
247: DocumentBuilder builder = factory.newDocumentBuilder();
248: builder.setEntityResolver(getEntityResolver());
249: Document document = null;
250: if (src instanceof FileResource) {
251: document = builder.parse(((FileResource) src)
252: .getFile());
253: } else {
254: document = builder.parse(src.getInputStream());
255: }
256: Element topElement = document.getDocumentElement();
257:
258: // Keep a hashtable of attributes added by this task.
259: // This task is allow to override its own properties
260: // but not other properties. So we need to keep track
261: // of which properties we've added.
262: addedAttributes = new Hashtable();
263:
264: if (keepRoot) {
265: addNodeRecursively(topElement, prefix, null);
266: } else {
267: NodeList topChildren = topElement.getChildNodes();
268: int numChildren = topChildren.getLength();
269: for (int i = 0; i < numChildren; i++) {
270: addNodeRecursively(topChildren.item(i), prefix,
271: null);
272: }
273: }
274:
275: } else {
276: log("Unable to find property resource: " + r,
277: Project.MSG_VERBOSE);
278: }
279:
280: } catch (SAXException sxe) {
281: // Error generated during parsing
282: Exception x = sxe;
283: if (sxe.getException() != null) {
284: x = sxe.getException();
285: }
286: throw new BuildException("Failed to load " + src, x);
287:
288: } catch (ParserConfigurationException pce) {
289: // Parser with specified options can't be built
290: throw new BuildException(pce);
291: } catch (IOException ioe) {
292: // I/O error
293: throw new BuildException("Failed to load " + src, ioe);
294: }
295: }
296:
297: /** Iterate through all nodes in the tree. */
298: private void addNodeRecursively(Node node, String prefix,
299: Object container) {
300:
301: // Set the prefix for this node to include its tag name.
302: String nodePrefix = prefix;
303: if (node.getNodeType() != Node.TEXT_NODE) {
304: if (prefix.trim().length() > 0) {
305: nodePrefix += ".";
306: }
307: nodePrefix += node.getNodeName();
308: }
309:
310: // Pass the container to the processing of this node,
311: Object nodeObject = processNode(node, nodePrefix, container);
312:
313: // now, iterate through children.
314: if (node.hasChildNodes()) {
315:
316: NodeList nodeChildren = node.getChildNodes();
317: int numChildren = nodeChildren.getLength();
318:
319: for (int i = 0; i < numChildren; i++) {
320: // For each child, pass the object added by
321: // processNode to its children -- in other word, each
322: // object can pass information along to its children.
323: addNodeRecursively(nodeChildren.item(i), nodePrefix,
324: nodeObject);
325: }
326: }
327: }
328:
329: void addNodeRecursively(org.w3c.dom.Node node, String prefix) {
330: addNodeRecursively(node, prefix, null);
331: }
332:
333: /**
334: * Process the given node, adding any required attributes from
335: * this child node alone -- but <em>not</em> processing any
336: * children.
337: *
338: * @param node the XML Node to parse
339: * @param prefix A string to prepend to any properties that get
340: * added by this node.
341: * @param container Optionally, an object that a parent node
342: * generated that this node might belong to. For example, this
343: * node could be within a node that generated a Path.
344: * @return the Object created by this node. Generally, this is
345: * either a String if this node resulted in setting an attribute,
346: * or a Path.
347: */
348: public Object processNode(Node node, String prefix, Object container) {
349:
350: // Parse the attribute(s) and text of this node, adding
351: // properties for each.
352: // if the "path" attribute is specified, then return the created path
353: // which will be passed to the children of this node.
354: Object addedPath = null;
355:
356: // The value of an id attribute of this node.
357: String id = null;
358:
359: if (node.hasAttributes()) {
360:
361: NamedNodeMap nodeAttributes = node.getAttributes();
362:
363: // Is there an id attribute?
364: Node idNode = nodeAttributes.getNamedItem(ID);
365: id = (semanticAttributes && idNode != null ? idNode
366: .getNodeValue() : null);
367:
368: // Now, iterate through the attributes adding them.
369: for (int i = 0; i < nodeAttributes.getLength(); i++) {
370:
371: Node attributeNode = nodeAttributes.item(i);
372:
373: if (!semanticAttributes) {
374: String attributeName = getAttributeName(attributeNode);
375: String attributeValue = getAttributeValue(attributeNode);
376: addProperty(prefix + attributeName, attributeValue,
377: null);
378: } else {
379:
380: String nodeName = attributeNode.getNodeName();
381: String attributeValue = getAttributeValue(attributeNode);
382:
383: Path containingPath = (container != null
384: && container instanceof Path ? (Path) container
385: : null);
386:
387: /*
388: * The main conditional logic -- if the attribute
389: * is somehow "special" (i.e., it has known
390: * semantic meaning) then deal with it
391: * appropriately.
392: */
393: if (nodeName.equals(ID)) {
394: // ID has already been found above.
395: continue;
396: } else if (containingPath != null
397: && nodeName.equals(PATH)) {
398: // A "path" attribute for a node within a Path object.
399: containingPath.setPath(attributeValue);
400: } else if (container instanceof Path
401: && nodeName.equals(REF_ID)) {
402: // A "refid" attribute for a node within a Path object.
403: containingPath.setPath(attributeValue);
404: } else if (container instanceof Path
405: && nodeName.equals(LOCATION)) {
406: // A "location" attribute for a node within a
407: // Path object.
408: containingPath
409: .setLocation(resolveFile(attributeValue));
410: } else if (nodeName.equals(PATHID)) {
411: // A node identifying a new path
412: if (container != null) {
413: throw new BuildException(
414: "XmlProperty does not "
415: + "support nested paths");
416: }
417:
418: addedPath = new Path(getProject());
419: getProject().addReference(attributeValue,
420: addedPath);
421: } else {
422: // An arbitrary attribute.
423: String attributeName = getAttributeName(attributeNode);
424: addProperty(prefix + attributeName,
425: attributeValue, id);
426: }
427: }
428: }
429: }
430:
431: String nodeText = null;
432: boolean emptyNode = false;
433: boolean semanticEmptyOverride = false;
434: if (node.getNodeType() == Node.ELEMENT_NODE
435: && semanticAttributes
436: && node.hasAttributes()
437: && (node.getAttributes().getNamedItem(VALUE) != null
438: || node.getAttributes().getNamedItem(LOCATION) != null
439: || node.getAttributes().getNamedItem(REF_ID) != null
440: || node.getAttributes().getNamedItem(PATH) != null || node
441: .getAttributes().getNamedItem(PATHID) != null)) {
442: semanticEmptyOverride = true;
443: }
444: if (node.getNodeType() == Node.TEXT_NODE) {
445: // For the text node, add a property.
446: nodeText = getAttributeValue(node);
447: } else if ((node.getNodeType() == Node.ELEMENT_NODE)
448: && (node.getChildNodes().getLength() == 1)
449: && (node.getFirstChild().getNodeType() == Node.CDATA_SECTION_NODE)) {
450:
451: nodeText = node.getFirstChild().getNodeValue();
452: if ("".equals(nodeText) && !semanticEmptyOverride) {
453: emptyNode = true;
454: }
455: } else if ((node.getNodeType() == Node.ELEMENT_NODE)
456: && (node.getChildNodes().getLength() == 0)
457: && !semanticEmptyOverride) {
458: nodeText = "";
459: emptyNode = true;
460: } else if ((node.getNodeType() == Node.ELEMENT_NODE)
461: && (node.getChildNodes().getLength() == 1)
462: && (node.getFirstChild().getNodeType() == Node.TEXT_NODE)
463: && ("".equals(node.getFirstChild().getNodeValue()))
464: && !semanticEmptyOverride) {
465: nodeText = "";
466: emptyNode = true;
467: }
468:
469: if (nodeText != null) {
470: // If the containing object was a String, then use it as the ID.
471: if (semanticAttributes && id == null
472: && container instanceof String) {
473: id = (String) container;
474: }
475: if (nodeText.trim().length() != 0 || emptyNode) {
476: addProperty(prefix, nodeText, id);
477: }
478: }
479:
480: // Return the Path we added or the ID of this node for
481: // children to reference if needed. Path objects are
482: // definitely used by child path elements, and ID may be used
483: // for a child text node.
484: return (addedPath != null ? addedPath : id);
485: }
486:
487: /**
488: * Actually add the given property/value to the project
489: * after writing a log message.
490: */
491: private void addProperty(String name, String value, String id) {
492: String msg = name + ":" + value;
493: if (id != null) {
494: msg += ("(id=" + id + ")");
495: }
496: log(msg, Project.MSG_DEBUG);
497:
498: if (addedAttributes.containsKey(name)) {
499: // If this attribute was added by this task, then
500: // we append this value to the existing value.
501: // We use the setProperty method which will
502: // forcibly override the property if it already exists.
503: // We need to put these properties into the project
504: // when we read them, though (instead of keeping them
505: // outside of the project and batch adding them at the end)
506: // to allow other properties to reference them.
507: value = (String) addedAttributes.get(name) + "," + value;
508: getProject().setProperty(name, value);
509: addedAttributes.put(name, value);
510: } else if (getProject().getProperty(name) == null) {
511: getProject().setNewProperty(name, value);
512: addedAttributes.put(name, value);
513: } else {
514: log("Override ignored for property " + name,
515: Project.MSG_VERBOSE);
516: }
517: if (id != null) {
518: getProject().addReference(id, value);
519: }
520: }
521:
522: /**
523: * Return a reasonable attribute name for the given node.
524: * If we are using semantic attributes or collapsing
525: * attributes, the returned name is ".nodename".
526: * Otherwise, we return "(nodename)". This is long-standing
527: * (and default) <xmlproperty> behavior.
528: */
529: private String getAttributeName(Node attributeNode) {
530: String attributeName = attributeNode.getNodeName();
531:
532: if (semanticAttributes) {
533: // Never include the "refid" attribute as part of the
534: // attribute name.
535: if (attributeName.equals(REF_ID)) {
536: return "";
537: // Otherwise, return it appended unless property to hide it is set.
538: } else if (!isSemanticAttribute(attributeName)
539: || includeSemanticAttribute) {
540: return "." + attributeName;
541: } else {
542: return "";
543: }
544: } else if (collapseAttributes) {
545: return "." + attributeName;
546: } else {
547: return "(" + attributeName + ")";
548: }
549: }
550:
551: /**
552: * Return whether the provided attribute name is recognized or not.
553: */
554: private static boolean isSemanticAttribute(String attributeName) {
555: for (int i = 0; i < ATTRIBUTES.length; i++) {
556: if (attributeName.equals(ATTRIBUTES[i])) {
557: return true;
558: }
559: }
560: return false;
561: }
562:
563: /**
564: * Return the value for the given attribute.
565: * If we are not using semantic attributes, its just the
566: * literal string value of the attribute.
567: *
568: * <p>If we <em>are</em> using semantic attributes, then first
569: * dependent properties are resolved (i.e., ${foo} is resolved
570: * based on the foo property value), and then an appropriate data
571: * type is used. In particular, location-based properties are
572: * resolved to absolute file names. Also for refid values, look
573: * up the referenced object from the project.</p>
574: */
575: private String getAttributeValue(Node attributeNode) {
576: String nodeValue = attributeNode.getNodeValue().trim();
577: if (semanticAttributes) {
578: String attributeName = attributeNode.getNodeName();
579: nodeValue = getProject().replaceProperties(nodeValue);
580: if (attributeName.equals(LOCATION)) {
581: File f = resolveFile(nodeValue);
582: return f.getPath();
583: } else if (attributeName.equals(REF_ID)) {
584: Object ref = getProject().getReference(nodeValue);
585: if (ref != null) {
586: return ref.toString();
587: }
588: }
589: }
590: return nodeValue;
591: }
592:
593: /**
594: * The XML file to parse; required.
595: * @param src the file to parse
596: */
597: public void setFile(File src) {
598: setSrcResource(new FileResource(src));
599: }
600:
601: /**
602: * The resource to pack; required.
603: * @param src resource to expand
604: */
605: public void setSrcResource(Resource src) {
606: if (src.isDirectory()) {
607: throw new BuildException("the source can't be a directory");
608: }
609: if (src instanceof FileResource && !supportsNonFileResources()) {
610: throw new BuildException("Only FileSystem resources are"
611: + " supported.");
612: }
613: this .src = src;
614: }
615:
616: /**
617: * Set the source resource.
618: * @param a the resource to pack as a single element Resource collection.
619: */
620: public void addConfigured(ResourceCollection a) {
621: if (a.size() != 1) {
622: throw new BuildException(
623: "only single argument resource collections"
624: + " are supported as archives");
625: }
626: setSrcResource((Resource) a.iterator().next());
627: }
628:
629: /**
630: * the prefix to prepend to each property
631: * @param prefix the prefix to prepend to each property
632: */
633: public void setPrefix(String prefix) {
634: this .prefix = prefix.trim();
635: }
636:
637: /**
638: * flag to include the xml root tag as a
639: * first value in the property name; optional,
640: * default is true
641: * @param keepRoot if true (default), include the xml root tag
642: */
643: public void setKeeproot(boolean keepRoot) {
644: this .keepRoot = keepRoot;
645: }
646:
647: /**
648: * flag to validate the XML file; optional, default false
649: * @param validate if true validate the XML file, default false
650: */
651: public void setValidate(boolean validate) {
652: this .validate = validate;
653: }
654:
655: /**
656: * flag to treat attributes as nested elements;
657: * optional, default false
658: * @param collapseAttributes if true treat attributes as nested elements
659: */
660: public void setCollapseAttributes(boolean collapseAttributes) {
661: this .collapseAttributes = collapseAttributes;
662: }
663:
664: /**
665: * Attribute to enable special handling of attributes - see ant manual.
666: * @param semanticAttributes if true enable the special handling.
667: */
668: public void setSemanticAttributes(boolean semanticAttributes) {
669: this .semanticAttributes = semanticAttributes;
670: }
671:
672: /**
673: * The directory to use for resolving file references.
674: * Ignored if semanticAttributes is not set to true.
675: * @param rootDirectory the directory.
676: */
677: public void setRootDirectory(File rootDirectory) {
678: this .rootDirectory = rootDirectory;
679: }
680:
681: /**
682: * Include the semantic attribute name as part of the property name.
683: * Ignored if semanticAttributes is not set to true.
684: * @param includeSemanticAttribute if true include the sematic attribute
685: * name.
686: */
687: public void setIncludeSemanticAttribute(
688: boolean includeSemanticAttribute) {
689: this .includeSemanticAttribute = includeSemanticAttribute;
690: }
691:
692: /**
693: * add an XMLCatalog as a nested element; optional.
694: * @param catalog the XMLCatalog to use
695: */
696: public void addConfiguredXMLCatalog(XMLCatalog catalog) {
697: xmlCatalog.addConfiguredXMLCatalog(catalog);
698: }
699:
700: /* Expose members for extensibility */
701:
702: /**
703: * @return the file attribute.
704: */
705: protected File getFile() {
706: if (src instanceof FileResource) {
707: return ((FileResource) src).getFile();
708: } else {
709: return null;
710: }
711: }
712:
713: /**
714: * @return the resource.
715: */
716: protected Resource getResource() {
717: // delegate this way around to support subclasses that
718: // overwrite getFile
719: File f = getFile();
720: if (f != null) {
721: return new FileResource(f);
722: } else {
723: return src;
724: }
725: }
726:
727: /**
728: * @return the prefix attribute.
729: */
730: protected String getPrefix() {
731: return this .prefix;
732: }
733:
734: /**
735: * @return the keeproot attribute.
736: */
737: protected boolean getKeeproot() {
738: return this .keepRoot;
739: }
740:
741: /**
742: * @return the validate attribute.
743: */
744: protected boolean getValidate() {
745: return this .validate;
746: }
747:
748: /**
749: * @return the collapse attributes attribute.
750: */
751: protected boolean getCollapseAttributes() {
752: return this .collapseAttributes;
753: }
754:
755: /**
756: * @return the semantic attributes attribute.
757: */
758: protected boolean getSemanticAttributes() {
759: return this .semanticAttributes;
760: }
761:
762: /**
763: * @return the root directory attribute.
764: */
765: protected File getRootDirectory() {
766: return this .rootDirectory;
767: }
768:
769: /**
770: * @return the include semantic attribute.
771: */
772: protected boolean getIncludeSementicAttribute() {
773: return this .includeSemanticAttribute;
774: }
775:
776: /**
777: * Let project resolve the file - or do it ourselves if
778: * rootDirectory has been set.
779: */
780: private File resolveFile(String fileName) {
781: if (rootDirectory == null) {
782: return FILE_UTILS.resolveFile(getProject().getBaseDir(),
783: fileName);
784: }
785: return FILE_UTILS.resolveFile(rootDirectory, fileName);
786: }
787:
788: /**
789: * Whether this task can deal with non-file resources.
790: *
791: * <p>This implementation returns true only if this task is
792: * <xmlproperty>. Any subclass of this class that also wants to
793: * support non-file resources needs to override this method. We
794: * need to do so for backwards compatibility reasons since we
795: * can't expect subclasses to support resources.</p>
796: * @return true for this task.
797: * @since Ant 1.7
798: */
799: protected boolean supportsNonFileResources() {
800: return getClass().equals(XmlProperty.class);
801: }
802: }
|