/** * Copyright 2006 OCLC Online Computer Library Center 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 info.openurl.oom.util; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Source; 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.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * General purpose static methods for manipulating XML. * * @author Jeffrey A. Young */ public class XMLHelper { // private static HashMap builderMap = new HashMap(); private static DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); private static TransformerFactory tFactory = TransformerFactory.newInstance(); // private static HashMap transformerMap = new HashMap(); static { dbFactory.setNamespaceAware(true); } private static Element xmlnsEl; static { try { DOMImplementation impl = getThreadedDocumentBuilder().getDOMImplementation(); Document xmlnsDoc = impl.createDocument("uri:foo", "foo:xmlnsDoc", null); xmlnsEl = xmlnsDoc.getDocumentElement(); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:foo", "uri:foo"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ctx", "info:ofi/fmt:xml:xsd:ctx"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:oai", "http://www.openarchives.org/OAI/2.0/"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:oai_dc", "http://www.openarchives.org/OAI/2.0/oai_dc/"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:dc", "http://purl.org/dc/elements/1.1/"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:srw", "http://www.loc.gov/zing/srw/"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:mets", "http://www.loc.gov/METS/"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/TR/xlink"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:config", "info:sid/localhost:CollectionSimpleSchemas:config"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:purl", "info:sid/localhost:CollectionSimpleSchemas:purl"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:reg", "http://info-uri.info/registry"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:wr", "http://errol.oclc.org/oai:xmlregistry.oclc.org:errol/WikiRepository"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:pearsIdx", "http://www.oclc.org/pears/"); xmlnsEl.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:oomRef", "info:collections/oomImpls/oomRef"); } catch (ParserConfigurationException e) { e.printStackTrace(); } } /** * Get a thread-safe Transformer without an assigned transform. This is useful * for transforming a DOM Document into XML text. * @param omitXmlDeclaration * @return an "identity" Transformer assigned to the current thread * @throws TransformerConfigurationException */ public static Transformer getThreadedIdentityTransformer( boolean omitXmlDeclaration) throws TransformerConfigurationException { return getThreadedTransformer(omitXmlDeclaration, // transformerMap, null, (String) null); } /** * Get a thread-safe Transformer without an assigned transform. This is useful * for transforming a DOM Document into XML text. * @param omitXmlDeclaration * @param standalone * @return an Identity Transformer * @throws TransformerConfigurationException */ public static Transformer getThreadedIdentityTransformer( boolean omitXmlDeclaration, boolean standalone) throws TransformerConfigurationException { return getThreadedTransformer(omitXmlDeclaration, standalone, // transformerMap, null, (String) null); } private static Transformer getThreadedTransformer( boolean omitXmlDeclaration, boolean standalone, Map threadMap, String xslURL) throws TransformerConfigurationException { Thread currentThread = Thread.currentThread(); Transformer transformer = null; if (threadMap != null) { transformer = (Transformer) threadMap.get(currentThread); } if (transformer == null) { if (xslURL == null) { transformer = tFactory.newTransformer(); // "never null" } else { transformer = tFactory.newTransformer(new StreamSource(xslURL)); } transformer.setOutputProperty(OutputKeys.INDENT, "yes"); if (threadMap != null) { threadMap.put(currentThread, transformer); } } transformer.setOutputProperty(OutputKeys.STANDALONE, standalone?"yes":"no"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, omitXmlDeclaration?"yes":"no"); return transformer; } /** * Get a thread-safe Transformer. * @param omitXmlDeclaration * @param threadMap * @param xslURL * @return a thread-safe Transformer * @throws TransformerConfigurationException */ public static Transformer getThreadedTransformer(boolean omitXmlDeclaration, Map threadMap, String xslURL) throws TransformerConfigurationException { return getThreadedTransformer(omitXmlDeclaration, true, threadMap, xslURL); } /** * Get an Element with some handy xmlns attributes defined. * This comes in handy for calling XPathAPI methods. * @return an Element containing various xmlns attributes. */ public static Element getXmlnsEl() { return xmlnsEl; } /** * Grab the Document found at the specified URL. * * @param ref the URL location of an XML document. * @return a Document loaded from the specified URL. * @throws SAXException * @throws IOException * @throws ParserConfigurationException */ public static Document parse(String ref) throws SAXException, IOException, ParserConfigurationException { String protocol = ref.split(":", 2)[0]; return parse(protocol, new InputSource(ref)); } /** * Grab the Document found at the specified URL. * * @param protocol * @param is * @return a Document loaded from the specified URL. * @throws SAXException * @throws IOException * @throws ParserConfigurationException */ public static Document parse(String protocol, InputSource is) throws SAXException, IOException, ParserConfigurationException { if ("http".equals(protocol)) { return getThreadedDocumentBuilder().parse(is); } throw new IOException("Protocol handler not implemented yet: " + protocol); } /** * Grab the Document loaded from the specified InputSource. * * @param is * @return a Document loaded from the specified InputSource * @throws SAXException * @throws IOException * @throws ParserConfigurationException */ public static Document parse(InputSource is) throws SAXException, IOException, ParserConfigurationException { return getThreadedDocumentBuilder().parse(is); } /** * Grab the Document loaded from the specified InputStream * * @param is * @return a Document loaded from the specified InputStream. * @throws SAXException * @throws IOException * @throws ParserConfigurationException */ public static Document parse(InputStream is) throws SAXException, IOException, ParserConfigurationException { return getThreadedDocumentBuilder().parse(is); } /** * Get a thread-safe DocumentBuilder * @return a namespaceAware DocumentBuilder assigned to the current thread * @throws ParserConfigurationException */ public static DocumentBuilder getThreadedDocumentBuilder() throws ParserConfigurationException { // Thread currentThread = Thread.currentThread(); // DocumentBuilder builder = // (DocumentBuilder) builderMap.get(currentThread); // if (builder == null) { // builder = dbFactory.newDocumentBuilder(); // builderMap.put(currentThread, builder); // } DocumentBuilder builder = dbFactory.newDocumentBuilder(); return builder; } /** * XML-encode a String * @param value the String to be XML-encoded * @return the XML-encoded String */ public static String encode(String value) { StringBuffer sb = new StringBuffer(); for (int i=0; i<value.length(); ++i) { char c = value.charAt(i); switch (c) { case '&': sb.append("&"); break; case '<': sb.append("<"); break; case '>': sb.append(">"); break; case '\'': sb.append("'"); break; case '"': sb.append("""); break; default: sb.append(c); } } return sb.toString(); } /** * Transform a DOM Node into an XML String. * @param node * @return an XML String representation of the specified Node * @throws TransformerException */ public static String toString(Node node) throws TransformerException { return toString(node, true); } /** * Transform a DOM Node into an XML String * @param node * @param omitXMLDeclaration * @return an XML String representation of the specified Node * @throws TransformerException */ public static String toString(Node node, boolean omitXMLDeclaration) throws TransformerException { StringWriter writer = new StringWriter(); Transformer transformer = getThreadedIdentityTransformer(omitXMLDeclaration); Source source = new DOMSource(node); Result result = new StreamResult(writer); transformer.transform(source, result); return writer.toString(); } /** * Remove named nodes of the specified nodeType from the * specified node. * @param node the node to be cleaned. * @param nodeType the type of nodes to be removed. * @param name the name of nodes to be removed. */ public static void removeAll(Node node, short nodeType, String name) { if (node.getNodeType() == nodeType && (name == null || node.getNodeName().equals(name))) { node.getParentNode().removeChild(node); } else { // Visit the children NodeList list = node.getChildNodes(); for (int i=0; i<list.getLength(); i++) { removeAll(list.item(i), nodeType, name); } } } }