2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.mso.bpmn.core.xml;
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.io.Reader;
28 import java.io.StringReader;
29 import java.io.StringWriter;
30 import java.util.HashMap;
33 import javax.xml.parsers.DocumentBuilder;
34 import javax.xml.parsers.DocumentBuilderFactory;
35 import javax.xml.parsers.ParserConfigurationException;
36 import javax.xml.transform.OutputKeys;
37 import javax.xml.transform.Source;
38 import javax.xml.transform.Transformer;
39 import javax.xml.transform.TransformerException;
40 import javax.xml.transform.TransformerFactory;
41 import javax.xml.transform.dom.DOMSource;
42 import javax.xml.transform.stream.StreamResult;
43 import javax.xml.transform.stream.StreamSource;
44 import javax.xml.xpath.XPath;
45 import javax.xml.xpath.XPathConstants;
46 import javax.xml.xpath.XPathExpressionException;
47 import javax.xml.xpath.XPathFactory;
49 import org.w3c.dom.Document;
50 import org.w3c.dom.Node;
51 import org.w3c.dom.NodeList;
52 import org.xml.sax.InputSource;
53 import org.xml.sax.SAXException;
56 * XML transformation methods and other useful functions.
58 public final class XmlTool {
60 private static final Map<String, Integer> ENTITIES = new HashMap<String, Integer>();
63 ENTITIES.put("amp", new Integer(38));
64 ENTITIES.put("quot", new Integer(34));
65 ENTITIES.put("lt", new Integer(60));
66 ENTITIES.put("gt", new Integer(62));
70 * Normalizes and formats XML. This method consolidates and moves all namespace
71 * declarations to the root element. The result will not have an XML prolog or
73 * @param xml the XML to normalize
75 * @throws TransformerException
76 * @throws ParserConfigurationException
77 * @throws SAXException
78 * @throws XPathExpressionException
80 public static String normalize(Object xml) throws IOException, TransformerException,
81 ParserConfigurationException, SAXException, XPathExpressionException {
87 Source xsltSource = new StreamSource(new StringReader(
88 readResourceFile("normalize-namespaces.xsl")));
90 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
91 dbFactory.setNamespaceAware(true);
92 DocumentBuilder db = dbFactory.newDocumentBuilder();
93 InputSource source = new InputSource(new StringReader(String.valueOf(xml)));
94 Document doc = db.parse(source);
96 // Start of code to remove whitespace outside of tags
97 XPath xPath = XPathFactory.newInstance().newXPath();
98 NodeList nodeList = (NodeList) xPath.evaluate(
99 "//text()[normalize-space()='']", doc, XPathConstants.NODESET);
101 for (int i = 0; i < nodeList.getLength(); ++i) {
102 Node node = nodeList.item(i);
103 node.getParentNode().removeChild(node);
105 // End of code to remove whitespace outside of tags
107 // the factory pattern supports different XSLT processors
108 TransformerFactory transformerFactory = TransformerFactory.newInstance();
109 Transformer transformer = transformerFactory.newTransformer(xsltSource);
111 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
112 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
113 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
114 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
116 StringWriter writer = new StringWriter();
117 transformer.transform(new DOMSource(doc), new StreamResult(writer));
118 return writer.toString().trim();
122 * Encodes a value so it can be used inside an XML text element.
123 * @param s the string to encode
124 * @return the encoded string
126 public static String encode(Object value) {
131 String s = String.valueOf(value);
132 StringBuilder out = new StringBuilder();
133 boolean modified = false;
135 for (int i = 0; i < s.length(); i++) {
136 char c = s.charAt(i);
141 } else if (c == '>') {
144 } else if (c == '&') {
147 } else if (c < 32 || c > 126) {
148 out.append("&#" + (int)c + ";");
156 return out.toString();
163 * Encodes a value so it can be used inside an XML attribute.
164 * @param s the string to encode
165 * @return the encoded string
167 public static String encodeAttr(Object value) {
172 String s = String.valueOf(value);
173 StringBuilder out = new StringBuilder();
174 boolean modified = false;
176 for (int i = 0; i < s.length(); i++) {
177 char c = s.charAt(i);
182 } else if (c == '>') {
185 } else if (c == '"') {
186 out.append(""");
188 } else if (c == '&') {
191 } else if (c < 32 || c > 126) {
192 out.append("&#" + (int)c + ";");
200 return out.toString();
207 * Decodes XML entities in a string value
208 * @param value a value with embedded XML entities
209 * @return the decoded string
211 public static String decode(Object value) {
216 String s = String.valueOf(value);
218 StringBuilder out = new StringBuilder(s.length());
219 int ampIndex = s.indexOf("&");
222 while (ampIndex >= 0) {
223 int nextAmpIndex = s.indexOf("&", ampIndex + 1);
224 int nextSemiIndex = s.indexOf(";", ampIndex + 1);
225 if (nextSemiIndex != -1 && (nextAmpIndex == -1 || nextSemiIndex < nextAmpIndex)) {
227 String entity = s.substring(ampIndex + 1, nextSemiIndex);
230 if (entity.startsWith("#")) {
231 code = Integer.parseInt(entity.substring(1), 10);
233 if (ENTITIES.containsKey(entity)) {
234 code = ENTITIES.get(entity);
237 } catch (NumberFormatException x) {
241 out.append(s.substring(lastEnd, ampIndex));
242 lastEnd = nextSemiIndex + 1;
243 if (code >= 0 && code <= 0xffff) {
244 out.append((char) code);
252 ampIndex = nextAmpIndex;
255 out.append(s.substring(lastEnd));
256 return out.toString();
260 * Removes the preamble, if present, from an XML document.
261 * @param xml the XML document
262 * @return a possibly modified document
264 public static String removePreamble(Object xml) {
269 return String.valueOf(xml).replaceAll("(<\\?[^<]*\\?>\\s*[\\r\\n]*)?", "");
273 * Removes namespaces and namespace declarations from an XML document.
274 * @param xml the XML document
275 * @return a possibly modified document
277 public static String removeNamespaces(Object xml) {
282 String text = String.valueOf(xml);
284 // remove xmlns declaration
285 text = text.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
286 // remove opening tag prefix
287 text = text.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
288 // remove closing tags prefix
289 text = text.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
290 // remove extra spaces left when xmlns declarations are removed
291 text = text.replaceAll("\\s+>", ">");
298 * Reads the specified resource file and return the contents as a string.
299 * @param file Name of the resource file
300 * @return the contents of the resource file as a String
301 * @throws IOException if there is a problem reading the file
303 private static String readResourceFile(String file) throws IOException {
304 InputStream stream = null;
306 stream = XmlTool.class.getClassLoader().getResourceAsStream(file);
308 if (stream == null) {
309 throw new FileNotFoundException("No such resource file: " + file);
312 Reader reader = new InputStreamReader(stream, "UTF-8");
313 StringBuilder out = new StringBuilder();
314 char[] buf = new char[1024];
317 while ((n = reader.read(buf)) >= 0) {
318 out.append(buf, 0, n);
323 return out.toString();
325 if (stream != null) {
328 } catch (Exception e) {
336 * Instantiation is not allowed.