Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/dom/DOMUtil.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/dom/DOMUtil.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/dom/DOMUtil.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/dom/DOMUtil.java Thu Nov 3 05:41:06 2005
@@ -0,0 +1,1140 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.cocoon.xml.dom;
+
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.xml.IncludeXMLConsumer;
+import org.apache.cocoon.xml.XMLUtils;
+
+import org.apache.commons.lang.BooleanUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.excalibur.source.SourceParameters;
+import org.apache.excalibur.xml.sax.SAXParser;
+import org.apache.excalibur.xml.sax.XMLizable;
+import org.apache.excalibur.xml.xpath.NodeListImpl;
+import org.apache.excalibur.xml.xpath.XPathProcessor;
+import org.apache.excalibur.xml.xpath.XPathUtil;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * This class is a utility class for miscellaneous DOM functions, like
+ * getting and setting values of nodes.
+ *
+ * @author Carsten Ziegeler
+ * @version $Id: DOMUtil.java 164876 2005-04-26 20:49:28Z vgritsenko $
+*/
+public final class DOMUtil {
+
+ /**
+ * Get the owner of the DOM document belonging to the node.
+ * This works even if the node is the document itself.
+ *
+ * @param node The node.
+ * @return The corresponding document.
+ */
+ public static Document getOwnerDocument(Node node) {
+ if (node.getNodeType() == Node.DOCUMENT_NODE) {
+ return (Document) node;
+ } else {
+ return node.getOwnerDocument();
+ }
+ }
+
+ /**
+ * Get the value of the node specified by the XPath.
+ * This works similar to xsl:value-of. If the node does not exist null
+ * is returned.
+ *
+ * @param root The node to start the search.
+ * @param path XPath search expression.
+ * @return The value of the node or null
+ */
+ public static String getValueOfNode(XPathProcessor processor, Node root, String path)
+ throws ProcessingException {
+ if (path == null) {
+ throw new ProcessingException("Not a valid XPath: " + path);
+ }
+ if (root != null) {
+ path = StringUtils.strip(path, "/");
+ Node node = XPathUtil.searchSingleNode(processor, root, path);
+ if (node != null) {
+ return getValueOfNode(node);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the value of the node specified by the XPath.
+ * This works similar to xsl:value-of. If the node is not found
+ * the defaultValue
is returned.
+ *
+ * @param root The node to start the search.
+ * @param path XPath search expression.
+ * @param defaultValue The default value if the node does not exist.
+ * @return The value of the node or defaultValue
+ */
+ public static String getValueOfNode(
+ XPathProcessor processor,
+ Node root,
+ String path,
+ String defaultValue)
+ throws ProcessingException {
+ String value = getValueOfNode(processor, root, path);
+ if (value == null)
+ value = defaultValue;
+
+ return value;
+ }
+
+ /**
+ * Get the boolean value of the node specified by the XPath.
+ * This works similar to xsl:value-of. If the node exists and has a value
+ * this value is converted to a boolean, e.g. "true" or "false" as value
+ * will result into the corresponding boolean values.
+ *
+ * @param root The node to start the search.
+ * @param path XPath search expression.
+ * @return The boolean value of the node.
+ * @throws ProcessingException If the node is not found.
+ */
+ public static boolean getValueOfNodeAsBoolean(XPathProcessor processor, Node root, String path)
+ throws ProcessingException {
+ String value = getValueOfNode(processor, root, path);
+ if (value == null) {
+ throw new ProcessingException("No such node: " + path);
+ }
+ return Boolean.valueOf(value).booleanValue();
+ }
+
+ /**
+ * Get the boolean value of the node specified by the XPath.
+ * This works similar to xsl:value-of. If the node exists and has a value
+ * this value is converted to a boolean, e.g. "true" or "false" as value
+ * will result into the corresponding boolean values.
+ * If the node does not exist, the defaultValue
is returned.
+ *
+ * @param root The node to start the search.
+ * @param path XPath search expression.
+ * @param defaultValue Default boolean value.
+ * @return The value of the node or defaultValue
+ */
+ public static boolean getValueOfNodeAsBoolean(XPathProcessor processor,
+ Node root,
+ String path,
+ boolean defaultValue)
+ throws ProcessingException {
+ String value = getValueOfNode(processor, root, path);
+ if (value != null) {
+ return BooleanUtils.toBoolean(value);
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Get the value of the DOM node.
+ * The value of a node is the content of the first text node.
+ * If the node has no text nodes, null
is returned.
+ */
+ public static String getValueOfNode(Node node) {
+ if (node != null) {
+ if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
+ return node.getNodeValue();
+ } else {
+ node.normalize();
+ NodeList childs = node.getChildNodes();
+ int i = 0;
+ int length = childs.getLength();
+ while (i < length) {
+ if (childs.item(i).getNodeType() == Node.TEXT_NODE) {
+ return childs.item(i).getNodeValue().trim();
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the value of the node.
+ * The value of the node is the content of the first text node.
+ * If the node has no text nodes the defaultValue
is
+ * returned.
+ */
+ public static String getValueOfNode(Node node, String defaultValue) {
+ return StringUtils.defaultString(getValueOfNode(node), defaultValue);
+ }
+
+ /**
+ * Set the value of the DOM node.
+ * All current children of the node are removed and a new text node
+ * with the value is appended.
+ */
+ public static void setValueOfNode(Node node, String value) {
+ if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
+ node.setNodeValue(value);
+ } else {
+ while (node.hasChildNodes() == true) {
+ node.removeChild(node.getFirstChild());
+ }
+ node.appendChild(node.getOwnerDocument().createTextNode(value));
+ }
+ }
+
+ /** XML definition for a document */
+ private static final String XML_DEFINITION = "";
+ private static final String XML_ROOT_DEFINITION = XML_DEFINITION + "";
+
+ /**
+ * Get a document fragment from a Reader
.
+ * The reader must provide valid XML, but it is allowed that the XML
+ * has more than one root node. This xml is parsed by the
+ * specified parser instance and a DOM DocumentFragment is created.
+ */
+ public static DocumentFragment getDocumentFragment(SAXParser parser, Reader stream)
+ throws ProcessingException {
+ DocumentFragment frag = null;
+
+ Writer writer;
+ Reader reader;
+ boolean removeRoot = true;
+
+ try {
+ // create a writer,
+ // write the root element, then the input from the
+ // reader
+ writer = new StringWriter();
+
+ writer.write(XML_ROOT_DEFINITION);
+ char[] cbuf = new char[16384];
+ int len;
+ do {
+ len = stream.read(cbuf, 0, 16384);
+ if (len != -1) {
+ writer.write(cbuf, 0, len);
+ }
+ } while (len != -1);
+ writer.write("");
+
+ // now test if xml input start with
+ final Document doc = builder.getDocument();
+ frag = doc.createDocumentFragment();
+ final Node root = doc.getDocumentElement().getFirstChild();
+ root.normalize();
+ if (removeRoot == false) {
+ root.getParentNode().removeChild(root);
+ frag.appendChild(root);
+ } else {
+ Node child;
+ while (root.hasChildNodes() == true) {
+ child = root.getFirstChild();
+ root.removeChild(child);
+ frag.appendChild(child);
+ }
+ }
+ } catch (SAXException sax) {
+ throw new ProcessingException("SAXException: " + sax, sax);
+ } catch (IOException ioe) {
+ throw new ProcessingException("IOException: " + ioe, ioe);
+ }
+ return frag;
+ }
+
+ /**
+ * Create a parameter object from xml.
+ * The xml is flat and consists of elements which all have exactly one text node:
+ * value_one
+ * value_two
+ * A parameter can occur more than once with different values.
+ * If source
is not specified a new paramter object is created
+ * otherwise the parameters are added to source.
+ */
+ public static SourceParameters createParameters(Node fragment, SourceParameters source) {
+ SourceParameters par = (source == null ? new SourceParameters() : source);
+ if (fragment != null) {
+ NodeList childs = fragment.getChildNodes();
+ if (childs != null) {
+ Node current;
+ for (int i = 0; i < childs.getLength(); i++) {
+ current = childs.item(i);
+
+ // only element nodes
+ if (current.getNodeType() == Node.ELEMENT_NODE) {
+ current.normalize();
+ NodeList valueChilds = current.getChildNodes();
+ String key;
+ StringBuffer valueBuffer;
+ String value;
+
+ key = current.getNodeName();
+ valueBuffer = new StringBuffer();
+ for (int m = 0; m < valueChilds.getLength(); m++) {
+ current = valueChilds.item(m); // attention: current is reused here!
+ if (current.getNodeType() == Node.TEXT_NODE) { // only text nodes
+ if (valueBuffer.length() > 0)
+ valueBuffer.append(' ');
+ valueBuffer.append(current.getNodeValue());
+ }
+ }
+ value = valueBuffer.toString().trim();
+ if (key != null && value != null && value.length() > 0) {
+ par.setParameter(key, value);
+ }
+ }
+ }
+ }
+ }
+ return par;
+ }
+
+ /**
+ * Create a string from a DOM document fragment.
+ * Only the top level text nodes are chained together to build the text.
+ */
+ public static String createText(DocumentFragment fragment) {
+ StringBuffer value = new StringBuffer();
+ if (fragment != null) {
+ NodeList childs = fragment.getChildNodes();
+ if (childs != null) {
+ Node current;
+
+ for (int i = 0; i < childs.getLength(); i++) {
+ current = childs.item(i);
+
+ // only text nodes
+ if (current.getNodeType() == Node.TEXT_NODE) {
+ if (value.length() > 0)
+ value.append(' ');
+ value.append(current.getNodeValue());
+ }
+ }
+ }
+ }
+ return value.toString().trim();
+ }
+
+ /**
+ * Compare all attributes of two elements.
+ * This method returns true only if both nodes have the same number of
+ * attributes and the same attributes with equal values.
+ * Namespace definition nodes are ignored
+ */
+ public static boolean compareAttributes(Element first, Element second) {
+ NamedNodeMap attr1 = first.getAttributes();
+ NamedNodeMap attr2 = second.getAttributes();
+ String value;
+
+ if (attr1 == null && attr2 == null)
+ return true;
+ int attr1Len = (attr1 == null ? 0 : attr1.getLength());
+ int attr2Len = (attr2 == null ? 0 : attr2.getLength());
+ if (attr1Len > 0) {
+ int l = attr1.getLength();
+ for (int i = 0; i < l; i++) {
+ if (attr1.item(i).getNodeName().startsWith("xmlns:") == true)
+ attr1Len--;
+ }
+ }
+ if (attr2Len > 0) {
+ int l = attr2.getLength();
+ for (int i = 0; i < l; i++) {
+ if (attr2.item(i).getNodeName().startsWith("xmlns:") == true)
+ attr2Len--;
+ }
+ }
+ if (attr1Len != attr2Len)
+ return false;
+ int i, l;
+ int m, l2;
+ i = 0;
+ l = attr1.getLength();
+ l2 = attr2.getLength();
+ boolean ok = true;
+ // each attribute of first must be in second with the same value
+ while (i < l && ok == true) {
+ value = attr1.item(i).getNodeName();
+ if (value.startsWith("xmlns:") == false) {
+ ok = false;
+ m = 0;
+ while (m < l2 && ok == false) {
+ if (attr2.item(m).getNodeName().equals(value) == true) {
+ // same name, same value?
+ ok = attr1.item(i).getNodeValue().equals(attr2.item(m).getNodeValue());
+ }
+ m++;
+ }
+ }
+
+ i++;
+ }
+ return ok;
+ }
+
+ /**
+ * Implementation for String
:
+ * outputs characters representing the value.
+ *
+ * @param parent The node getting the value
+ * @param text the value
+ */
+ public static void valueOf(Node parent, String text) throws ProcessingException {
+ if (text != null) {
+ parent.appendChild(parent.getOwnerDocument().createTextNode(text));
+ }
+ }
+
+ /**
+ * Implementation for XMLizable
:
+ * outputs the value by calling v.toSax(contentHandler)
.
+ *
+ * @param parent The node getting the value
+ * @param v the XML fragment
+ */
+ public static void valueOf(Node parent, XMLizable v) throws ProcessingException {
+ if (v != null) {
+ DOMBuilder builder = new DOMBuilder(parent);
+ try {
+ v.toSAX(builder);
+ } catch (SAXException e) {
+ throw new ProcessingException(e);
+ }
+ }
+ }
+
+ /**
+ * Implementation for org.w3c.dom.Node
:
+ * converts the Node to a SAX event stream.
+ *
+ * @param parent The node getting the value
+ * @param v the value
+ */
+ public static void valueOf(Node parent, Node v) throws ProcessingException {
+ if (v != null) {
+ parent.appendChild(parent.getOwnerDocument().importNode(v, true));
+ }
+ }
+
+ /**
+ * Implementation for java.util.Collection
:
+ * outputs the value by calling {@link #valueOf(Node, Object)} on each element of the
+ * collection.
+ *
+ * @param parent The node getting the value
+ * @param v the XML fragment
+ */
+ public static void valueOf(Node parent, Collection v) throws ProcessingException {
+ if (v != null) {
+ Iterator iterator = v.iterator();
+ while (iterator.hasNext()) {
+ valueOf(parent, iterator.next());
+ }
+ }
+ }
+
+ /**
+ * Implementation for java.util.Map
:
+ * For each entry an element is created with the childs key and value
+ * Outputs the value and the key by calling {@link #valueOf(Node, Object)}
+ * on each value and key of the Map.
+ *
+ * @param parent The node getting the value
+ * @param v the Map
+ */
+ public static void valueOf(Node parent, Map v) throws ProcessingException {
+ if (v != null) {
+ Iterator iterator = v.keySet().iterator();
+ Node mapNode = parent.getOwnerDocument().createElementNS(null, "java.util.map");
+ parent.appendChild(mapNode);
+ for (Iterator iter = v.entrySet().iterator(); iter.hasNext();) {
+ final Map.Entry me = (Map.Entry) iter.next();
+
+ Node entryNode = mapNode.getOwnerDocument().createElementNS(null, "entry");
+ mapNode.appendChild(entryNode);
+
+ Node keyNode = entryNode.getOwnerDocument().createElementNS(null, "key");
+ entryNode.appendChild(keyNode);
+ valueOf(keyNode, me.getKey());
+
+ Node valueNode = entryNode.getOwnerDocument().createElementNS(null, "value");
+ entryNode.appendChild(valueNode);
+ valueOf(valueNode, me.getValue());
+ }
+ }
+ }
+
+ /**
+ * Implementation for Object
depending on its class :
+ *
+ * - if it's an array, call {@link #valueOf(Node, Object)} on all its elements,
+ * - if it's class has a specific {@link #valueOf(Node, Object)} implementation, use it,
+ * - else, output it's string representation.
+ *
+ *
+ * @param parent The node getting the value
+ * @param v the value
+ */
+ public static void valueOf(Node parent, Object v) throws ProcessingException {
+ if (v == null) {
+ return;
+ }
+
+ // Array: recurse over each element
+ if (v.getClass().isArray()) {
+ Object[] elements = (Object[]) v;
+
+ for (int i = 0; i < elements.length; i++) {
+ valueOf(parent, elements[i]);
+ }
+ return;
+ }
+
+ // Check handled object types in case they were not typed in the XSP
+
+ // XMLizable
+ if (v instanceof XMLizable) {
+ valueOf(parent, (XMLizable) v);
+ return;
+ }
+
+ // Node
+ if (v instanceof Node) {
+ valueOf(parent, (Node) v);
+ return;
+ }
+
+ // Collection
+ if (v instanceof Collection) {
+ valueOf(parent, (Collection) v);
+ return;
+ }
+
+ // Map
+ if (v instanceof Map) {
+ valueOf(parent, (Map) v);
+ return;
+ }
+
+ // Give up: hope it's a string or has a meaningful string representation
+ valueOf(parent, String.valueOf(v));
+ }
+
+ /**
+ * Use an XPath string to select a single node. XPath namespace
+ * prefixes are resolved from the context node, which may not
+ * be what you want (see the next method).
+ *
+ * @param contextNode The node to start searching from.
+ * @param str A valid XPath string.
+ * @param processor The XPath processor to use
+ * @return The first node found that matches the XPath, or null.
+ *
+ * @throws TransformerException
+ */
+ public static Node getSingleNode(Node contextNode, String str,
+ XPathProcessor processor)
+ throws TransformerException {
+ String[] pathComponents = buildPathArray(str);
+ if (pathComponents == null) {
+ return processor.selectSingleNode(contextNode, str);
+ } else {
+ return getFirstNodeFromPath(contextNode, pathComponents, false);
+ }
+ }
+
+ /**
+ * Return the Node
from the DOM Node rootNode
+ * using the XPath expression path
.
+ * If the node does not exist, it is created and then returned.
+ * This is a very simple method for creating new nodes. If the
+ * XPath contains selectors ([,,,]) or "*" it is of course not
+ * possible to create the new node. So if you use such XPaths
+ * the node must exist beforehand.
+ * An simple exception is if the expression contains attribute
+ * test to values (e.g. [@id = 'du' and @number = 'you'],
+ * the attributes with the given values are added. The attributes
+ * must be separated with 'and'.
+ * Another problem are namespaces: XPath requires sometimes selectors for
+ * namespaces, e.g. : /*[namespace-uri()="uri" and local-name()="name"]
+ * Creating such a node with a namespace is not possible right now as we use
+ * a very simple XPath parser which is not able to parse all kinds of selectors
+ * correctly.
+ *
+ * @param rootNode The node to start the search.
+ * @param path XPath expression for searching the node.
+ * @param processor The XPath processor to use
+ * @return The node specified by the path.
+ * @throws ProcessingException If no path is specified or the XPath engine fails.
+ */
+ public static Node selectSingleNode(Node rootNode, String path, XPathProcessor processor)
+ throws ProcessingException {
+ // Now we have to parse the string
+ // First test: path? rootNode?
+ if (path == null) {
+ throw new ProcessingException("XPath is required.");
+ }
+ if (rootNode == null)
+ return rootNode;
+
+ if (path.length() == 0 || path.equals("/") == true)
+ return rootNode;
+
+ // now the first "quick" test is if the node exists using the
+ // full XPathAPI
+ try {
+ Node testNode = getSingleNode(rootNode, path, processor);
+ if (testNode != null)
+ return testNode;
+ } catch (javax.xml.transform.TransformerException local) {
+ throw new ProcessingException(
+ "Transforming exception during selectSingleNode with path: '"
+ + path
+ + "'. Exception: "
+ + local,
+ local);
+ }
+
+ // remove leading "/" oon both ends
+ path = StringUtils.strip(path, "/");
+
+ // now step through the nodes!
+ Node parent = rootNode;
+ int pos;
+ int posSelector;
+ do {
+ pos = path.indexOf("/"); // get next separator
+ posSelector = path.indexOf("[");
+ if (posSelector != -1 && posSelector < pos) {
+ posSelector = path.indexOf("]");
+ pos = path.indexOf("/", posSelector);
+ }
+
+ String nodeName;
+ boolean isAttribute = false;
+ if (pos != -1) { // found separator
+ nodeName = path.substring(0, pos); // string until "/"
+ path = path.substring(pos + 1); // rest of string after "/"
+ } else {
+ nodeName = path;
+ }
+
+ // test for attribute spec
+ if (nodeName.startsWith("@") == true) {
+ isAttribute = true;
+ }
+
+ Node singleNode;
+ try {
+ singleNode = getSingleNode(parent, nodeName, processor);
+ } catch (javax.xml.transform.TransformerException localException) {
+ throw new ProcessingException(
+ "XPathUtil.selectSingleNode: " + localException.getMessage(),
+ localException);
+ }
+
+ // create node if necessary
+ if (singleNode == null) {
+ Node newNode;
+ // delete XPath selectors
+ int posSelect = nodeName.indexOf("[");
+ String XPathExp = null;
+ if (posSelect != -1) {
+ XPathExp = nodeName.substring(posSelect + 1, nodeName.length() - 1);
+ nodeName = nodeName.substring(0, posSelect);
+ }
+ if (isAttribute == true) {
+ try {
+ newNode =
+ getOwnerDocument(rootNode).createAttributeNS(
+ null,
+ nodeName.substring(1));
+ ((Element) parent).setAttributeNodeNS((org.w3c.dom.Attr) newNode);
+ parent = newNode;
+ } catch (DOMException local) {
+ throw new ProcessingException(
+ "Unable to create new DOM node: '" + nodeName + "'.",
+ local);
+ }
+ } else {
+ try {
+ newNode = getOwnerDocument(rootNode).createElementNS(null, nodeName);
+ } catch (DOMException local) {
+ throw new ProcessingException(
+ "Unable to create new DOM node: '" + nodeName + "'.",
+ local);
+ }
+ if (XPathExp != null) {
+ java.util.List attrValuePairs = new java.util.ArrayList(4);
+ boolean noError = true;
+
+ String attr;
+ String value;
+ // scan for attributes
+ java.util.StringTokenizer tokenizer =
+ new java.util.StringTokenizer(XPathExp, "= ");
+ while (tokenizer.hasMoreTokens() == true) {
+ attr = tokenizer.nextToken();
+ if (attr.startsWith("@") == true) {
+ if (tokenizer.hasMoreTokens() == true) {
+ value = tokenizer.nextToken();
+ if (value.startsWith("'") && value.endsWith("'"))
+ value = value.substring(1, value.length() - 1);
+ if (value.startsWith("\"") && value.endsWith("\""))
+ value = value.substring(1, value.length() - 1);
+ attrValuePairs.add(attr.substring(1));
+ attrValuePairs.add(value);
+ } else {
+ noError = false;
+ }
+ } else if (attr.trim().equals("and") == false) {
+ noError = false;
+ }
+ }
+ if (noError == true) {
+ for (int l = 0; l < attrValuePairs.size(); l = l + 2) {
+ ((Element) newNode).setAttributeNS(
+ null,
+ (String) attrValuePairs.get(l),
+ (String) attrValuePairs.get(l + 1));
+ }
+ }
+ }
+ parent.appendChild(newNode);
+ parent = newNode;
+ }
+ } else {
+ parent = singleNode;
+ }
+ }
+ while (pos != -1);
+ return parent;
+ }
+
+ /**
+ * Get the value of the node specified by the XPath.
+ * This works similar to xsl:value-of. If the node does not exist null
+ * is returned.
+ *
+ * @param root The node to start the search.
+ * @param path XPath search expression.
+ * @param processor The XPath processor to use
+ * @return The value of the node or null
+ */
+ public static String getValueOf(Node root, String path,
+ XPathProcessor processor) throws ProcessingException {
+ if (path == null) {
+ throw new ProcessingException("Not a valid XPath: " + path);
+ }
+ if (root == null)
+ return null;
+ path = StringUtils.strip(path, "/");
+
+ try {
+ Node node = getSingleNode(root, path, processor);
+ if (node != null) {
+ return getValueOfNode(node);
+ }
+ } catch (javax.xml.transform.TransformerException localException) {
+ throw new ProcessingException(
+ "XPathUtil.selectSingleNode: " + localException.getMessage(),
+ localException);
+ }
+ return null;
+ }
+
+ /**
+ * Get the value of the node specified by the XPath.
+ * This works similar to xsl:value-of. If the node is not found
+ * the defaultValue
is returned.
+ *
+ * @param root The node to start the search.
+ * @param path XPath search expression.
+ * @param defaultValue The default value if the node does not exist.
+ * @param processor The XPath Processor
+ * @return The value of the node or defaultValue
+ */
+ public static String getValueOf(Node root, String path, String defaultValue,
+ XPathProcessor processor)
+ throws ProcessingException {
+ String value = getValueOf(root, path, processor);
+ if (value == null) {
+ value = defaultValue;
+ }
+ return value;
+ }
+
+ /**
+ * Get the boolean value of the node specified by the XPath.
+ * This works similar to xsl:value-of. If the node exists and has a value
+ * this value is converted to a boolean, e.g. "true" or "false" as value
+ * will result into the corresponding boolean values.
+ *
+ * @param root The node to start the search.
+ * @param path XPath search expression.
+ * @param processor The XPath Processor
+ * @return The boolean value of the node.
+ * @throws ProcessingException If the node is not found.
+ */
+ public static boolean getValueAsBooleanOf(Node root, String path,
+ XPathProcessor processor)
+ throws ProcessingException {
+ String value = getValueOf(root, path, processor);
+ if (value == null) {
+ throw new ProcessingException("No such node: " + path);
+ }
+ return Boolean.valueOf(value).booleanValue();
+ }
+
+ /**
+ * Get the boolean value of the node specified by the XPath.
+ * This works similar to xsl:value-of. If the node exists and has a value
+ * this value is converted to a boolean, e.g. "true" or "false" as value
+ * will result into the corresponding boolean values.
+ * If the node does not exist, the defaultValue
is returned.
+ *
+ * @param root The node to start the search.
+ * @param path XPath search expression.
+ * @param defaultValue Default boolean value.
+ * @param processor The XPath Processor
+ * @return The value of the node or defaultValue
+ */
+ public static boolean getValueAsBooleanOf(Node root, String path, boolean defaultValue,
+ XPathProcessor processor)
+ throws ProcessingException {
+ String value = getValueOf(root, path, processor);
+ if (value != null) {
+ return Boolean.valueOf(value).booleanValue();
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Create a new empty DOM document.
+ */
+ public static Document createDocument() throws ProcessingException {
+ try {
+ DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
+ documentFactory.setNamespaceAware(true);
+ documentFactory.setValidating(false);
+ DocumentBuilder docBuilder = documentFactory.newDocumentBuilder();
+ return docBuilder.newDocument();
+ } catch (ParserConfigurationException pce) {
+ throw new ProcessingException("Creating document failed.", pce);
+ }
+ }
+
+ /**
+ * Use an XPath string to select a nodelist.
+ * XPath namespace prefixes are resolved from the contextNode.
+ *
+ * @param contextNode The node to start searching from.
+ * @param str A valid XPath string.
+ * @param processor The XPath Processor
+ * @return A NodeIterator, should never be null.
+ *
+ * @throws TransformerException
+ */
+ public static NodeList selectNodeList(Node contextNode, String str, XPathProcessor processor)
+ throws TransformerException {
+ String[] pathComponents = buildPathArray(str);
+ if (pathComponents != null) {
+ return getNodeListFromPath(contextNode, pathComponents);
+ }
+ return processor.selectNodeList(contextNode, str);
+ }
+
+ /**
+ * Build the input for the get...FromPath methods. If the XPath
+ * expression cannot be handled by the methods, null
+ * is returned.
+ */
+ public static String[] buildPathArray(String xpath) {
+ String[] result = null;
+ if (xpath != null && xpath.charAt(0) != '/') {
+ // test
+ int components = 1;
+ int i, l;
+ l = xpath.length();
+ boolean found = false;
+ i = 0;
+ while (i < l && found == false) {
+ switch (xpath.charAt(i)) {
+ case '[' :
+ found = true;
+ break;
+ case '(' :
+ found = true;
+ break;
+ case '*' :
+ found = true;
+ break;
+ case '@' :
+ found = true;
+ break;
+ case ':' :
+ found = true;
+ break;
+ case '/' :
+ components++;
+ default :
+ i++;
+ }
+ }
+ if (found == false) {
+ result = new String[components];
+ if (components == 1) {
+ result[components - 1] = xpath;
+ } else {
+ i = 0;
+ int start = 0;
+ components = 0;
+ while (i < l) {
+ if (xpath.charAt(i) == '/') {
+ result[components] = xpath.substring(start, i);
+ start = i + 1;
+ components++;
+ }
+ i++;
+ }
+ result[components] = xpath.substring(start);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Use a path to select the first occurence of a node. The namespace
+ * of a node is ignored!
+ * @param contextNode The node starting the search.
+ * @param path The path to search the node. The
+ * contextNode is searched for a child named path[0],
+ * this node is searched for a child named path[1]...
+ * @param create If a child with the corresponding name is not found
+ * and create is set, this node will be created.
+ */
+ public static Node getFirstNodeFromPath(
+ Node contextNode,
+ final String[] path,
+ final boolean create) {
+ if (contextNode == null || path == null || path.length == 0)
+ return contextNode;
+ // first test if the node exists
+ Node item = getFirstNodeFromPath(contextNode, path, 0);
+ if (item == null && create == true) {
+ int i = 0;
+ NodeList childs;
+ boolean found;
+ int m, l;
+ while (contextNode != null && i < path.length) {
+ childs = contextNode.getChildNodes();
+ found = false;
+ if (childs != null) {
+ m = 0;
+ l = childs.getLength();
+ while (found == false && m < l) {
+ item = childs.item(m);
+ if (item.getNodeType() == Node.ELEMENT_NODE
+ && item.getLocalName().equals(path[i]) == true) {
+ found = true;
+ contextNode = item;
+ }
+ m++;
+ }
+ }
+ if (found == false) {
+ Element e = contextNode.getOwnerDocument().createElementNS(null, path[i]);
+ contextNode.appendChild(e);
+ contextNode = e;
+ }
+ i++;
+ }
+ item = contextNode;
+ }
+ return item;
+ }
+
+ /**
+ * Private helper method for getFirstNodeFromPath()
+ */
+ private static Node getFirstNodeFromPath(
+ final Node contextNode,
+ final String[] path,
+ final int startIndex) {
+ int i = 0;
+ NodeList childs;
+ boolean found;
+ int l;
+ Node item = null;
+
+ childs = contextNode.getChildNodes();
+ found = false;
+ if (childs != null) {
+ i = 0;
+ l = childs.getLength();
+ while (found == false && i < l) {
+ item = childs.item(i);
+ if (item.getNodeType() == Node.ELEMENT_NODE
+ && path[startIndex].equals(
+ item.getLocalName() != null ? item.getLocalName() : item.getNodeName())
+ == true) {
+ if (startIndex == path.length - 1) {
+ found = true;
+ } else {
+ item = getFirstNodeFromPath(item, path, startIndex + 1);
+ if (item != null)
+ found = true;
+ }
+ }
+ if (found == false) {
+ i++;
+ }
+ }
+ if (found == false) {
+ item = null;
+ }
+ }
+ return item;
+ }
+
+ /**
+ * Use a path to select all occurences of a node. The namespace
+ * of a node is ignored!
+ * @param contextNode The node starting the search.
+ * @param path The path to search the node. The
+ * contextNode is searched for a child named path[0],
+ * this node is searched for a child named path[1]...
+ */
+ public static NodeList getNodeListFromPath(Node contextNode, String[] path) {
+ if (contextNode == null)
+ return new NodeListImpl();
+ if (path == null || path.length == 0) {
+ return new NodeListImpl(new Node[] { contextNode });
+ }
+ NodeListImpl result = new NodeListImpl();
+ try {
+ getNodesFromPath(result, contextNode, path, 0);
+ } catch (NullPointerException npe) {
+ // this NPE is thrown because the parser is not configured
+ // to use DOM Level 2
+ throw new NullPointerException(
+ "XMLUtil.getNodeListFromPath() did catch a NullPointerException."
+ + "This might be due to a missconfigured XML parser which does not use DOM Level 2."
+ + "Make sure that you use the XML parser shipped with Cocoon.");
+ }
+ return result;
+ }
+
+ /**
+ * Helper method for getNodeListFromPath()
+ */
+ private static void getNodesFromPath(
+ final NodeListImpl result,
+ final Node contextNode,
+ final String[] path,
+ final int startIndex) {
+ final NodeList childs = contextNode.getChildNodes();
+ int m, l;
+ Node item;
+ if (startIndex == (path.length - 1)) {
+ if (childs != null) {
+ m = 0;
+ l = childs.getLength();
+ while (m < l) {
+ item = childs.item(m);
+ if (item.getNodeType() == Node.ELEMENT_NODE) {
+ // Work around: org.apache.xerces.dom.ElementImpl doesn't handle getLocalName() correct
+ if (path[startIndex]
+ .equals(
+ item.getLocalName() != null
+ ? item.getLocalName()
+ : item.getNodeName())
+ == true) {
+ result.addNode(item);
+ }
+ }
+ m++;
+ }
+ }
+ } else {
+ if (childs != null) {
+ m = 0;
+ l = childs.getLength();
+ while (m < l) {
+ item = childs.item(m);
+ if (item.getNodeType() == Node.ELEMENT_NODE) {
+ // Work around: org.apache.xerces.dom.ElementImpl doesn't handle getLocalName() correct
+ if (path[startIndex]
+ .equals(
+ item.getLocalName() != null
+ ? item.getLocalName()
+ : item.getNodeName())
+ == true) {
+ getNodesFromPath(result, item, path, startIndex + 1);
+ }
+ }
+ m++;
+ }
+ }
+ }
+ }
+
+}
Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/dom/DOMUtil.java
------------------------------------------------------------------------------
svn:eol-style = native