/* * Copyright 2017 OmniFaces * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package org.omnifaces.util; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * <p> * Collection of utility methods for the JAXP API in general. * * @author Bauke Scholtz * @since 2.1 */ public final class Xml { // Constructors --------------------------------------------------------------------------------------------------- private Xml() { // Hide constructor. } // Create --------------------------------------------------------------------------------------------------------- /** * Creates a single XML {@link Document} based on given URLs representing XML documents. All those XML documents * are merged into a single root element named <code>root</code>. * @param urls The URLs representing XML documents. * @return A single XML document containing all given XML documents. * @throws IOException When an I/O error occurs. * @throws SAXException When a XML parsing error occurs. */ public static Document createDocument(List<URL> urls) throws IOException, SAXException { DocumentBuilder builder = createDocumentBuilder(); Document document = builder.newDocument(); document.appendChild(document.createElement("root")); parseAndAppendChildren(builder, document, urls); return document; } /** * Creates an instance of {@link DocumentBuilder} which doesn't validate, nor is namespace aware nor expands entity * references (to keep it as lenient as possible). * @return A lenient instance of {@link DocumentBuilder}. */ public static DocumentBuilder createDocumentBuilder() { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); factory.setNamespaceAware(false); factory.setExpandEntityReferences(false); try { return factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { throw new UnsupportedOperationException(e); } } // Manipulate ----------------------------------------------------------------------------------------------------- /** * Parse the given URLs as a document using the given builder and then append all its child nodes to the given * document. * @param builder The document builder. * @param document The document. * @param urls The URLs representing XML documents. * @throws IOException When an I/O error occurs. * @throws SAXException When a XML parsing error occurs. */ public static void parseAndAppendChildren(DocumentBuilder builder, Document document, List<URL> urls) throws IOException, SAXException { for (URL url : urls) { if (url == null) { continue; } URLConnection connection = url.openConnection(); connection.setUseCaches(false); try (InputStream input = connection.getInputStream()) { NodeList children = builder.parse(input).getDocumentElement().getChildNodes(); for (int i = 0; i < children.getLength(); i++) { document.getDocumentElement().appendChild(document.importNode(children.item(i), true)); } } } } // Traverse ------------------------------------------------------------------------------------------------------- /** * Convenience method to return a node list matching given XPath expression. * @param node The node to return node list matching given XPath expression for. * @param xpath The XPath instance. * @param expression The XPath expression to match node list. * @return A node list matching given XPath expression * @throws XPathExpressionException When the XPath expression contains a syntax error. */ public static NodeList getNodeList(Node node, XPath xpath, String expression) throws XPathExpressionException { return (NodeList) xpath.compile(expression).evaluate(node, XPathConstants.NODESET); } /** * Convenience method to return trimmed text content of given node. This uses * <code>getFirstChild().getNodeValue()</code> instead of <code>getTextContent()</code> to workaround some buggy * JAXP implementations. * @param node The node to return text content for. * @return Trimmed text content of given node. */ public static String getTextContent(Node node) { return node.getFirstChild().getNodeValue().trim(); } }