📄 xmlproperty.java
字号:
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */package org.apache.tools.ant.taskdefs;import java.io.File;import java.io.IOException;import java.util.Hashtable;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.DocumentBuilder;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.Resource;import org.apache.tools.ant.types.ResourceCollection;import org.apache.tools.ant.types.XMLCatalog;import org.apache.tools.ant.types.resources.FileResource;import org.apache.tools.ant.util.FileUtils;import org.w3c.dom.Document;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;import org.xml.sax.EntityResolver;/** * Loads property values from a valid XML file, generating the * property names from the file's element and attribute names. * * <p>Example:</p> * <pre> * <root-tag myattr="true"> * <inner-tag someattr="val">Text</inner-tag> * <a2><a3><a4>false</a4></a3></a2> * <x>x1</x> * <x>x2</x> * </root-tag> *</pre> * * <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 "semanticAttributes" property of this task * must be set to true (it defaults to false). If this attribute is * specified, the following attributes take on special meaning * (setting this to true implicitly sets collapseAttributes to true as * well):</p> * * <ul> * <li><b>value</b>: Identifies a text value for a property.</li> * <li><b>location</b>: Identifies a file location for a property.</li> * <li><b>id</b>: Sets an id for a property</li> * <li><b>refid</b>: Sets a property to the value of another property * based upon the provided id</li> * <li><b>pathid</b>: Defines a path rather than a property with * the given id.</li> * </ul> * * <p>For example, with keepRoot = false, the following properties file:</p> * * <pre> * <root-tag> * <build> * <build folder="build"> * <classes id="build.classes" location="${build.folder}/classes"/> * <reference refid="build.classes"/> * </build> * <compile> * <classpath pathid="compile.classpath"> * <pathelement location="${build.classes}"/> * </classpath> * </compile> * <run-time> * <jars>*.jar</jars> * <classpath pathid="run-time.classpath"> * <path refid="compile.classpath"/> * <pathelement path="${run-time.jars}"/> * </classpath> * </run-time> * </root-tag> * </pre> * * <p>is equivalent to the following entries in a build file:</p> * * <pre> * <property name="build" location="build"/> * <property name="build.classes" location="${build.location}/classes"/> * <property name="build.reference" refid="build.classes"/> * * <property name="run-time.jars" value="*.jar/> * * <classpath id="compile.classpath"> * <pathelement location="${build.classes}"/> * </classpath> * * <classpath id="run-time.classpath"> * <path refid="compile.classpath"/> * <pathelement path="${run-time.jars}"/> * </classpath> * </pre> * * <p> This task <i>requires</i> the following attributes:</p> * * <ul> * <li><b>file</b>: The name of the file to load.</li> * </ul> * * <p>This task supports the following attributes:</p> * * <ul> * <li><b>prefix</b>: Optionally specify a prefix applied to * all properties loaded. Defaults to an empty string.</li> * <li><b>keepRoot</b>: Indicate whether the root xml element * is kept as part of property name. Defaults to true.</li> * <li><b>validate</b>: Indicate whether the xml file is validated. * Defaults to false.</li> * <li><b>collapseAttributes</b>: Indicate whether attributes are * stored in property names with parens or with period * delimiters. Defaults to false, meaning properties * are stored with parens (i.e., foo(attr)).</li> * <li><b>semanticAttributes</b>: Indicate whether attributes * named "location", "value", "refid" and "path" * are interpreted as ant properties. Defaults * to false.</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 semantic attribute ("location" or "value") as * part of the property name. Defaults to false.</li> * </ul> * * @ant.task name="xmlproperty" category="xml" */public class XmlProperty extends org.apache.tools.ant.Task { private Resource src; private String prefix = ""; private boolean keepRoot = true; private boolean validate = false; private boolean collapseAttributes = false; private boolean semanticAttributes = false; private boolean includeSemanticAttribute = false; private File rootDirectory = null; private Hashtable addedAttributes = new Hashtable(); private XMLCatalog xmlCatalog = new XMLCatalog(); private String delimiter = ","; 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 }; private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); /** * Constructor. */ public XmlProperty() { super(); } /** * Initializes the task. */ public void init() { super.init(); xmlCatalog.setProject(getProject()); } /** * @return the xmlCatalog as the entityresolver. */ protected EntityResolver getEntityResolver() { return xmlCatalog; } /** * Run the task. * @throws BuildException The exception raised during task execution. * @todo validate the source file is valid before opening, print a better error message * @todo add a verbose level log message listing the name of the file being loaded */ public void execute() throws BuildException { Resource r = getResource(); if (r == null) { String msg = "XmlProperty task requires a source resource"; throw new BuildException(msg); } try { log("Loading " + src, Project.MSG_VERBOSE); if (r.isExists()) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(validate); factory.setNamespaceAware(false); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(getEntityResolver()); Document document = null; if (src instanceof FileResource) { document = builder.parse(((FileResource) src).getFile()); } else { document = builder.parse(src.getInputStream()); } Element topElement = document.getDocumentElement(); // Keep a hashtable of attributes added by this task. // This task is allow to override its own properties // but not other properties. So we need to keep track // of which properties we've added. addedAttributes = new Hashtable(); if (keepRoot) { 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, null); } } } else { log("Unable to find property resource: " + r, Project.MSG_VERBOSE); } } catch (SAXException sxe) { // Error generated during parsing Exception x = sxe; if (sxe.getException() != null) { x = sxe.getException(); } throw new BuildException("Failed to load " + src, x); } catch (ParserConfigurationException pce) { // Parser with specified options can't be built throw new BuildException(pce); } catch (IOException ioe) { // I/O error throw new BuildException("Failed to load " + src, ioe); } } /** 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); } /** * 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) { // 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()) { 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); 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -