001/** 002The contents of this file are subject to the Mozilla Public License Version 1.1 003(the "License"); you may not use this file except in compliance with the License. 004You may obtain a copy of the License at http://www.mozilla.org/MPL/ 005Software distributed under the License is distributed on an "AS IS" basis, 006WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 007specific language governing rights and limitations under the License. 008 009The Original Code is "XMLUtils.java". Description: 010"XML utilities" 011 012The Initial Developer of the Original Code is University Health Network. Copyright (C) 0132001. All Rights Reserved. 014 015Contributor(s): ______________________________________. 016 017Alternatively, the contents of this file may be used under the terms of the 018GNU General Public License (the "GPL"), in which case the provisions of the GPL are 019applicable instead of those above. If you wish to allow use of your version of this 020file only under the terms of the GPL and not to allow others to use your version 021of this file under the MPL, indicate your decision by deleting the provisions above 022and replace them with the notice and other provisions required by the GPL License. 023If you do not delete the provisions above, a recipient may use your version of 024this file under either the MPL or the GPL. 025 */ 026package ca.uhn.hl7v2.util; 027 028import java.io.*; 029 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032import org.w3c.dom.DOMConfiguration; 033import org.w3c.dom.DOMErrorHandler; 034import org.w3c.dom.DOMImplementation; 035import org.w3c.dom.Document; 036import org.w3c.dom.bootstrap.DOMImplementationRegistry; 037import org.w3c.dom.ls.DOMImplementationLS; 038import org.w3c.dom.ls.LSInput; 039import org.w3c.dom.ls.LSOutput; 040import org.w3c.dom.ls.LSResourceResolver; 041import org.w3c.dom.ls.LSSerializer; 042import org.xml.sax.InputSource; 043import org.xml.sax.SAXException; 044 045import javax.xml.parsers.DocumentBuilder; 046import javax.xml.parsers.DocumentBuilderFactory; 047import javax.xml.parsers.ParserConfigurationException; 048 049public class XMLUtils { 050 private static final Logger ourLog = LoggerFactory.getLogger(XMLUtils.class); 051 052 private static DOMImplementation IMPL; 053 054 @SuppressWarnings("unchecked") 055 public synchronized static <T> T getDOMImpl() { 056 if (IMPL == null) { 057 try { 058 DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); 059 IMPL = registry.getDOMImplementation("LS 3.0"); 060 } catch (Exception e) { 061 throw new RuntimeException(e); 062 } 063 } 064 return (T) IMPL; 065 } 066 067 @SuppressWarnings("unchecked") 068 public static Document parse(String s) { 069 return parseDocument(new InputSource(new StringReader(s)), false); 070 } 071 072 public static Document parse(String s, boolean validateIfSchema) { 073 return parseDocument(new InputSource(new StringReader(s)), validateIfSchema); 074 } 075 076 public static Document parse(InputStream s, boolean validateIfSchema) { 077 return parseDocument(new InputSource(s), validateIfSchema); 078 } 079 080 public static Document parseDocument(InputSource theInputSource, boolean theValidating) { 081 DocumentBuilder builder; 082 try { 083 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); 084 docBuilderFactory.setNamespaceAware(true); 085 docBuilderFactory.setXIncludeAware(false); 086 docBuilderFactory.setExpandEntityReferences(false); 087 docBuilderFactory.setValidating(theValidating); 088 try { 089 docBuilderFactory.setFeature( 090 "http://apache.org/xml/features/disallow-doctype-decl", false); 091 docBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); 092 docBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false); 093 docBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); 094 docBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 095 } catch (Exception e) { 096 ourLog.warn("Failed to set feature on XML parser: " + e.toString()); 097 } 098 099 builder = docBuilderFactory.newDocumentBuilder(); 100 } catch (ParserConfigurationException e) { 101 throw new RuntimeException(e); 102 } 103 104 try { 105 return builder.parse(theInputSource); 106 } catch (SAXException | IOException e) { 107 throw new RuntimeException(e); 108 } 109 } 110 111 112 public static void validate(Document d, String schema, DOMErrorHandler handler) { 113 DOMConfiguration config = d.getDomConfig(); 114 config.setParameter("schema-type", "http://www.w3.org/2001/XMLSchema"); 115 config.setParameter("validate", true); 116 config.setParameter("schema-location", schema); 117 config.setParameter("resource-resolver", new ClasspathResourceResolver()); 118 config.setParameter("error-handler", handler); 119 d.normalizeDocument(); 120 } 121 122 public static String serialize(Document document, boolean prettyPrint) { 123 DOMImplementationLS impl = getDOMImpl(); 124 LSSerializer serializer = impl.createLSSerializer(); 125 // document.normalizeDocument(); 126 DOMConfiguration config = serializer.getDomConfig(); 127 if (prettyPrint && config.canSetParameter("format-pretty-print", Boolean.TRUE)) { 128 config.setParameter("format-pretty-print", true); 129 } 130 config.setParameter("xml-declaration", true); 131 LSOutput output = impl.createLSOutput(); 132 output.setEncoding("UTF-8"); 133 Writer writer = new StringWriter(); 134 output.setCharacterStream(writer); 135 serializer.write(document, output); 136 return writer.toString(); 137 } 138 139 public static Document emptyDocument(String title) { 140 DOMImplementation impl = getDOMImpl(); 141 return impl.createDocument("urn:hl7-org:v2xml", title, null); 142 } 143 144 /** 145 * This is an implementation of LSResourceResolver that can resolve XML schemas from the 146 * classpath 147 */ 148 private static class ClasspathResourceResolver implements LSResourceResolver { 149 private final DOMImplementationLS impl; 150 151 ClasspathResourceResolver() { 152 impl = getDOMImpl(); 153 } 154 155 public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, 156 String baseURI) { 157 LSInput lsInput = impl.createLSInput(); 158 InputStream is = getClass().getResourceAsStream("/" + systemId); 159 if (is == null) 160 return null; 161 lsInput.setByteStream(is); 162 return lsInput; 163 } 164 } 165 166}