/* * * Copyright (c) 2013 - 2017 Lijun Liao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT * OF THIRD PARTY RIGHTS. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License. * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial activities involving the XiPKI software without * disclosing the source code of your own applications. * * For more information, please contact Lijun Liao at this * address: lijun.liao@gmail.com */ package org.xipki.commons.common.util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TimeZone; import javax.xml.bind.JAXBException; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathExpressionException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * @author Lijun Liao * @since 2.0.0 */ public class XmlUtil { static final TimeZone UTC = TimeZone.getTimeZone("UTC"); private static Document document; private static DocumentBuilder builder; static { try { builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); } catch (ParserConfigurationException ex) { System.err.println("could not initialize the XMLDocumentBuilder. Message: " + ex.getMessage()); System.err.println("could not initialize the XMLDocumentBuilder" + ex.getMessage()); } if (builder != null) { document = builder.newDocument(); } } private XmlUtil() { } public static Element createElement(final String namespace, final String localPart, final String value) { if (document == null) { throw new RuntimeException("XMLDocumentBuilder must not be initialized"); } ParamUtil.requireNonBlank("localPart", localPart); Element element = document.createElementNS(namespace, "ns:" + localPart); if (StringUtil.isNotBlank(value)) { element.appendChild(document.createTextNode(value)); } return element; } public static Element getDocumentElment(final byte[] xmlFragement) throws IOException, SAXException { ParamUtil.requireNonNull("xmlFragement", xmlFragement); Document doc = builder.parse(new ByteArrayInputStream(xmlFragement)); return doc.getDocumentElement(); } public static Calendar getCalendar(final Date dateAndTime) { if (null == dateAndTime) { return null; } Calendar cal = (Calendar) Calendar.getInstance(UTC).clone(); cal.setTime(dateAndTime); return cal; } public static XMLGregorianCalendar currentXmlDate() { return getXmlDate(new Date()); } public static XMLGregorianCalendar getXmlDate(final Calendar calendar) { ParamUtil.requireNonNull("calendar", calendar); GregorianCalendar cal; if (calendar instanceof GregorianCalendar) { cal = (GregorianCalendar) calendar; } else { cal = new GregorianCalendar(); cal.setTimeZone(UTC); cal.setTime(calendar.getTime()); } try { XMLGregorianCalendar ret = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal); ret.setMillisecond(DatatypeConstants.FIELD_UNDEFINED); return ret; } catch (DatatypeConfigurationException ex) { return null; } } public static XMLGregorianCalendar getXmlDate(final Date dateAndTime) { ParamUtil.requireNonNull("dateAndTime", dateAndTime); GregorianCalendar cal = new GregorianCalendar(); cal.setTimeZone(UTC); cal.setTime(dateAndTime); try { XMLGregorianCalendar ret = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal); ret.setMillisecond(DatatypeConstants.FIELD_UNDEFINED); return ret; } catch (DatatypeConfigurationException ex) { return null; } } public static String getValueOfFirstElementChild(final Element element, final String namespace, final String localname) { Node node = getFirstElementChild(element, namespace, localname); return (node == null) ? null : getNodeValue(node); } public static String getNodeValue(final Node node) { ParamUtil.requireNonNull("node", node); if (node.getNodeType() == Node.ELEMENT_NODE) { Node cn = node.getFirstChild(); if (cn != null) { do { if (cn.getNodeType() == Node.TEXT_NODE) { return cn.getNodeValue(); } cn = cn.getNextSibling(); } while (cn != null); } } return node.getNodeValue(); } public static Element getFirstElementChild(final Element element, final String namespace, final String localname) { ParamUtil.requireNonNull("element", element); ParamUtil.requireNonBlank("localname", localname); Node node = element.getFirstChild(); if (node == null) { return null; } do { if (match(node, namespace, localname)) { return (Element) node; } node = node.getNextSibling(); } while (node != null); return null; } /** * * @param element context element. * @param namespace namespace of the expected element. Set it to {@code null} if namespace * will not be evaluated. * @param localname localname of the expected element. * @return List of the expected children element. If no match children could be found, empty * list will be returned. */ public static List<Element> getElementChilden(final Element element, final String namespace, final String localname) { ParamUtil.requireNonNull("element", element); ParamUtil.requireNonBlank("localname", localname); List<Element> rv = new LinkedList<Element>(); NodeList children = element.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if (match(child, namespace, localname)) { rv.add((Element) child); } } return rv; } public static List<Element> getAllElementsWithAttrId(final Element element, final String namespace) { ParamUtil.requireNonNull("element", element); List<Element> list = new LinkedList<Element>(); if (elementHasId(element, namespace)) { list.add(element); } NodeList children = element.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if (!(child instanceof Element)) { continue; } addAllElementsWithAttrId(list, (Element) child, namespace); } return list; } private static void addAllElementsWithAttrId(final List<Element> list, final Element element, final String namespace) { if (elementHasId(element, namespace)) { list.add(element); } NodeList children = element.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if (!(child instanceof Element)) { continue; } Element childElement = (Element) child; if (elementHasId(childElement, namespace)) { list.add(childElement); } addAllElementsWithAttrId(list, childElement, namespace); } } private static boolean elementHasId(final Element element, final String namespace) { return element.hasAttributeNS(namespace, "Id"); } private static boolean match(final Node node, final String namespace, final String localname) { if (node instanceof Element) { Element element = (Element) node; String ln = element.getLocalName(); if (ln == null) { ln = element.getTagName(); } if (ln.equals(localname)) { if (namespace == null || namespace.equals(element.getNamespaceURI())) { return true; } } } return false; } public static String getValueOfFirstMatch(final Element contextNode, final String relativeXpath, final Map<String, String> nsPrefixUriMap) { Node node = getFirstMatch(contextNode, relativeXpath, nsPrefixUriMap); return (node == null) ? null : getNodeValue(node); } public static Node getFirstMatch(final Element contextNode, final String relativeXPath, final Map<String, String> nsPrefixUriMap) { List<Node> nodes = getMatch(contextNode, relativeXPath, nsPrefixUriMap, true); return CollectionUtil.isEmpty(nodes) ? null : nodes.get(0); } public static List<Node> getMatch(final Element contextNode, final String relativeXPath, final Map<String, String> nsPrefixUriMap) { return getMatch(contextNode, relativeXPath, nsPrefixUriMap, false); } private static List<Node> getMatch(final Element contextNode, final String relativeXpath, final Map<String, String> nsPrefixUriMap, final boolean onlyFirstMatch) { try { SimpleXpath simpleXpath = new SimpleXpath(relativeXpath, nsPrefixUriMap); if (onlyFirstMatch) { Node node = simpleXpath.selectFirstMatch(contextNode); return (node == null) ? Collections.emptyList() : Arrays.asList(node); } else { return simpleXpath.select(contextNode); } } catch (XPathExpressionException ex) { System.err.println("invalid xpath {}" + relativeXpath); return Collections.emptyList(); } } public static List<Element> getElementMatch(final Element contextNode, final String relativeXpath, final Map<String, String> nsPrefixUriMap) { List<Node> nodes = getMatch(contextNode, relativeXpath, nsPrefixUriMap, false); List<Element> elements = new ArrayList<Element>(nodes.size()); for (Node node : nodes) { if (node instanceof Element) { elements.add((Element) node); } } return elements; } public static String getMessage(final JAXBException ex) { ParamUtil.requireNonNull("ex", ex); String ret = ex.getMessage(); if (ret == null && ex.getLinkedException() != null) { ret = ex.getLinkedException().getMessage(); } return ret; } public static JAXBException convert(final JAXBException ex) { ParamUtil.requireNonNull("ex", ex); return new JAXBException(getMessage(ex), ex.getLinkedException()); } }