0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.cocoon.xml.dom;
0018:
0019: import org.apache.cocoon.ProcessingException;
0020: import org.apache.cocoon.xml.IncludeXMLConsumer;
0021: import org.apache.cocoon.xml.XMLUtils;
0022:
0023: import org.apache.commons.lang.BooleanUtils;
0024: import org.apache.commons.lang.StringUtils;
0025: import org.apache.excalibur.source.SourceParameters;
0026: import org.apache.excalibur.xml.sax.SAXParser;
0027: import org.apache.excalibur.xml.sax.XMLizable;
0028: import org.apache.excalibur.xml.xpath.NodeListImpl;
0029: import org.apache.excalibur.xml.xpath.XPathProcessor;
0030: import org.apache.excalibur.xml.xpath.XPathUtil;
0031: import org.apache.xpath.XPathAPI;
0032: import org.w3c.dom.DOMException;
0033: import org.w3c.dom.Document;
0034: import org.w3c.dom.DocumentFragment;
0035: import org.w3c.dom.Element;
0036: import org.w3c.dom.NamedNodeMap;
0037: import org.w3c.dom.Node;
0038: import org.w3c.dom.NodeList;
0039: import org.xml.sax.InputSource;
0040: import org.xml.sax.SAXException;
0041:
0042: import javax.xml.parsers.DocumentBuilder;
0043: import javax.xml.parsers.DocumentBuilderFactory;
0044: import javax.xml.parsers.ParserConfigurationException;
0045: import javax.xml.transform.OutputKeys;
0046: import javax.xml.transform.TransformerException;
0047: import java.io.IOException;
0048: import java.io.Reader;
0049: import java.io.StringReader;
0050: import java.io.StringWriter;
0051: import java.io.Writer;
0052: import java.util.Collection;
0053: import java.util.Iterator;
0054: import java.util.Map;
0055: import java.util.Properties;
0056: import java.util.StringTokenizer;
0057:
0058: /**
0059: * This class is a utility class for miscellaneous DOM functions, like getting
0060: * and setting values of nodes.
0061: *
0062: * @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
0063: * @version $Id: DOMUtil.java 433543 2006-08-22 06:22:54Z crossley $
0064: */
0065: public final class DOMUtil {
0066:
0067: private static final String XPATH_IS_REQUIRED = "XPath is required.";
0068:
0069: /**
0070: * Get the owner of the DOM document belonging to the node. This works even
0071: * if the node is the document itself.
0072: *
0073: * @param node
0074: * The node.
0075: * @return The corresponding document.
0076: */
0077: public static Document getOwnerDocument(Node node) {
0078: if (node.getNodeType() == Node.DOCUMENT_NODE) {
0079: return (Document) node;
0080: } else {
0081: return node.getOwnerDocument();
0082: }
0083: }
0084:
0085: /**
0086: * Get the value of the node specified by the XPath. This works similar to
0087: * <xsl:value-of>. If the node does not exist <CODE>null</CODE> is
0088: * returned.
0089: *
0090: * @param root
0091: * The node to start the search.
0092: * @param path
0093: * XPath search expression.
0094: * @return The value of the node or <CODE>null</CODE>
0095: */
0096: public static String getValueOfNode(XPathProcessor processor,
0097: Node root, String path) throws ProcessingException {
0098: if (path == null) {
0099: throw new ProcessingException(XPATH_IS_REQUIRED);
0100: }
0101: if (root != null) {
0102: path = StringUtils.strip(path, "/");
0103: Node node = XPathUtil.searchSingleNode(processor, root,
0104: path);
0105: if (node != null) {
0106: return getValueOfNode(node);
0107: }
0108: }
0109: return null;
0110: }
0111:
0112: /**
0113: * Get the value of the node specified by the XPath. This works similar to
0114: * <xsl:value-of>. If the node is not found the <CODE>defaultValue</CODE>
0115: * is returned.
0116: *
0117: * @param root
0118: * The node to start the search.
0119: * @param path
0120: * XPath search expression.
0121: * @param defaultValue
0122: * The default value if the node does not exist.
0123: * @return The value of the node or <CODE>defaultValue</CODE>
0124: */
0125: public static String getValueOfNode(XPathProcessor processor,
0126: Node root, String path, String defaultValue)
0127: throws ProcessingException {
0128: String value = getValueOfNode(processor, root, path);
0129: if (value == null)
0130: value = defaultValue;
0131:
0132: return value;
0133: }
0134:
0135: /**
0136: * Get the boolean value of the node specified by the XPath. This works
0137: * similar to <xsl:value-of>. If the node exists and has a value this
0138: * value is converted to a boolean, e.g. "true" or "false" as value will
0139: * result into the corresponding boolean values.
0140: *
0141: * @param root
0142: * The node to start the search.
0143: * @param path
0144: * XPath search expression.
0145: * @return The boolean value of the node.
0146: * @throws ProcessingException
0147: * If the node is not found.
0148: */
0149: public static boolean getValueOfNodeAsBoolean(
0150: XPathProcessor processor, Node root, String path)
0151: throws ProcessingException {
0152: String value = getValueOfNode(processor, root, path);
0153: if (value == null) {
0154: throw new ProcessingException("No such node: " + path);
0155: }
0156: return Boolean.valueOf(value).booleanValue();
0157: }
0158:
0159: /**
0160: * Get the boolean value of the node specified by the XPath. This works
0161: * similar to <xsl:value-of>. If the node exists and has a value this
0162: * value is converted to a boolean, e.g. "true" or "false" as value will
0163: * result into the corresponding boolean values. If the node does not exist,
0164: * the <CODE>defaultValue</CODE> is returned.
0165: *
0166: * @param root
0167: * The node to start the search.
0168: * @param path
0169: * XPath search expression.
0170: * @param defaultValue
0171: * Default boolean value.
0172: * @return The value of the node or <CODE>defaultValue</CODE>
0173: */
0174: public static boolean getValueOfNodeAsBoolean(
0175: XPathProcessor processor, Node root, String path,
0176: boolean defaultValue) throws ProcessingException {
0177: String value = getValueOfNode(processor, root, path);
0178: if (value != null) {
0179: return BooleanUtils.toBoolean(value);
0180: }
0181: return defaultValue;
0182: }
0183:
0184: /**
0185: * Get the value of the DOM node. The value of a node is the content of the
0186: * first text node. If the node has no text nodes, <code>null</code> is
0187: * returned.
0188: */
0189: public static String getValueOfNode(Node node) {
0190: if (node != null) {
0191: if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
0192: return node.getNodeValue();
0193: } else {
0194: node.normalize();
0195: NodeList childs = node.getChildNodes();
0196: int i = 0;
0197: int length = childs.getLength();
0198: while (i < length) {
0199: if (childs.item(i).getNodeType() == Node.TEXT_NODE) {
0200: return childs.item(i).getNodeValue().trim();
0201: } else {
0202: i++;
0203: }
0204: }
0205: }
0206: }
0207: return null;
0208: }
0209:
0210: /**
0211: * Get the value of the node. The value of the node is the content of the
0212: * first text node. If the node has no text nodes the <CODE>defaultValue</CODE>
0213: * is returned.
0214: */
0215: public static String getValueOfNode(Node node, String defaultValue) {
0216: return StringUtils.defaultString(getValueOfNode(node),
0217: defaultValue);
0218: }
0219:
0220: /**
0221: * Set the value of the DOM node. All current children of the node are
0222: * removed and a new text node with the value is appended.
0223: */
0224: public static void setValueOfNode(Node node, String value) {
0225: if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
0226: node.setNodeValue(value);
0227: } else {
0228: while (node.hasChildNodes()) {
0229: node.removeChild(node.getFirstChild());
0230: }
0231: node.appendChild(node.getOwnerDocument().createTextNode(
0232: value));
0233: }
0234: }
0235:
0236: /** XML definition for a document */
0237: private static final String XML_DEFINITION = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
0238:
0239: private static final String XML_ROOT_DEFINITION = XML_DEFINITION
0240: + "<root>";
0241:
0242: /**
0243: * Get a document fragment from a <code>Reader</code>. The reader must
0244: * provide valid XML, but it is allowed that the XML has more than one root
0245: * node. This xml is parsed by the specified parser instance and a DOM
0246: * DocumentFragment is created.
0247: */
0248: public static DocumentFragment getDocumentFragment(
0249: SAXParser parser, Reader stream) throws ProcessingException {
0250: DocumentFragment frag = null;
0251:
0252: Writer writer;
0253: Reader reader;
0254: boolean removeRoot = true;
0255:
0256: try {
0257: // create a writer,
0258: // write the root element, then the input from the
0259: // reader
0260: writer = new StringWriter();
0261:
0262: writer.write(XML_ROOT_DEFINITION);
0263: char[] cbuf = new char[16384];
0264: int len;
0265: do {
0266: len = stream.read(cbuf, 0, 16384);
0267: if (len != -1) {
0268: writer.write(cbuf, 0, len);
0269: }
0270: } while (len != -1);
0271: writer.write("</root>");
0272:
0273: // now test if xml input start with <?xml
0274: String xml = writer.toString();
0275: String searchString = XML_ROOT_DEFINITION + "<?xml ";
0276: if (xml.startsWith(searchString)) {
0277: // now remove the surrounding root element
0278: xml = xml.substring(XML_ROOT_DEFINITION.length(), xml
0279: .length() - 7);
0280: removeRoot = false;
0281: }
0282:
0283: reader = new StringReader(xml);
0284:
0285: InputSource input = new InputSource(reader);
0286:
0287: DOMBuilder builder = new DOMBuilder();
0288: builder.startDocument();
0289: builder.startElement("", "root", "root",
0290: XMLUtils.EMPTY_ATTRIBUTES);
0291:
0292: IncludeXMLConsumer filter = new IncludeXMLConsumer(builder,
0293: builder);
0294: parser.parse(input, filter);
0295:
0296: builder.endElement("", "root", "root");
0297: builder.endDocument();
0298:
0299: // Create Document Fragment, remove <root>
0300: final Document doc = builder.getDocument();
0301: frag = doc.createDocumentFragment();
0302: final Node root = doc.getDocumentElement().getFirstChild();
0303: root.normalize();
0304: if (removeRoot == false) {
0305: root.getParentNode().removeChild(root);
0306: frag.appendChild(root);
0307: } else {
0308: Node child;
0309: while (root.hasChildNodes()) {
0310: child = root.getFirstChild();
0311: root.removeChild(child);
0312: frag.appendChild(child);
0313: }
0314: }
0315: } catch (SAXException sax) {
0316: throw new ProcessingException("SAXException: " + sax, sax);
0317: } catch (IOException ioe) {
0318: throw new ProcessingException("IOException: " + ioe, ioe);
0319: }
0320: return frag;
0321: }
0322:
0323: /**
0324: * Create a parameter object from xml. The xml is flat and consists of
0325: * elements which all have exactly one text node: <parone>value_one<parone>
0326: * <partwo>value_two<partwo> A parameter can occur more than once with
0327: * different values. If <CODE>source</CODE> is not specified a new
0328: * parameter object is created otherwise the parameters are added to source.
0329: */
0330: public static SourceParameters createParameters(Node fragment,
0331: SourceParameters source) {
0332: SourceParameters par = (source == null ? new SourceParameters()
0333: : source);
0334: if (fragment != null) {
0335: NodeList childs = fragment.getChildNodes();
0336: if (childs != null) {
0337: Node current;
0338: for (int i = 0; i < childs.getLength(); i++) {
0339: current = childs.item(i);
0340:
0341: // only element nodes
0342: if (current.getNodeType() == Node.ELEMENT_NODE) {
0343: current.normalize();
0344: NodeList valueChilds = current.getChildNodes();
0345: String key;
0346: StringBuffer valueBuffer;
0347: String value;
0348:
0349: key = current.getNodeName();
0350: valueBuffer = new StringBuffer();
0351: for (int m = 0; m < valueChilds.getLength(); m++) {
0352: current = valueChilds.item(m); // attention: current is reused here!
0353: if (current.getNodeType() == Node.TEXT_NODE) { // only text nodes
0354: if (valueBuffer.length() > 0)
0355: valueBuffer.append(' ');
0356: valueBuffer.append(current
0357: .getNodeValue());
0358: }
0359: }
0360: value = valueBuffer.toString().trim();
0361: if (key != null && value.length() > 0) {
0362: par.setParameter(key, value);
0363: }
0364: }
0365: }
0366: }
0367: }
0368: return par;
0369: }
0370:
0371: /**
0372: * Create a string from a DOM document fragment. Only the top level text
0373: * nodes are chained together to build the text.
0374: */
0375: public static String createText(DocumentFragment fragment) {
0376: StringBuffer value = new StringBuffer();
0377: if (fragment != null) {
0378: NodeList childs = fragment.getChildNodes();
0379: if (childs != null) {
0380: Node current;
0381:
0382: for (int i = 0; i < childs.getLength(); i++) {
0383: current = childs.item(i);
0384:
0385: // only text nodes
0386: if (current.getNodeType() == Node.TEXT_NODE) {
0387: if (value.length() > 0)
0388: value.append(' ');
0389: value.append(current.getNodeValue());
0390: }
0391: }
0392: }
0393: }
0394: return value.toString().trim();
0395: }
0396:
0397: /**
0398: * Compare all attributes of two elements. This method returns true only if
0399: * both nodes have the same number of attributes and the same attributes
0400: * with equal values. Namespace definition nodes are ignored
0401: */
0402: public static boolean compareAttributes(Element first,
0403: Element second) {
0404: NamedNodeMap attr1 = first.getAttributes();
0405: NamedNodeMap attr2 = second.getAttributes();
0406: String value;
0407:
0408: if (attr1 == null && attr2 == null)
0409: return true;
0410: int attr1Len = (attr1 == null ? 0 : attr1.getLength());
0411: int attr2Len = (attr2 == null ? 0 : attr2.getLength());
0412: if (attr1Len > 0) {
0413: int l = attr1.getLength();
0414: for (int i = 0; i < l; i++) {
0415: if (attr1.item(i).getNodeName().startsWith("xmlns:"))
0416: attr1Len--;
0417: }
0418: }
0419: if (attr2Len > 0) {
0420: int l = attr2.getLength();
0421: for (int i = 0; i < l; i++) {
0422: if (attr2.item(i).getNodeName().startsWith("xmlns:"))
0423: attr2Len--;
0424: }
0425: }
0426: if (attr1Len != attr2Len)
0427: return false;
0428: int i, l;
0429: int m, l2;
0430: i = 0;
0431: l = attr1.getLength();
0432: l2 = attr2.getLength();
0433: boolean ok = true;
0434: // each attribute of first must be in second with the same value
0435: while (i < l && ok) {
0436: value = attr1.item(i).getNodeName();
0437: if (value.startsWith("xmlns:") == false) {
0438: ok = false;
0439: m = 0;
0440: while (m < l2 && ok == false) {
0441: if (attr2.item(m).getNodeName().equals(value)) {
0442: // same name, same value?
0443: ok = attr1.item(i).getNodeValue().equals(
0444: attr2.item(m).getNodeValue());
0445: }
0446: m++;
0447: }
0448: }
0449:
0450: i++;
0451: }
0452: return ok;
0453: }
0454:
0455: /**
0456: * Implementation for <code>String</code> : outputs characters
0457: * representing the value.
0458: *
0459: * @param parent
0460: * The node getting the value
0461: * @param text
0462: * the value
0463: */
0464: public static void valueOf(Node parent, String text)
0465: throws ProcessingException {
0466: if (text != null) {
0467: parent.appendChild(parent.getOwnerDocument()
0468: .createTextNode(text));
0469: }
0470: }
0471:
0472: /**
0473: * Implementation for <code>XMLizable</code> : outputs the value by
0474: * calling <code>v.toSax(contentHandler)</code>.
0475: *
0476: * @param parent
0477: * The node getting the value
0478: * @param v
0479: * the XML fragment
0480: */
0481: public static void valueOf(Node parent, XMLizable v)
0482: throws ProcessingException {
0483: if (v != null) {
0484: DOMBuilder builder = new DOMBuilder(parent);
0485: try {
0486: v.toSAX(builder);
0487: } catch (SAXException e) {
0488: throw new ProcessingException(e);
0489: }
0490: }
0491: }
0492:
0493: /**
0494: * Implementation for <code>org.w3c.dom.Node</code> : converts the Node to
0495: * a SAX event stream.
0496: *
0497: * @param parent
0498: * The node getting the value
0499: * @param v
0500: * the value
0501: */
0502: public static void valueOf(Node parent, Node v)
0503: throws ProcessingException {
0504: if (v != null) {
0505: parent.appendChild(parent.getOwnerDocument().importNode(v,
0506: true));
0507: }
0508: }
0509:
0510: /**
0511: * Implementation for <code>java.util.Collection</code> : outputs the
0512: * value by calling {@link #valueOf(Node, Object)} on each element of the
0513: * collection.
0514: *
0515: * @param parent
0516: * The node getting the value
0517: * @param v
0518: * the XML fragment
0519: */
0520: public static void valueOf(Node parent, Collection v)
0521: throws ProcessingException {
0522: if (v != null) {
0523: Iterator iterator = v.iterator();
0524: while (iterator.hasNext()) {
0525: valueOf(parent, iterator.next());
0526: }
0527: }
0528: }
0529:
0530: /**
0531: * Implementation for <code>java.util.Map</code> : For each entry an
0532: * element is created with the childs key and value Outputs the value and
0533: * the key by calling {@link #valueOf(Node, Object)} on each value and key
0534: * of the Map.
0535: *
0536: * @param parent
0537: * The node getting the value
0538: * @param v
0539: * the Map
0540: */
0541: public static void valueOf(Node parent, Map v)
0542: throws ProcessingException {
0543: if (v != null) {
0544: Node mapNode = parent.getOwnerDocument().createElementNS(
0545: null, "java.util.map");
0546: parent.appendChild(mapNode);
0547: for (Iterator iter = v.entrySet().iterator(); iter
0548: .hasNext();) {
0549: Map.Entry me = (Map.Entry) iter.next();
0550:
0551: Node entryNode = mapNode.getOwnerDocument()
0552: .createElementNS(null, "entry");
0553: mapNode.appendChild(entryNode);
0554:
0555: Node keyNode = entryNode.getOwnerDocument()
0556: .createElementNS(null, "key");
0557: entryNode.appendChild(keyNode);
0558: valueOf(keyNode, me.getKey());
0559:
0560: Node valueNode = entryNode.getOwnerDocument()
0561: .createElementNS(null, "value");
0562: entryNode.appendChild(valueNode);
0563: valueOf(valueNode, me.getValue());
0564: }
0565: }
0566: }
0567:
0568: /**
0569: * Implementation for <code>Object</code> depending on its class :
0570: * <ul>
0571: * <li>if it's an array, call {@link #valueOf(Node, Object)} on all its
0572: * elements,</li>
0573: * <li>if it's class has a specific {@link #valueOf(Node, Object)}
0574: * implementation, use it,</li>
0575: * <li>else, output it's string representation.</li>
0576: * </ul>
0577: *
0578: * @param parent
0579: * The node getting the value
0580: * @param v
0581: * the value
0582: */
0583: public static void valueOf(Node parent, Object v)
0584: throws ProcessingException {
0585: if (v == null) {
0586: return;
0587: }
0588:
0589: // Array: recurse over each element
0590: if (v.getClass().isArray()) {
0591: Object[] elements = (Object[]) v;
0592:
0593: for (int i = 0; i < elements.length; i++) {
0594: valueOf(parent, elements[i]);
0595: }
0596: return;
0597: }
0598:
0599: // Check handled object types in case they were not typed in the XSP
0600:
0601: // XMLizable
0602: if (v instanceof XMLizable) {
0603: valueOf(parent, (XMLizable) v);
0604: return;
0605: }
0606:
0607: // Node
0608: if (v instanceof Node) {
0609: valueOf(parent, (Node) v);
0610: return;
0611: }
0612:
0613: // Collection
0614: if (v instanceof Collection) {
0615: valueOf(parent, (Collection) v);
0616: return;
0617: }
0618:
0619: // Map
0620: if (v instanceof Map) {
0621: valueOf(parent, (Map) v);
0622: return;
0623: }
0624:
0625: // Give up: hope it's a string or has a meaningful string representation
0626: valueOf(parent, String.valueOf(v));
0627: }
0628:
0629: /**
0630: * Use an XPath string to select a single node. XPath namespace prefixes are
0631: * resolved from the context node, which may not be what you want (see the
0632: * next method).
0633: *
0634: * @param contextNode
0635: * The node to start searching from.
0636: * @param str
0637: * A valid XPath string.
0638: * @param processor
0639: * The XPath processor to use
0640: * @return The first node found that matches the XPath, or null.
0641: *
0642: * @throws TransformerException
0643: */
0644: public static Node getSingleNode(Node contextNode, String str,
0645: XPathProcessor processor) throws TransformerException {
0646: String[] pathComponents = buildPathArray(str);
0647: if (pathComponents == null) {
0648: return processor.selectSingleNode(contextNode, str);
0649: } else {
0650: return getFirstNodeFromPath(contextNode, pathComponents,
0651: false);
0652: }
0653: }
0654:
0655: /**
0656: * Use an XPath string to select a single node. XPath namespace prefixes are
0657: * resolved from the context node, which may not be what you want (see the
0658: * next method).
0659: *
0660: * @param contextNode
0661: * The node to start searching from.
0662: * @param str
0663: * A valid XPath string.
0664: * @return The first node found that matches the XPath, or null.
0665: *
0666: * @throws TransformerException
0667: * @deprecated. To be removed in 2.2.
0668: */
0669: public static Node getSingleNode(Node contextNode, String str)
0670: throws TransformerException {
0671: String[] pathComponents = buildPathArray(str);
0672: if (pathComponents == null) {
0673: return XPathAPI.selectSingleNode(contextNode, str);
0674: } else {
0675: return getFirstNodeFromPath(contextNode, pathComponents,
0676: false);
0677: }
0678: }
0679:
0680: /**
0681: * Return the <CODE>Node</CODE> from the DOM Node <CODE>rootNode</CODE>
0682: * using the XPath expression <CODE>path</CODE>. If the node does not
0683: * exist, it is created and then returned. This is a very simple method for
0684: * creating new nodes. If the XPath contains selectors ([,,,]) or "*" it is
0685: * of course not possible to create the new node. So if you use such XPaths
0686: * the node must exist beforehand. An simple exception is if the expression
0687: * contains attribute test to values (e.g. [@id = 'du' and
0688: *
0689: * @number = 'you'], the attributes with the given values are added. The
0690: * attributes must be separated with 'and'. Another problem are
0691: * namespaces: XPath requires sometimes selectors for namespaces,
0692: * e.g. : /*[namespace-uri()="uri" and local-name()="name"] Creating
0693: * such a node with a namespace is not possible right now as we use
0694: * a very simple XPath parser which is not able to parse all kinds
0695: * of selectors correctly.
0696: *
0697: * @param rootNode
0698: * The node to start the search.
0699: * @param path
0700: * XPath expression for searching the node.
0701: * @return The node specified by the path.
0702: * @throws ProcessingException
0703: * If no path is specified or the XPath engine fails.
0704: * @deprecated To be removed in 2.2.
0705: */
0706: public static Node selectSingleNode(Node rootNode, String path)
0707: throws ProcessingException {
0708: // Now we have to parse the string
0709: // First test: path? rootNode?
0710: if (path == null) {
0711: throw new ProcessingException(XPATH_IS_REQUIRED);
0712: }
0713: if (rootNode == null)
0714: return rootNode;
0715:
0716: if (path.length() == 0 || path.equals("/"))
0717: return rootNode;
0718:
0719: // now the first "quick" test is if the node exists using the
0720: // full XPathAPI
0721: try {
0722: Node testNode = getSingleNode(rootNode, path);
0723: if (testNode != null)
0724: return testNode;
0725: } catch (TransformerException local) {
0726: throw new ProcessingException(
0727: "Transforming exception during selectSingleNode with path: '"
0728: + path + "'. Exception: " + local, local);
0729: }
0730: // Remove leading "/" on both ends
0731: path = StringUtils.strip(path, "/");
0732:
0733: // now step through the nodes!
0734: Node parent = rootNode;
0735: int pos;
0736: int posSelector;
0737: do {
0738: pos = path.indexOf("/"); // get next separator
0739: posSelector = path.indexOf("[");
0740: if (posSelector != -1 && posSelector < pos) {
0741: posSelector = path.indexOf("]");
0742: pos = path.indexOf("/", posSelector);
0743: }
0744:
0745: String nodeName;
0746: boolean isAttribute = false;
0747: if (pos != -1) { // found separator
0748: nodeName = path.substring(0, pos); // string until "/"
0749: path = path.substring(pos + 1); // rest of string after "/"
0750: } else {
0751: nodeName = path;
0752: }
0753:
0754: // test for attribute spec
0755: if (nodeName.startsWith("@")) {
0756: isAttribute = true;
0757: }
0758:
0759: Node singleNode;
0760: try {
0761: singleNode = getSingleNode(parent, nodeName);
0762: } catch (TransformerException localException) {
0763: throw new ProcessingException(
0764: "XPathUtil.selectSingleNode: "
0765: + localException.getMessage(),
0766: localException);
0767: }
0768:
0769: // create node if necessary
0770: if (singleNode == null) {
0771: Node newNode;
0772: // delete XPath selectors
0773: int posSelect = nodeName.indexOf("[");
0774: String XPathExp = null;
0775: if (posSelect != -1) {
0776: XPathExp = nodeName.substring(posSelect + 1,
0777: nodeName.length() - 1);
0778: nodeName = nodeName.substring(0, posSelect);
0779: }
0780: if (isAttribute) {
0781: try {
0782: newNode = getOwnerDocument(rootNode)
0783: .createAttributeNS(null,
0784: nodeName.substring(1));
0785: ((Element) parent)
0786: .setAttributeNodeNS((org.w3c.dom.Attr) newNode);
0787: parent = newNode;
0788: } catch (DOMException local) {
0789: throw new ProcessingException(
0790: "Unable to create new DOM node: '"
0791: + nodeName + "'.", local);
0792: }
0793: } else {
0794: try {
0795: newNode = getOwnerDocument(rootNode)
0796: .createElementNS(null, nodeName);
0797: } catch (DOMException local) {
0798: throw new ProcessingException(
0799: "Unable to create new DOM node: '"
0800: + nodeName + "'.", local);
0801: }
0802: if (XPathExp != null) {
0803: java.util.List attrValuePairs = new java.util.ArrayList(
0804: 4);
0805: boolean noError = true;
0806:
0807: String attr;
0808: String value;
0809: // scan for attributes
0810: StringTokenizer tokenizer = new StringTokenizer(
0811: XPathExp, "= ");
0812: while (tokenizer.hasMoreTokens()) {
0813: attr = tokenizer.nextToken();
0814: if (attr.startsWith("@")) {
0815: if (tokenizer.hasMoreTokens()) {
0816: value = tokenizer.nextToken();
0817: if (value.startsWith("'")
0818: && value.endsWith("'"))
0819: value = value.substring(1,
0820: value.length() - 1);
0821: if (value.startsWith("\"")
0822: && value.endsWith("\""))
0823: value = value.substring(1,
0824: value.length() - 1);
0825: attrValuePairs.add(attr
0826: .substring(1));
0827: attrValuePairs.add(value);
0828: } else {
0829: noError = false;
0830: }
0831: } else if (attr.trim().equals("and") == false) {
0832: noError = false;
0833: }
0834: }
0835: if (noError) {
0836: for (int l = 0; l < attrValuePairs.size(); l = l + 2) {
0837: ((Element) newNode).setAttributeNS(
0838: null, (String) attrValuePairs
0839: .get(l),
0840: (String) attrValuePairs
0841: .get(l + 1));
0842: }
0843: }
0844: }
0845: parent.appendChild(newNode);
0846: parent = newNode;
0847: }
0848: } else {
0849: parent = singleNode;
0850: }
0851: } while (pos != -1);
0852: return parent;
0853: }
0854:
0855: /**
0856: * Return the <CODE>Node</CODE> from the DOM Node <CODE>rootNode</CODE>
0857: * using the XPath expression <CODE>path</CODE>. If the node does not
0858: * exist, it is created and then returned. This is a very simple method for
0859: * creating new nodes. If the XPath contains selectors ([,,,]) or "*" it is
0860: * of course not possible to create the new node. So if you use such XPaths
0861: * the node must exist beforehand. An simple exception is if the expression
0862: * contains attribute test to values (e.g. [@id = 'du' and
0863: *
0864: * @number = 'you'], the attributes with the given values are added. The
0865: * attributes must be separated with 'and'. Another problem are
0866: * namespaces: XPath requires sometimes selectors for namespaces,
0867: * e.g. : /*[namespace-uri()="uri" and local-name()="name"] Creating
0868: * such a node with a namespace is not possible right now as we use
0869: * a very simple XPath parser which is not able to parse all kinds
0870: * of selectors correctly.
0871: *
0872: * @param rootNode
0873: * The node to start the search.
0874: * @param path
0875: * XPath expression for searching the node.
0876: * @param processor
0877: * The XPath processor to use
0878: * @return The node specified by the path.
0879: * @throws ProcessingException
0880: * If no path is specified or the XPath engine fails.
0881: */
0882: public static Node selectSingleNode(Node rootNode, String path,
0883: XPathProcessor processor) throws ProcessingException {
0884: // Now we have to parse the string
0885: // First test: path? rootNode?
0886: if (path == null) {
0887: throw new ProcessingException(XPATH_IS_REQUIRED);
0888: }
0889: if (rootNode == null)
0890: return rootNode;
0891:
0892: if (path.length() == 0 || path.equals("/"))
0893: return rootNode;
0894:
0895: // now the first "quick" test is if the node exists using the
0896: // full XPathAPI
0897: try {
0898: Node testNode = getSingleNode(rootNode, path, processor);
0899: if (testNode != null)
0900: return testNode;
0901: } catch (TransformerException local) {
0902: throw new ProcessingException(
0903: "Transforming exception during selectSingleNode with path: '"
0904: + path + "'. Exception: " + local, local);
0905: }
0906:
0907: // remove leading "/" oon both ends
0908: path = StringUtils.strip(path, "/");
0909:
0910: // now step through the nodes!
0911: Node parent = rootNode;
0912: int pos;
0913: int posSelector;
0914: do {
0915: pos = path.indexOf("/"); // get next separator
0916: posSelector = path.indexOf("[");
0917: if (posSelector != -1 && posSelector < pos) {
0918: posSelector = path.indexOf("]");
0919: pos = path.indexOf("/", posSelector);
0920: }
0921:
0922: String nodeName;
0923: boolean isAttribute = false;
0924: if (pos != -1) { // found separator
0925: nodeName = path.substring(0, pos); // string until "/"
0926: path = path.substring(pos + 1); // rest of string after "/"
0927: } else {
0928: nodeName = path;
0929: }
0930:
0931: // test for attribute spec
0932: if (nodeName.startsWith("@")) {
0933: isAttribute = true;
0934: }
0935:
0936: Node singleNode;
0937: try {
0938: singleNode = getSingleNode(parent, nodeName, processor);
0939: } catch (TransformerException localException) {
0940: throw new ProcessingException(
0941: "XPathUtil.selectSingleNode: "
0942: + localException.getMessage(),
0943: localException);
0944: }
0945:
0946: // create node if necessary
0947: if (singleNode == null) {
0948: Node newNode;
0949: // delete XPath selectors
0950: int posSelect = nodeName.indexOf("[");
0951: String XPathExp = null;
0952: if (posSelect != -1) {
0953: XPathExp = nodeName.substring(posSelect + 1,
0954: nodeName.length() - 1);
0955: nodeName = nodeName.substring(0, posSelect);
0956: }
0957: if (isAttribute) {
0958: try {
0959: newNode = getOwnerDocument(rootNode)
0960: .createAttributeNS(null,
0961: nodeName.substring(1));
0962: ((Element) parent)
0963: .setAttributeNodeNS((org.w3c.dom.Attr) newNode);
0964: parent = newNode;
0965: } catch (DOMException local) {
0966: throw new ProcessingException(
0967: "Unable to create new DOM node: '"
0968: + nodeName + "'.", local);
0969: }
0970: } else {
0971: try {
0972: newNode = getOwnerDocument(rootNode)
0973: .createElementNS(null, nodeName);
0974: } catch (DOMException local) {
0975: throw new ProcessingException(
0976: "Unable to create new DOM node: '"
0977: + nodeName + "'.", local);
0978: }
0979: if (XPathExp != null) {
0980: java.util.List attrValuePairs = new java.util.ArrayList(
0981: 4);
0982: boolean noError = true;
0983:
0984: String attr;
0985: String value;
0986: // scan for attributes
0987: StringTokenizer tokenizer = new StringTokenizer(
0988: XPathExp, "= ");
0989: while (tokenizer.hasMoreTokens()) {
0990: attr = tokenizer.nextToken();
0991: if (attr.startsWith("@")) {
0992: if (tokenizer.hasMoreTokens()) {
0993: value = tokenizer.nextToken();
0994: if (value.startsWith("'")
0995: && value.endsWith("'"))
0996: value = value.substring(1,
0997: value.length() - 1);
0998: if (value.startsWith("\"")
0999: && value.endsWith("\""))
1000: value = value.substring(1,
1001: value.length() - 1);
1002: attrValuePairs.add(attr
1003: .substring(1));
1004: attrValuePairs.add(value);
1005: } else {
1006: noError = false;
1007: }
1008: } else if (attr.trim().equals("and") == false) {
1009: noError = false;
1010: }
1011: }
1012: if (noError) {
1013: for (int l = 0; l < attrValuePairs.size(); l = l + 2) {
1014: ((Element) newNode).setAttributeNS(
1015: null, (String) attrValuePairs
1016: .get(l),
1017: (String) attrValuePairs
1018: .get(l + 1));
1019: }
1020: }
1021: }
1022: parent.appendChild(newNode);
1023: parent = newNode;
1024: }
1025: } else {
1026: parent = singleNode;
1027: }
1028: } while (pos != -1);
1029: return parent;
1030: }
1031:
1032: /**
1033: * Get the value of the node specified by the XPath. This works similar to
1034: * <xsl:value-of>. If the node does not exist <CODE>null</CODE> is
1035: * returned.
1036: *
1037: * @param root
1038: * The node to start the search.
1039: * @param path
1040: * XPath search expression.
1041: * @return The value of the node or <CODE>null</CODE>
1042: * @deprecated To be removed in 2.2.
1043: */
1044: public static String getValueOf(Node root, String path)
1045: throws ProcessingException {
1046: if (path == null) {
1047: throw new ProcessingException(XPATH_IS_REQUIRED);
1048: }
1049: if (root == null)
1050: return null;
1051: path = StringUtils.strip(path, "/");
1052:
1053: try {
1054: Node node = getSingleNode(root, path);
1055: if (node != null) {
1056: return getValueOfNode(node);
1057: }
1058: } catch (TransformerException localException) {
1059: throw new ProcessingException(
1060: "XPathUtil.selectSingleNode: "
1061: + localException.getMessage(),
1062: localException);
1063: }
1064: return null;
1065: }
1066:
1067: /**
1068: * Get the value of the node specified by the XPath. This works similar to
1069: * <xsl:value-of>. If the node does not exist <CODE>null</CODE> is
1070: * returned.
1071: *
1072: * @param root
1073: * The node to start the search.
1074: * @param path
1075: * XPath search expression.
1076: * @param processor
1077: * The XPath processor to use
1078: * @return The value of the node or <CODE>null</CODE>
1079: */
1080: public static String getValueOf(Node root, String path,
1081: XPathProcessor processor) throws ProcessingException {
1082: if (path == null) {
1083: throw new ProcessingException(XPATH_IS_REQUIRED);
1084: }
1085: if (root == null)
1086: return null;
1087: path = StringUtils.strip(path, "/");
1088:
1089: try {
1090: Node node = getSingleNode(root, path, processor);
1091: if (node != null) {
1092: return getValueOfNode(node);
1093: }
1094: } catch (TransformerException localException) {
1095: throw new ProcessingException(
1096: "XPathUtil.selectSingleNode: "
1097: + localException.getMessage(),
1098: localException);
1099: }
1100: return null;
1101: }
1102:
1103: /**
1104: * Get the value of the node specified by the XPath. This works similar to
1105: * <xsl:value-of>. If the node is not found the <CODE>defaultValue</CODE>
1106: * is returned.
1107: *
1108: * @param root
1109: * The node to start the search.
1110: * @param path
1111: * XPath search expression.
1112: * @param defaultValue
1113: * The default value if the node does not exist.
1114: * @return The value of the node or <CODE>defaultValue</CODE>
1115: * @deprecated To be removed in 2.2.
1116: */
1117: public static String getValueOf(Node root, String path,
1118: String defaultValue) throws ProcessingException {
1119: String value = getValueOf(root, path);
1120: if (value == null) {
1121: value = defaultValue;
1122: }
1123: return value;
1124: }
1125:
1126: /**
1127: * Get the value of the node specified by the XPath. This works similar to
1128: * <xsl:value-of>. If the node is not found the <CODE>defaultValue</CODE>
1129: * is returned.
1130: *
1131: * @param root
1132: * The node to start the search.
1133: * @param path
1134: * XPath search expression.
1135: * @param defaultValue
1136: * The default value if the node does not exist.
1137: * @param processor
1138: * The XPath Processor
1139: * @return The value of the node or <CODE>defaultValue</CODE>
1140: */
1141: public static String getValueOf(Node root, String path,
1142: String defaultValue, XPathProcessor processor)
1143: throws ProcessingException {
1144: String value = getValueOf(root, path, processor);
1145: if (value == null) {
1146: value = defaultValue;
1147: }
1148: return value;
1149: }
1150:
1151: /**
1152: * Get the boolean value of the node specified by the XPath. This works
1153: * similar to <xsl:value-of>. If the node exists and has a value this
1154: * value is converted to a boolean, e.g. "true" or "false" as value will
1155: * result into the corresponding boolean values.
1156: *
1157: * @param root
1158: * The node to start the search.
1159: * @param path
1160: * XPath search expression.
1161: * @return The boolean value of the node.
1162: * @throws ProcessingException
1163: * If the node is not found.
1164: * @deprecated To be removed in 2.2.
1165: */
1166: public static boolean getValueAsBooleanOf(Node root, String path)
1167: throws ProcessingException {
1168: String value = getValueOf(root, path);
1169: if (value != null) {
1170: return Boolean.valueOf(value).booleanValue();
1171: } else {
1172: throw new ProcessingException("No such node: " + path);
1173: }
1174: }
1175:
1176: /**
1177: * Get the boolean value of the node specified by the XPath. This works
1178: * similar to <xsl:value-of>. If the node exists and has a value this
1179: * value is converted to a boolean, e.g. "true" or "false" as value will
1180: * result into the corresponding boolean values.
1181: *
1182: * @param root
1183: * The node to start the search.
1184: * @param path
1185: * XPath search expression.
1186: * @param processor
1187: * The XPath Processor
1188: * @return The boolean value of the node.
1189: * @throws ProcessingException
1190: * If the node is not found.
1191: */
1192: public static boolean getValueAsBooleanOf(Node root, String path,
1193: XPathProcessor processor) throws ProcessingException {
1194: String value = getValueOf(root, path, processor);
1195: if (value == null) {
1196: throw new ProcessingException("No such node: " + path);
1197: }
1198: return Boolean.valueOf(value).booleanValue();
1199: }
1200:
1201: /**
1202: * Get the boolean value of the node specified by the XPath. This works
1203: * similar to <xsl:value-of>. If the node exists and has a value this
1204: * value is converted to a boolean, e.g. "true" or "false" as value will
1205: * result into the corresponding boolean values. If the node does not exist,
1206: * the <CODE>defaultValue</CODE> is returned.
1207: *
1208: * @param root
1209: * The node to start the search.
1210: * @param path
1211: * XPath search expression.
1212: * @param defaultValue
1213: * Default boolean value.
1214: * @return The value of the node or <CODE>defaultValue</CODE>
1215: * @deprecated To be removed in 2.2.
1216: */
1217: public static boolean getValueAsBooleanOf(Node root, String path,
1218: boolean defaultValue) throws ProcessingException {
1219: String value = getValueOf(root, path);
1220: if (value != null) {
1221: return Boolean.valueOf(value).booleanValue();
1222: }
1223: return defaultValue;
1224: }
1225:
1226: /**
1227: * Get the boolean value of the node specified by the XPath. This works
1228: * similar to <xsl:value-of>. If the node exists and has a value this
1229: * value is converted to a boolean, e.g. "true" or "false" as value will
1230: * result into the corresponding boolean values. If the node does not exist,
1231: * the <CODE>defaultValue</CODE> is returned.
1232: *
1233: * @param root
1234: * The node to start the search.
1235: * @param path
1236: * XPath search expression.
1237: * @param defaultValue
1238: * Default boolean value.
1239: * @param processor
1240: * The XPath Processor
1241: * @return The value of the node or <CODE>defaultValue</CODE>
1242: */
1243: public static boolean getValueAsBooleanOf(Node root, String path,
1244: boolean defaultValue, XPathProcessor processor)
1245: throws ProcessingException {
1246: String value = getValueOf(root, path, processor);
1247: if (value != null) {
1248: return Boolean.valueOf(value).booleanValue();
1249: }
1250: return defaultValue;
1251: }
1252:
1253: /**
1254: * Create a new empty DOM document.
1255: */
1256: public static Document createDocument() throws ProcessingException {
1257: try {
1258: DocumentBuilderFactory documentFactory = DocumentBuilderFactory
1259: .newInstance();
1260: documentFactory.setNamespaceAware(true);
1261: documentFactory.setValidating(false);
1262: DocumentBuilder docBuilder = documentFactory
1263: .newDocumentBuilder();
1264: return docBuilder.newDocument();
1265: } catch (ParserConfigurationException pce) {
1266: throw new ProcessingException("Creating document failed.",
1267: pce);
1268: }
1269: }
1270:
1271: /**
1272: * Use an XPath string to select a nodelist. XPath namespace prefixes are
1273: * resolved from the contextNode.
1274: *
1275: * @param contextNode
1276: * The node to start searching from.
1277: * @param str
1278: * A valid XPath string.
1279: * @return A NodeIterator, should never be null.
1280: *
1281: * @throws TransformerException
1282: * @deprecated To be removed in 2.2.
1283: */
1284: public static NodeList selectNodeList(Node contextNode, String str)
1285: throws TransformerException {
1286: String[] pathComponents = buildPathArray(str);
1287: if (pathComponents != null) {
1288: return getNodeListFromPath(contextNode, pathComponents);
1289: }
1290: return XPathAPI.selectNodeList(contextNode, str);
1291: }
1292:
1293: /**
1294: * Use an XPath string to select a nodelist. XPath namespace prefixes are
1295: * resolved from the contextNode.
1296: *
1297: * @param contextNode
1298: * The node to start searching from.
1299: * @param str
1300: * A valid XPath string.
1301: * @param processor
1302: * The XPath Processor
1303: * @return A NodeIterator, should never be null.
1304: *
1305: * @throws TransformerException
1306: */
1307: public static NodeList selectNodeList(Node contextNode, String str,
1308: XPathProcessor processor) throws TransformerException {
1309: String[] pathComponents = buildPathArray(str);
1310: if (pathComponents != null) {
1311: return getNodeListFromPath(contextNode, pathComponents);
1312: }
1313: return processor.selectNodeList(contextNode, str);
1314: }
1315:
1316: /**
1317: * Build the input for the get...FromPath methods. If the XPath expression
1318: * cannot be handled by the methods, <code>null</code> is returned.
1319: */
1320: public static String[] buildPathArray(String xpath) {
1321: String[] result = null;
1322: if (xpath != null && xpath.charAt(0) != '/') {
1323: // test
1324: int components = 1;
1325: int i, l;
1326: l = xpath.length();
1327: boolean found = false;
1328: i = 0;
1329: while (i < l && found == false) {
1330: switch (xpath.charAt(i)) {
1331: case '[':
1332: found = true;
1333: break;
1334: case '(':
1335: found = true;
1336: break;
1337: case '*':
1338: found = true;
1339: break;
1340: case '@':
1341: found = true;
1342: break;
1343: case ':':
1344: found = true;
1345: break;
1346: case '/':
1347: components++;
1348: default:
1349: i++;
1350: }
1351: }
1352: if (found == false) {
1353: result = new String[components];
1354: if (components == 1) {
1355: result[components - 1] = xpath;
1356: } else {
1357: i = 0;
1358: int start = 0;
1359: components = 0;
1360: while (i < l) {
1361: if (xpath.charAt(i) == '/') {
1362: result[components] = xpath.substring(start,
1363: i);
1364: start = i + 1;
1365: components++;
1366: }
1367: i++;
1368: }
1369: result[components] = xpath.substring(start);
1370: }
1371: }
1372: }
1373: return result;
1374: }
1375:
1376: /**
1377: * Use a path to select the first occurence of a node. The namespace of a
1378: * node is ignored!
1379: *
1380: * @param contextNode
1381: * The node starting the search.
1382: * @param path
1383: * The path to search the node. The contextNode is searched for a
1384: * child named path[0], this node is searched for a child named
1385: * path[1]...
1386: * @param create
1387: * If a child with the corresponding name is not found and create
1388: * is set, this node will be created.
1389: */
1390: public static Node getFirstNodeFromPath(Node contextNode,
1391: final String[] path, final boolean create) {
1392: if (contextNode == null || path == null || path.length == 0)
1393: return contextNode;
1394: // first test if the node exists
1395: Node item = getFirstNodeFromPath(contextNode, path, 0);
1396: if (item == null && create) {
1397: int i = 0;
1398: NodeList childs;
1399: boolean found;
1400: int m, l;
1401: while (contextNode != null && i < path.length) {
1402: childs = contextNode.getChildNodes();
1403: found = false;
1404: if (childs != null) {
1405: m = 0;
1406: l = childs.getLength();
1407: while (found == false && m < l) {
1408: item = childs.item(m);
1409: if (item.getNodeType() == Node.ELEMENT_NODE
1410: && item.getLocalName().equals(path[i])) {
1411: found = true;
1412: contextNode = item;
1413: }
1414: m++;
1415: }
1416: }
1417: if (found == false) {
1418: Element e = contextNode.getOwnerDocument()
1419: .createElementNS(null, path[i]);
1420: contextNode.appendChild(e);
1421: contextNode = e;
1422: }
1423: i++;
1424: }
1425: item = contextNode;
1426: }
1427: return item;
1428: }
1429:
1430: /**
1431: * Private helper method for getFirstNodeFromPath()
1432: */
1433: private static Node getFirstNodeFromPath(final Node contextNode,
1434: final String[] path, final int startIndex) {
1435: int i = 0;
1436: NodeList childs;
1437: boolean found;
1438: int l;
1439: Node item = null;
1440:
1441: childs = contextNode.getChildNodes();
1442: found = false;
1443: if (childs != null) {
1444: i = 0;
1445: l = childs.getLength();
1446: while (found == false && i < l) {
1447: item = childs.item(i);
1448: if (item.getNodeType() == Node.ELEMENT_NODE
1449: && path[startIndex]
1450: .equals(item.getLocalName() != null ? item
1451: .getLocalName()
1452: : item.getNodeName())) {
1453: if (startIndex == path.length - 1) {
1454: found = true;
1455: } else {
1456: item = getFirstNodeFromPath(item, path,
1457: startIndex + 1);
1458: if (item != null)
1459: found = true;
1460: }
1461: }
1462: if (found == false) {
1463: i++;
1464: }
1465: }
1466: if (found == false) {
1467: item = null;
1468: }
1469: }
1470: return item;
1471: }
1472:
1473: /**
1474: * Use a path to select all occurences of a node. The namespace of a node is
1475: * ignored!
1476: *
1477: * @param contextNode
1478: * The node starting the search.
1479: * @param path
1480: * The path to search the node. The contextNode is searched for a
1481: * child named path[0], this node is searched for a child named
1482: * path[1]...
1483: */
1484: public static NodeList getNodeListFromPath(Node contextNode,
1485: String[] path) {
1486: if (contextNode == null)
1487: return new NodeListImpl();
1488: if (path == null || path.length == 0) {
1489: return new NodeListImpl(new Node[] { contextNode });
1490: }
1491: NodeListImpl result = new NodeListImpl();
1492: try {
1493: getNodesFromPath(result, contextNode, path, 0);
1494: } catch (NullPointerException npe) {
1495: // this NPE is thrown because the parser is not configured
1496: // to use DOM Level 2
1497: throw new NullPointerException(
1498: "XMLUtil.getNodeListFromPath() did catch a NullPointerException."
1499: + "This might be due to a missconfigured XML parser which does not use DOM Level 2."
1500: + "Make sure that you use the XML parser shipped with Cocoon.");
1501: }
1502: return result;
1503: }
1504:
1505: /**
1506: * Helper method for getNodeListFromPath()
1507: */
1508: private static void getNodesFromPath(final NodeListImpl result,
1509: final Node contextNode, final String[] path,
1510: final int startIndex) {
1511: final NodeList childs = contextNode.getChildNodes();
1512: int m, l;
1513: Node item;
1514: if (startIndex == (path.length - 1)) {
1515: if (childs != null) {
1516: m = 0;
1517: l = childs.getLength();
1518: while (m < l) {
1519: item = childs.item(m);
1520: if (item.getNodeType() == Node.ELEMENT_NODE) {
1521: // Work around: org.apache.xerces.dom.ElementImpl
1522: // doesn't handle getLocalName() correct
1523: if (path[startIndex]
1524: .equals(item.getLocalName() != null ? item
1525: .getLocalName()
1526: : item.getNodeName())) {
1527: result.addNode(item);
1528: }
1529: }
1530: m++;
1531: }
1532: }
1533: } else {
1534: if (childs != null) {
1535: m = 0;
1536: l = childs.getLength();
1537: while (m < l) {
1538: item = childs.item(m);
1539: if (item.getNodeType() == Node.ELEMENT_NODE) {
1540: // Work around: org.apache.xerces.dom.ElementImpl
1541: // doesn't handle getLocalName() correct
1542: if (path[startIndex]
1543: .equals(item.getLocalName() != null ? item
1544: .getLocalName()
1545: : item.getNodeName())) {
1546: getNodesFromPath(result, item, path,
1547: startIndex + 1);
1548: }
1549: }
1550: m++;
1551: }
1552: }
1553: }
1554: }
1555:
1556: /**
1557: * Converts a org.w3c.dom.Node to a String. Uses
1558: * {@link javax.xml.transform.Transformer} to convert from a Node to a
1559: * String.
1560: *
1561: * @param node
1562: * a <code>org.w3c.dom.Node</code> value
1563: * @return String representation of the document
1564: * @deprecated Use {@link XMLUtils#serializeNodeToXML(Node)} instead. To be
1565: * removed in 2.2.
1566: */
1567: public static String node2String(Node node) {
1568: try {
1569: return XMLUtils.serializeNodeToXML(node);
1570: } catch (ProcessingException e) {
1571: // Empty
1572: }
1573: return "";
1574: }
1575:
1576: /**
1577: * Create a string representation of a org.w3c.dom.Node and any (most)
1578: * subtypes.
1579: *
1580: * @param node
1581: * a <code>org.w3c.dom.Node</code> value
1582: * @param pretty
1583: * a <code>boolean</code> value whether to format the XML
1584: * @return a <code>String</code> value
1585: * @deprecated Please use {@link XMLUtils#serializeNode(Node, Properties)}
1586: * instead. To be removed in 2.2.
1587: */
1588: public static String node2String(Node node, boolean pretty) {
1589: try {
1590: if (pretty) {
1591: Properties props = new Properties();
1592: props.setProperty(OutputKeys.INDENT, "yes");
1593: return XMLUtils.serializeNode(node, props);
1594: } else {
1595: return XMLUtils.serializeNodeToXML(node);
1596: }
1597: } catch (ProcessingException e) {
1598: }
1599: return "";
1600: }
1601:
1602: /**
1603: * Create a string representation of a org.w3c.dom.Node and any (most)
1604: * subtypes.
1605: *
1606: * @param node
1607: * a <code>org.w3c.dom.Node</code> value
1608: * @return a <code>StringBuffer</code> value
1609: * @deprecated Please use {@link XMLUtils#serializeNodeToXML(Node)} instead.
1610: * To be removed in 2.2.
1611: */
1612: public static StringBuffer node2StringBuffer(Node node) {
1613: return new StringBuffer(node2String(node));
1614: }
1615:
1616: /**
1617: * Create a string representation of a org.w3c.dom.Node and any (most)
1618: * subtypes.
1619: *
1620: * @param node
1621: * a <code>org.w3c.dom.Node</code> value
1622: * @param pretty
1623: * a <code>boolean</code> value whether to format the XML
1624: * @param indent
1625: * a <code>String</code> value containing spaces as initial
1626: * indent, if null defaults to empty string.
1627: * @return a <code>StringBuffer</code> value
1628: * @deprecated Please use {@link XMLUtils#serializeNode(Node, Properties)}
1629: * instead. To be removed in 2.2.
1630: */
1631: public static StringBuffer node2StringBuffer(Node node,
1632: boolean pretty, String indent) {
1633: return new StringBuffer(node2String(node, pretty));
1634: }
1635: }
|