blob: 3ca71bbd41ad38cb4eea92283c3059d227d032b7 [file] [log] [blame]
/*******************************************************************************
* 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.ofbiz.base.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.xerces.parsers.DOMParser;
import org.apache.xerces.xni.Augmentations;
import org.apache.xerces.xni.NamespaceContext;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLAttributes;
import org.apache.xerces.xni.XMLLocator;
import org.apache.xerces.xni.XMLResourceIdentifier;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XNIException;
import org.w3c.dom.DOMConfiguration;
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.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
/**
* Utilities methods to simplify dealing with JAXP & DOM XML parsing
*
*/
public final class UtilXml {
public static final String module = UtilXml.class.getName();
private static final XStream xstream = createXStream();
private UtilXml () {}
private static XStream createXStream() {
XStream xstream = new XStream();
/* This method is a pure helper method for XStream 1.4.x.
* It initializes an XStream instance with a white list of well-known and simply types of the Java runtime
* as it is done in XStream 1.5.x by default. This method will do therefore nothing in XStream 1.5
* and could be removed them
*/
XStream.setupDefaultSecurity(xstream);
/* You may want to enhance the white list created by XStream::setupDefaultSecurity (or by default with XStream 1.5)
* using xstream::allowTypesByWildcard with your own classes
*/
return xstream;
}
// ----- DOM Level 3 Load and Save Methods -- //
/** Returns a <code>DOMImplementationLS</code> instance.
* @return A <code>DOMImplementationLS</code> instance
* @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/">DOM Level 3 Load and Save Specification</a>
* @throws ClassCastException
* @throws ClassNotFoundException
* @throws InstantiationException
* @throws IllegalAccessException
*/
public static DOMImplementationLS getDomLsImplementation() throws ClassCastException, ClassNotFoundException, InstantiationException, IllegalAccessException {
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
return (DOMImplementationLS)registry.getDOMImplementation("LS");
}
/** Returns a <code>LSOutput</code> instance.
* @param impl A <code>DOMImplementationLS</code> instance
* @param os Optional <code>OutputStream</code> instance
* @param encoding Optional character encoding, default is UTF-8
* @return A <code>LSOutput</code> instance
* @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/">DOM Level 3 Load and Save Specification</a>
*/
public static LSOutput createLSOutput(DOMImplementationLS impl, OutputStream os, String encoding) {
LSOutput out = impl.createLSOutput();
if (os != null) {
out.setByteStream(os);
}
if (encoding != null) {
out.setEncoding(encoding);
}
return out;
}
/** Returns a <code>LSSerializer</code> instance.
* @param impl A <code>DOMImplementationLS</code> instance
* @param includeXmlDeclaration If set to <code>true</code>,
* the xml declaration will be included in the output
* @param enablePrettyPrint If set to <code>true</code>, the
* output will be formatted in human-readable form. If set to
* <code>false</code>, the entire document will consist of a single line.
* @return A <code>LSSerializer</code> instance
* @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/">DOM Level 3 Load and Save Specification</a>
*/
public static LSSerializer createLSSerializer(DOMImplementationLS impl, boolean includeXmlDeclaration, boolean enablePrettyPrint) {
LSSerializer writer = impl.createLSSerializer();
DOMConfiguration domConfig = writer.getDomConfig();
domConfig.setParameter("xml-declaration", includeXmlDeclaration);
domConfig.setParameter("format-pretty-print", enablePrettyPrint);
return writer;
}
/** Serializes a DOM Node to an <code>OutputStream</code> using DOM 3.
* @param os The <code>OutputStream</code> instance to write to
* @param node The DOM <code>Node</code> object to be serialized
* @param encoding Optional character encoding
* @param includeXmlDeclaration If set to <code>true</code>,
* the xml declaration will be included in the output
* @param enablePrettyPrint If set to <code>true</code>, the
* output will be formatted in human-readable form. If set to
* <code>false</code>, the entire document will consist of a single line.
* @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/">DOM Level 3 Load and Save Specification</a>
* @throws ClassCastException
* @throws ClassNotFoundException
* @throws InstantiationException
* @throws IllegalAccessException
*/
public static void writeXmlDocument(OutputStream os, Node node, String encoding, boolean includeXmlDeclaration, boolean enablePrettyPrint) throws ClassCastException, ClassNotFoundException, InstantiationException, IllegalAccessException {
DOMImplementationLS impl = getDomLsImplementation();
LSOutput out = createLSOutput(impl, os, encoding);
LSSerializer writer = createLSSerializer(impl, includeXmlDeclaration, enablePrettyPrint);
writer.write(node, out);
}
// ----- TrAX Methods ----------------- //
/** Creates a JAXP TrAX Transformer suitable for pretty-printing an
* XML document. This method is provided as an alternative to the
* deprecated <code>org.apache.xml.serialize.OutputFormat</code> class.
* @param encoding Optional encoding, defaults to UTF-8
* @param omitXmlDeclaration If <code>true</code> the xml declaration
* will be omitted from the output
* @param indent If <code>true</code>, the output will be indented
* @param indentAmount If <code>indent</code> is <code>true</code>,
* the number of spaces to indent. Default is 4.
* @return A <code>Transformer</code> instance
* @see <a href="http://java.sun.com/javase/6/docs/api/javax/xml/transform/package-summary.html">JAXP TrAX</a>
* @throws TransformerConfigurationException
*/
public static Transformer createOutputTransformer(String encoding, boolean omitXmlDeclaration, boolean indent, int indentAmount) throws TransformerConfigurationException {
// Developers: This stylesheet strips all formatting space characters from the XML,
// then indents the XML using the specified indentation.
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
sb.append("<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:xalan=\"http://xml.apache.org/xslt\" version=\"1.0\">\n");
sb.append("<xsl:output method=\"xml\" encoding=\"");
sb.append(encoding == null ? "UTF-8" : encoding);
sb.append("\"");
if (omitXmlDeclaration) {
sb.append(" omit-xml-declaration=\"yes\"");
}
sb.append(" indent=\"");
sb.append(indent ? "yes" : "no");
sb.append("\"");
if (indent) {
sb.append(" xalan:indent-amount=\"");
sb.append(indentAmount <= 0 ? 4 : indentAmount);
sb.append("\"");
}
sb.append("/>\n<xsl:strip-space elements=\"*\"/>\n");
sb.append("<xsl:template match=\"@*|node()\">\n");
sb.append("<xsl:copy><xsl:apply-templates select=\"@*|node()\"/></xsl:copy>\n");
sb.append("</xsl:template>\n</xsl:stylesheet>\n");
ByteArrayInputStream bis = new ByteArrayInputStream(sb.toString().getBytes());
TransformerFactory transformerFactory = TransformerFactory.newInstance();
return transformerFactory.newTransformer(new StreamSource(bis));
}
/** Serializes a DOM <code>Node</code> to an <code>OutputStream</code>
* using JAXP TrAX.
* @param transformer A <code>Transformer</code> instance
* @param node The <code>Node</code> to serialize
* @param os The <code>OutputStream</code> to serialize to
* @see <a href="http://java.sun.com/javase/6/docs/api/javax/xml/transform/package-summary.html">JAXP TrAX</a>
* @throws TransformerException
*/
public static void transformDomDocument(Transformer transformer, Node node, OutputStream os) throws TransformerException {
DOMSource source = new DOMSource(node);
StreamResult result = new StreamResult(os);
transformer.transform(source, result);
}
/** Serializes a DOM <code>Node</code> to an <code>OutputStream</code>
* using JAXP TrAX.
* @param node The <code>Node</code> to serialize
* @param os The <code>OutputStream</code> to serialize to
* @param encoding Optional encoding, defaults to UTF-8
* @param omitXmlDeclaration If <code>true</code> the xml declaration
* will be omitted from the output
* @param indent If <code>true</code>, the output will be indented
* @param indentAmount If <code>indent</code> is <code>true</code>,
* the number of spaces to indent. Default is 4.
* @see <a href="http://java.sun.com/javase/6/docs/api/javax/xml/transform/package-summary.html">JAXP TrAX</a>
* @throws TransformerException
*/
public static void writeXmlDocument(Node node, OutputStream os, String encoding, boolean omitXmlDeclaration, boolean indent, int indentAmount) throws TransformerException {
Transformer transformer = createOutputTransformer(encoding, omitXmlDeclaration, indent, indentAmount);
transformDomDocument(transformer, node, os);
}
// ----- Java Object Marshalling/Unmarshalling ----- //
/** Deserialize an object from an <code>InputStream</code>.
*
* @param input The <code>InputStream</code>
* @return The deserialized <code>Object</code>
*/
public static Object fromXml(InputStream input) {
return xstream.fromXML(input);
}
/** Deserialize an object from a <code>Reader</code>.
*
* @param reader The <code>Reader</code>
* @return The deserialized <code>Object</code>
*/
public static Object fromXml(Reader reader) {
return xstream.fromXML(reader);
}
/** Deserialize an object from a <code>String</code>.
*
* @param str The <code>String</code>
* @return The deserialized <code>Object</code>
*/
public static Object fromXml(String str) {
return xstream.fromXML(str);
}
/** Serialize an object to an XML <code>String</code>.
*
* @param obj The object to serialize
* @return An XML <code>String</code>
*/
public static String toXml(Object obj) {
return xstream.toXML(obj);
}
/** Serialize an object to an <code>OutputStream</code>.
*
* @param obj The object to serialize
* @param output The <code>OutputStream</code>
*/
public static void toXml(Object obj, OutputStream output) {
xstream.toXML(obj, output);
}
/** Serialize an object to a <code>Writer</code>.
*
* @param obj The object to serialize
* @param writer The <code>Writer</code>
*/
public static void toXml(Object obj, Writer writer) {
xstream.toXML(obj, writer);
}
// ------------------------------------------------- //
public static String writeXmlDocument(Node node) throws java.io.IOException {
if (node == null) {
Debug.logWarning("[UtilXml.writeXmlDocument] Node was null, doing nothing", module);
return null;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
writeXmlDocument(bos, node);
return bos.toString("UTF-8");
}
public static void writeXmlDocument(String filename, Node node) throws FileNotFoundException, IOException {
if (node == null) {
Debug.logWarning("[UtilXml.writeXmlDocument] Node was null, doing nothing", module);
return;
}
if (filename == null) {
Debug.logWarning("[UtilXml.writeXmlDocument] Filename was null, doing nothing", module);
return;
}
File outFile = new File(filename);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(outFile);
writeXmlDocument(fos, node);
} finally {
if (fos != null) {
fos.close();
}
}
}
public static void writeXmlDocument(OutputStream os, Node node) throws java.io.IOException {
if (node == null) {
Debug.logWarning("[UtilXml.writeXmlDocument] Node was null, doing nothing", module);
return;
}
// OutputFormat defaults are: indent on, indent = 4, include XML declaration,
// charset = UTF-8, line width = 72
try {
writeXmlDocument(node, os, "UTF-8", false, true, 4);
} catch (TransformerException e) {
// Wrapping this exception for backwards compatibility
throw new IOException(e.getMessage());
}
}
public static Document readXmlDocument(String content)
throws SAXException, ParserConfigurationException, java.io.IOException {
return readXmlDocument(content, true);
}
public static Document readXmlDocument(String content, boolean validate)
throws SAXException, ParserConfigurationException, java.io.IOException {
if (content == null) {
Debug.logWarning("[UtilXml.readXmlDocument] content was null, doing nothing", module);
return null;
}
ByteArrayInputStream bis = new ByteArrayInputStream(content.getBytes("UTF-8"));
return readXmlDocument(bis, validate, "Internal Content");
}
public static Document readXmlDocument(String content, boolean validate, boolean withPosition)
throws SAXException, ParserConfigurationException, java.io.IOException {
if (content == null) {
Debug.logWarning("[UtilXml.readXmlDocument] content was null, doing nothing", module);
return null;
}
ByteArrayInputStream bis = new ByteArrayInputStream(content.getBytes("UTF-8"));
return readXmlDocument(bis, validate, "Internal Content", withPosition);
}
public static Document readXmlDocument(URL url)
throws SAXException, ParserConfigurationException, java.io.IOException {
return readXmlDocument(url, true);
}
public static Document readXmlDocument(URL url, boolean validate)
throws SAXException, ParserConfigurationException, java.io.IOException {
if (url == null) {
Debug.logWarning("[UtilXml.readXmlDocument] URL was null, doing nothing", module);
return null;
}
InputStream is = url.openStream();
Document document = readXmlDocument(is, validate, url.toString());
is.close();
return document;
}
public static Document readXmlDocument(URL url, boolean validate, boolean withPosition)
throws SAXException, ParserConfigurationException, java.io.IOException {
if (url == null) {
Debug.logWarning("[UtilXml.readXmlDocument] URL was null, doing nothing", module);
return null;
}
InputStream is = url.openStream();
Document document = readXmlDocument(is, validate, url.toString(), withPosition);
is.close();
return document;
}
public static Document readXmlDocument(InputStream is, String docDescription)
throws SAXException, ParserConfigurationException, java.io.IOException {
return readXmlDocument(is, true, docDescription);
}
public static Document readXmlDocument(InputStream is, String docDescription, boolean withPosition)
throws SAXException, ParserConfigurationException, java.io.IOException {
return readXmlDocument(is, true, docDescription, withPosition);
}
public static Document readXmlDocument(InputStream is, boolean validate, String docDescription)
throws SAXException, ParserConfigurationException, java.io.IOException {
if (is == null) {
Debug.logWarning("[UtilXml.readXmlDocument] InputStream was null, doing nothing", module);
return null;
}
long startTime = System.currentTimeMillis();
Document document = null;
/* Standard JAXP (mostly), but doesn't seem to be doing XML Schema validation, so making sure that is on... */
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validate);
factory.setNamespaceAware(true);
factory.setAttribute("http://xml.org/sax/features/validation", validate);
factory.setAttribute("http://apache.org/xml/features/validation/schema", validate);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
// with a SchemaUrl, a URL object
DocumentBuilder builder = factory.newDocumentBuilder();
if (validate) {
LocalResolver lr = new LocalResolver(new DefaultHandler());
ErrorHandler eh = new LocalErrorHandler(docDescription, lr);
builder.setEntityResolver(lr);
builder.setErrorHandler(eh);
}
document = builder.parse(is);
double totalSeconds = (System.currentTimeMillis() - startTime)/1000.0;
if (Debug.verboseOn()) Debug.logVerbose("XML Read " + totalSeconds + "s: " + docDescription, module);
return document;
}
public static Document readXmlDocument(InputStream is, boolean validate, String docDescription, boolean withPosition)
throws SAXException, ParserConfigurationException, java.io.IOException {
if (!withPosition) {
return readXmlDocument(is, validate, docDescription);
}
if (is == null) {
Debug.logWarning("[UtilXml.readXmlDocument] InputStream was null, doing nothing", module);
return null;
}
long startTime = System.currentTimeMillis();
Document document = null;
DOMParser parser = new DOMParser() {
private XMLLocator locator;
private void setLineColumn(Node node) {
if (node.getUserData("startLine") != null) {
return;
}
node.setUserData("systemId",locator.getLiteralSystemId(), null);
node.setUserData("startLine",locator.getLineNumber(), null);
node.setUserData("startColumn",locator.getColumnNumber(), null);
}
private void setLineColumn() {
try {
Node node = (Node) getProperty("http://apache.org/xml/properties/dom/current-element-node");
if (node != null) {
setLineColumn(node);
}
} catch (SAXException ex) {
Debug.logWarning(ex, module);
}
}
private void setLastChildLineColumn() {
try {
Node node = (Node) getProperty("http://apache.org/xml/properties/dom/current-element-node");
if (node != null) {
setLineColumn(node.getLastChild());
}
} catch (SAXException ex) {
Debug.logWarning(ex, module);
}
}
@Override
public void startGeneralEntity(String name, XMLResourceIdentifier identifier, String encoding, Augmentations augs) throws XNIException {
super.startGeneralEntity(name, identifier, encoding, augs);
setLineColumn();
}
@Override
public void comment(XMLString text, Augmentations augs) throws XNIException {
super.comment(text, augs);
setLastChildLineColumn();
}
@Override
public void processingInstruction(String target, XMLString data, Augmentations augs) throws XNIException {
super.processingInstruction(target, data, augs);
setLastChildLineColumn();
}
@Override
public void startDocument(XMLLocator locator, String encoding, NamespaceContext namespaceContext, Augmentations augs) throws XNIException {
super.startDocument(locator, encoding, namespaceContext, augs);
this.locator = locator;
setLineColumn();
}
@Override
public void doctypeDecl(String rootElement, String publicId, String systemId, Augmentations augs) throws XNIException {
super.doctypeDecl(rootElement, publicId, systemId, augs);
}
@Override
public void startElement(QName elementQName, XMLAttributes attrList, Augmentations augs) throws XNIException {
super.startElement(elementQName, attrList, augs);
setLineColumn();
}
@Override
public void characters(XMLString text, Augmentations augs) throws XNIException {
super.characters(text, augs);
setLastChildLineColumn();
}
@Override
public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
super.ignorableWhitespace(text, augs);
setLastChildLineColumn();
}
};
parser.setFeature("http://xml.org/sax/features/namespaces", true);
parser.setFeature("http://xml.org/sax/features/validation", validate);
parser.setFeature("http://apache.org/xml/features/validation/schema", validate);
parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
// with a SchemaUrl, a URL object
if (validate) {
LocalResolver lr = new LocalResolver(new DefaultHandler());
ErrorHandler eh = new LocalErrorHandler(docDescription, lr);
parser.setEntityResolver(lr);
parser.setErrorHandler(eh);
}
InputSource inputSource = new InputSource(is);
inputSource.setSystemId(docDescription);
parser.parse(inputSource);
document = parser.getDocument();
double totalSeconds = (System.currentTimeMillis() - startTime)/1000.0;
if (Debug.verboseOn()) Debug.logVerbose("XML Read " + totalSeconds + "s: " + docDescription, module);
return document;
}
public static Document makeEmptyXmlDocument() {
return makeEmptyXmlDocument(null);
}
public static Document makeEmptyXmlDocument(String rootElementName) {
Document document = null;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(true);
try {
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.newDocument();
} catch (Exception e) {
Debug.logError(e, module);
}
if (document == null) return null;
if (rootElementName != null) {
Element rootElement = document.createElement(rootElementName);
document.appendChild(rootElement);
}
return document;
}
/** Creates a child element with the given name and appends it to the element child node list. */
public static Element addChildElement(Element element, String childElementName, Document document) {
Element newElement = document.createElement(childElementName);
element.appendChild(newElement);
return newElement;
}
/** Creates a child element with the given name and appends it to the element child node list.
* Also creates a Text node with the given value and appends it to the new elements child node list.
*/
public static Element addChildElementValue(Element element, String childElementName,
String childElementValue, Document document) {
Element newElement = addChildElement(element, childElementName, document);
newElement.appendChild(document.createTextNode(childElementValue));
return newElement;
}
/** Creates a child element with the given namespace supportive name and appends it to the element child node list. */
public static Element addChildElementNSElement(Element element, String childElementName,
Document document, String nameSpaceUrl) {
Element newElement = document.createElementNS(nameSpaceUrl, childElementName);
element.appendChild(newElement);
return element;
}
/** Creates a child element with the given namespace supportive name and appends it to the element child node list.
* Also creates a Text node with the given value and appends it to the new elements child node list.
*/
public static Element addChildElementNSValue(Element element, String childElementName,
String childElementValue, Document document, String nameSpaceUrl) {
Element newElement = document.createElementNS(nameSpaceUrl, childElementName);
newElement.appendChild(document.createTextNode(childElementValue));
element.appendChild(newElement);
return element;
}
/** Creates a child element with the given name and appends it to the element child node list.
* Also creates a CDATASection node with the given value and appends it to the new elements child node list.
*/
public static Element addChildElementCDATAValue(Element element, String childElementName,
String childElementValue, Document document) {
Element newElement = addChildElement(element, childElementName, document);
newElement.appendChild(document.createCDATASection(childElementValue));
return newElement;
}
/** Return a List of Element objects that are children of the given element */
public static List<? extends Element> childElementList(Element element) {
if (element == null) return null;
List<Element> elements = new LinkedList<Element>();
Node node = element.getFirstChild();
if (node != null) {
do {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) node;
elements.add(childElement);
}
} while ((node = node.getNextSibling()) != null);
}
return elements;
}
/** Return a List of Element objects that have the given name and are
* immediate children of the given element; if name is null, all child
* elements will be included. */
public static List<? extends Element> childElementList(Element element, String childElementName) {
if (element == null) return null;
List<Element> elements = new LinkedList<Element>();
Node node = element.getFirstChild();
if (node != null) {
do {
String nodeName = UtilXml.getNodeNameIgnorePrefix(node);
if (node.getNodeType() == Node.ELEMENT_NODE && (childElementName == null ||
childElementName.equals(nodeName))) {
Element childElement = (Element) node;
elements.add(childElement);
}
} while ((node = node.getNextSibling()) != null);
}
return elements;
}
/** Return a List of Element objects that have the given name and are
* immediate children of the given element; if name is null, all child
* elements will be included. */
public static List<? extends Element> childElementList(Element element, Set<String> childElementNames) {
if (element == null) return null;
List<Element> elements = new LinkedList<Element>();
if (childElementNames == null) return elements;
Node node = element.getFirstChild();
if (node != null) {
do {
if (node.getNodeType() == Node.ELEMENT_NODE && childElementNames.contains(node.getNodeName())) {
Element childElement = (Element) node;
elements.add(childElement);
}
} while ((node = node.getNextSibling()) != null);
}
return elements;
}
/** Return a List of Element objects that have the given name and are
* immediate children of the given element; if name is null, all child
* elements will be included. */
public static List<? extends Element> childElementList(Element element, String... childElementNames) {
return childElementList(element, UtilMisc.toSetArray(childElementNames));
}
/** Return a List of Element objects that are children of the given DocumentFragment */
public static List<? extends Element> childElementList(DocumentFragment fragment) {
if (fragment == null) return null;
List<Element> elements = new LinkedList<Element>();
Node node = fragment.getFirstChild();
if (node != null) {
do {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) node;
elements.add(childElement);
}
} while ((node = node.getNextSibling()) != null);
}
return elements;
}
/** Return a List of Node objects that have the given name and are immediate children of the given element;
* if name is null, all child elements will be included. */
public static List<? extends Node> childNodeList(Node node) {
if (node == null) return null;
List<Node> nodes = new LinkedList<Node>();
do {
if (node.getNodeType() == Node.ELEMENT_NODE || node.getNodeType() == Node.COMMENT_NODE) {
nodes.add(node);
}
} while ((node = node.getNextSibling()) != null);
return nodes;
}
/** Return the first child Element
* returns the first element. */
public static Element firstChildElement(Element element, Set<String> childElementNames) {
if (element == null) return null;
// get the first element with the given name
Node node = element.getFirstChild();
if (node != null) {
do {
if (node.getNodeType() == Node.ELEMENT_NODE && childElementNames.contains(node.getNodeName())) {
Element childElement = (Element) node;
return childElement;
}
} while ((node = node.getNextSibling()) != null);
}
return null;
}
/** Return the first child Element
* returns the first element. */
public static Element firstChildElement(Element element, String... childElementNames) {
return firstChildElement(element, UtilMisc.toSetArray(childElementNames));
}
/** Return the first child Element
* returns the first element. */
public static Element firstChildElement(Element element) {
if (element == null) return null;
// get the first element with the given name
Node node = element.getFirstChild();
if (node != null) {
do {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) node;
return childElement;
}
} while ((node = node.getNextSibling()) != null);
}
return null;
}
/** Return the first child Element with the given name; if name is null
* returns the first element. */
public static Element firstChildElement(Element element, String childElementName) {
if (element == null) return null;
if (UtilValidate.isEmpty(childElementName)) return null;
// get the first element with the given name
Node node = element.getFirstChild();
if (node != null) {
do {
String nodeName = node.getLocalName();
if (nodeName == null){
nodeName = UtilXml.getNodeNameIgnorePrefix(node);
}
if (node.getNodeType() == Node.ELEMENT_NODE && (childElementName == null ||
childElementName.equals(nodeName))) {
Element childElement = (Element) node;
return childElement;
}
} while ((node = node.getNextSibling()) != null);
}
return null;
}
/** Return the first child Element with the given name; if name is null
* returns the first element. */
public static Element firstChildElement(Element element, String childElementName, String attrName, String attrValue) {
if (element == null) return null;
// get the first element with the given name
Node node = element.getFirstChild();
if (node != null) {
do {
if (node.getNodeType() == Node.ELEMENT_NODE && (childElementName == null ||
childElementName.equals(node.getLocalName() != null ? node.getLocalName() : node.getNodeName()))) {
Element childElement = (Element) node;
String value = childElement.getAttribute(attrName);
if (value != null && value.equals(attrValue)) {
return childElement;
}
}
} while ((node = node.getNextSibling()) != null);
}
return null;
}
/** Return the text (node value) contained by the named child node. */
public static String childElementValue(Element element, String childElementName) {
if (element == null) return null;
// get the value of the first element with the given name
Element childElement = firstChildElement(element, childElementName);
return elementValue(childElement);
}
/** Return the text (node value) contained by the named child node or a default value if null. */
public static String childElementValue(Element element, String childElementName, String defaultValue) {
if (element == null) return defaultValue;
// get the value of the first element with the given name
Element childElement = firstChildElement(element, childElementName);
String elementValue = elementValue(childElement);
if (UtilValidate.isEmpty(elementValue))
return defaultValue;
else
return elementValue;
}
/** Return a named attribute of a named child node or a default if null. */
public static String childElementAttribute(Element element, String childElementName, String attributeName, String defaultValue) {
if (element == null) return defaultValue;
// get the value of the first element with the given name
Element childElement = firstChildElement(element, childElementName);
String elementAttribute = elementAttribute(childElement, attributeName, defaultValue);
if (UtilValidate.isEmpty(elementAttribute))
return defaultValue;
else
return elementAttribute;
}
/** Return the text (node value) of the first node under this, works best if normalized. */
public static String elementValue(Element element) {
if (element == null) return null;
// make sure we get all the text there...
element.normalize();
Node textNode = element.getFirstChild();
if (textNode == null) return null;
StringBuilder valueBuffer = new StringBuilder();
do {
if (textNode.getNodeType() == Node.CDATA_SECTION_NODE || textNode.getNodeType() == Node.TEXT_NODE) {
valueBuffer.append(textNode.getNodeValue());
}
} while ((textNode = textNode.getNextSibling()) != null);
return valueBuffer.toString();
}
/** Return the text (node value) of the first node under this */
public static String nodeValue(Node node) {
if (node == null) return null;
StringBuilder valueBuffer = new StringBuilder();
do {
if (node.getNodeType() == Node.CDATA_SECTION_NODE || node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.COMMENT_NODE) {
valueBuffer.append(node.getNodeValue());
}
} while ((node = node.getNextSibling()) != null);
return valueBuffer.toString();
}
public static String elementAttribute(Element element, String attrName, String defaultValue) {
if (element == null) return defaultValue;
String attrValue = element.getAttribute(attrName);
return UtilValidate.isNotEmpty(attrValue) ? attrValue : defaultValue;
}
public static String checkEmpty(String string) {
if (UtilValidate.isNotEmpty(string))
return string;
else
return "";
}
public static String checkEmpty(String string1, String string2) {
if (UtilValidate.isNotEmpty(string1))
return string1;
else if (UtilValidate.isNotEmpty(string2))
return string2;
else
return "";
}
public static String checkEmpty(String string1, String string2, String string3) {
if (UtilValidate.isNotEmpty(string1))
return string1;
else if (UtilValidate.isNotEmpty(string2))
return string2;
else if (UtilValidate.isNotEmpty(string3))
return string3;
else
return "";
}
public static boolean checkBoolean(String str) {
return checkBoolean(str, false);
}
public static boolean checkBoolean(String str, boolean defaultValue) {
if (defaultValue) {
//default to true, ie anything but false is true
return !"false".equals(str);
} else {
//default to false, ie anything but true is false
return "true".equals(str);
}
}
public static String nodeNameToJavaName(String nodeName, boolean capitalizeFirst) {
boolean capitalize = capitalizeFirst;
StringBuilder sb = new StringBuilder();
for (int index = 0; index < nodeName.length(); index++) {
char character = nodeName.charAt(index);
if ((sb.length() == 0 && !Character.isJavaIdentifierStart(character)) || (sb.length() != 0 && !Character.isJavaIdentifierPart(character))) {
capitalize = true;
continue;
}
if (sb.length() == 0 && !capitalizeFirst) {
sb.append(Character.toLowerCase(character));
} else {
if (capitalize) {
sb.append(Character.toUpperCase(character));
capitalize = false;
} else {
sb.append(character);
}
}
}
return sb.toString();
}
/**
* Local entity resolver to handle J2EE DTDs. With this a http connection
* to sun is not needed during deployment.
* Function boolean hadDTD() is here to avoid validation errors in
* descriptors that do not have a DOCTYPE declaration.
*/
public static class LocalResolver implements EntityResolver {
private boolean hasDTD = false;
private EntityResolver defaultResolver;
public LocalResolver(EntityResolver defaultResolver) {
this.defaultResolver = defaultResolver;
}
/**
* Returns DTD inputSource. If DTD was found in the dtds Map and inputSource was created
* flag hasDTD is set to true.
* @param publicId - Public ID of DTD
* @param systemId - System ID of DTD
* @return InputSource of DTD
*/
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
hasDTD = false;
String dtd = UtilProperties.getSplitPropertyValue(UtilURL.fromResource("localdtds.properties"), publicId);
if (UtilValidate.isNotEmpty(dtd)) {
if (Debug.verboseOn()) Debug.logVerbose("[UtilXml.LocalResolver.resolveEntity] resolving DTD with publicId [" + publicId +
"], systemId [" + systemId + "] and the dtd file is [" + dtd + "]", module);
try {
URL dtdURL = UtilURL.fromResource(dtd);
if (dtdURL == null) {
throw new GeneralException("Local DTD not found - " + dtd);
}
InputStream dtdStream = dtdURL.openStream();
InputSource inputSource = new InputSource(dtdStream);
inputSource.setPublicId(publicId);
hasDTD = true;
if (Debug.verboseOn()) Debug.logVerbose("[UtilXml.LocalResolver.resolveEntity] got LOCAL DTD input source with publicId [" +
publicId + "] and the dtd file is [" + dtd + "]", module);
return inputSource;
} catch (Exception e) {
Debug.logWarning(e, module);
}
} else {
// nothing found by the public ID, try looking at the systemId, or at least the filename part of it and look for that on the classpath
int lastSlash = systemId.lastIndexOf("/");
String filename = null;
if (lastSlash == -1) {
filename = systemId;
} else {
filename = systemId.substring(lastSlash + 1);
}
URL resourceUrl = UtilURL.fromResource(filename);
if (resourceUrl != null) {
InputStream resStream = resourceUrl.openStream();
InputSource inputSource = new InputSource(resStream);
if (UtilValidate.isNotEmpty(publicId)) {
inputSource.setPublicId(publicId);
}
hasDTD = true;
if (Debug.verboseOn()) Debug.logVerbose("[UtilXml.LocalResolver.resolveEntity] got LOCAL DTD/Schema input source with publicId [" +
publicId + "] and the file/resource is [" + filename + "]", module);
return inputSource;
} else {
Debug.logWarning("[UtilXml.LocalResolver.resolveEntity] could not find LOCAL DTD/Schema with publicId [" +
publicId + "] and the file/resource is [" + filename + "]", module);
return null;
}
}
return defaultResolver.resolveEntity(publicId, systemId);
}
/**
* Returns the boolean value to inform id DTD was found in the XML file or not
* @return boolean - true if DTD was found in XML
*/
public boolean hasDTD() {
return hasDTD;
}
}
/** Local error handler for entity resolver to DocumentBuilder parser.
* Error is printed to output just if DTD was detected in the XML file.
*/
public static class LocalErrorHandler implements ErrorHandler {
private String docDescription;
private LocalResolver localResolver;
public LocalErrorHandler(String docDescription, LocalResolver localResolver) {
this.docDescription = docDescription;
this.localResolver = localResolver;
}
public void error(SAXParseException exception) {
String exceptionMessage = exception.getMessage();
Pattern valueFlexExpr = Pattern.compile("value '\\$\\{.*\\}'");
Matcher matcher = valueFlexExpr.matcher(exceptionMessage.toLowerCase());
if (localResolver.hasDTD() && !matcher.find()) {
Debug.logError("XmlFileLoader: File "
+ docDescription
+ " process error. Line: "
+ String.valueOf(exception.getLineNumber())
+ ". Error message: "
+ exceptionMessage, module
);
}
}
public void fatalError(SAXParseException exception) {
if (localResolver.hasDTD()) {
Debug.logError("XmlFileLoader: File "
+ docDescription
+ " process fatal error. Line: "
+ String.valueOf(exception.getLineNumber())
+ ". Error message: "
+ exception.getMessage(), module
);
}
}
public void warning(SAXParseException exception) {
if (localResolver.hasDTD()) {
Debug.logError("XmlFileLoader: File "
+ docDescription
+ " process warning. Line: "
+ String.valueOf(exception.getLineNumber())
+ ". Error message: "
+ exception.getMessage(), module
);
}
}
}
/** This method is now useless
* Enhance rather the white list created by XStream::setupDefaultSecurity
* using xstream::allowTypesByWildcard with your own classes
*/
@Deprecated
private static class UnsupportedClassConverter implements Converter {
@Override
public boolean canConvert(@SuppressWarnings("rawtypes") Class arg0) {
if (java.lang.ProcessBuilder.class.equals(arg0)) {
return true;
}
return false;
}
@Override
public void marshal(Object arg0, HierarchicalStreamWriter arg1, MarshallingContext arg2) {
throw new UnsupportedOperationException();
}
@Override
public Object unmarshal(HierarchicalStreamReader arg0, UnmarshallingContext arg1) {
throw new UnsupportedOperationException();
}
}
/**
* get node name without any prefix
* @param node
* @return nodeName
*/
public static String getNodeNameIgnorePrefix(Node node){
if (node==null) return null;
String nodeName = node.getNodeName();
if (nodeName.contains(":")){
// remove any possible prefix
nodeName = nodeName.split(":")[1];
}
return nodeName;
}
/**
* get tag name without any prefix
* @param node
* @return tagName
*/
public static String getTagNameIgnorePrefix(Element element){
if (element==null) return null;
String tagName = element.getTagName();
if (tagName.contains(":")){
// remove any possible prefix
tagName = tagName.split(":")[1];
}
return tagName;
}
/**
* get attribute value ignoring prefix in attribute name
* @param node
* @return The value of the node, depending on its type; see the table Node class
*/
public static String getAttributeValueIgnorePrefix(Element element, String attributeName){
if (element==null) return "";
NamedNodeMap attributes = element.getAttributes();
if (attributes != null){
for (int i = 0, size = attributes.getLength(); i < size; i++)
{
Node node = attributes.item(i);
if (node.getNodeType() == Node.ATTRIBUTE_NODE){
String nodeName = UtilXml.getNodeNameIgnorePrefix(node);
if (nodeName.equals(attributeName)){
return node.getNodeValue();
}
}
}
}
return "";
}
}