/** * Copyright (C) 2016 Orbeon, Inc. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software Foundation; either version * 2.1 of the License, or (at your option) any later version. * * 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 Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.transformer.xupdate.dom4j; import org.apache.commons.io.output.StringBuilderWriter; import org.orbeon.dom4j.*; import org.orbeon.dom4j.io.OutputFormat; import org.orbeon.dom4j.io.XMLWriter; import org.orbeon.oxf.common.OXFException; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Collection of utility routines for working with DOM4J. In particular offers many methods found in DocumentHelper. * The difference between these 'copied' methods and the originals is that our copies use our NonLazyUserData* classes. * (As opposed to DOM4J's defaults or whatever happens to be specified in DOM4J's system property.) */ public class Dom4jUtils { private static final String XML_PREFIX = "xml"; private static final String XML_URI = "http://www.w3.org/XML/1998/namespace"; /** * Return a Map of namespaces in scope on the given element. */ public static Map<String, String> getNamespaceContext(Element element) { final Map<String, String> namespaces = new HashMap<String, String>(); for (Element currentNode = element; currentNode != null; currentNode = currentNode.getParent()) { final List currentNamespaces = currentNode.declaredNamespaces(); for (Iterator j = currentNamespaces.iterator(); j.hasNext();) { final Namespace namespace = (Namespace) j.next(); if (!namespaces.containsKey(namespace.getPrefix())) { namespaces.put(namespace.getPrefix(), namespace.getURI()); // TODO: Intern namespace strings to save memory; should use NamePool later // namespaces.put(namespace.getPrefix().intern(), namespace.getURI().intern()); } } } // It seems that by default this may not be declared. However, it should be: "The prefix xml is by definition // bound to the namespace name http://www.w3.org/XML/1998/namespace. It MAY, but need not, be declared, and MUST // NOT be bound to any other namespace name. Other prefixes MUST NOT be bound to this namespace name, and it // MUST NOT be declared as the default namespace." namespaces.put(XML_PREFIX, XML_URI); return namespaces; } public static XPath createXPath(final String expression) throws InvalidXPathException { final DocumentFactory factory = NonLazyUserDataDocumentFactory.getInstance(); return factory.createXPath(expression); } public static Text createText(final String text) { final DocumentFactory factory = NonLazyUserDataDocumentFactory.getInstance(); return factory.createText(text); } public static Attribute createAttribute(final QName qName, final String value) { final DocumentFactory factory = NonLazyUserDataDocumentFactory.getInstance(); return factory.createAttribute(null, qName, value); } public static Namespace createNamespace(final String prefix, final String uri) { final DocumentFactory factory = NonLazyUserDataDocumentFactory.getInstance(); return factory.createNamespace(prefix, uri); } private static String domToString(final Branch branch) { final OutputFormat format = new OutputFormat(); format.setIndent(false); format.setNewlines(false); return domToString(branch, format); } private static String domToString(final Node node, final OutputFormat format) { try { final StringBuilderWriter writer = new StringBuilderWriter(); // Ugh, XMLWriter doesn't accept null formatter _and_ default formatter is protected. final XMLWriter xmlWriter = format == null ? new XMLWriter(writer) : new XMLWriter(writer, format); xmlWriter.write(node); xmlWriter.close(); return writer.toString(); } catch (final IOException e) { throw new OXFException(e); } } /** * Convert a dom4j node to a string. * * @param node node to convert * @return resulting string */ public static String nodeToString(final Node node) { final String ret; switch (node.getNodeType()) { case Node.DOCUMENT_NODE: { ret = domToString(((Document) node).getRootElement()); break; } case Node.ELEMENT_NODE: { ret = domToString((Branch) node); break; } case Node.TEXT_NODE: { ret = node.getText(); break; } default : ret = domToString(node, null); break; } return ret; } /** * Removes the elements and text inside the given element, but not the attributes or namespace * declarations on the element. */ public static void clearElementContent(final Element elt) { final java.util.List cntnt = elt.content(); for (final java.util.ListIterator j = cntnt.listIterator(); j.hasNext();) { final Node chld = (Node) j.next(); if (chld.getNodeType() == Node.TEXT_NODE || chld.getNodeType() == Node.ELEMENT_NODE) { j.remove(); } } } }