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.openecomp.mso.logger.MsoLogger;
50 import org.w3c.dom.Document;
51 import org.w3c.dom.Node;
52 import org.w3c.dom.NodeList;
53 import org.xml.sax.InputSource;
54 import org.xml.sax.SAXException;
57 * XML transformation methods and other useful functions.
59 public final class XmlTool {
61 private static final Map<String, Integer> ENTITIES = new HashMap<String, Integer>();
62 private static final MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.BPEL);
64 ENTITIES.put("amp", new Integer(38));
65 ENTITIES.put("quot", new Integer(34));
66 ENTITIES.put("lt", new Integer(60));
67 ENTITIES.put("gt", new Integer(62));
71 * Normalizes and formats XML. This method consolidates and moves all namespace
72 * declarations to the root element. The result will not have an XML prolog or
74 * @param xml the XML to normalize
76 * @throws TransformerException
77 * @throws ParserConfigurationException
78 * @throws SAXException
79 * @throws XPathExpressionException
81 public static String normalize(Object xml) throws IOException, TransformerException,
82 ParserConfigurationException, SAXException, XPathExpressionException {
88 Source xsltSource = new StreamSource(new StringReader(
89 readResourceFile("normalize-namespaces.xsl")));
91 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
92 dbFactory.setNamespaceAware(true);
93 DocumentBuilder db = dbFactory.newDocumentBuilder();
94 InputSource source = new InputSource(new StringReader(String.valueOf(xml)));
95 Document doc = db.parse(source);
97 // Start of code to remove whitespace outside of tags
98 XPath xPath = XPathFactory.newInstance().newXPath();
99 NodeList nodeList = (NodeList) xPath.evaluate(
100 "//text()[normalize-space()='']", doc, XPathConstants.NODESET);
102 for (int i = 0; i < nodeList.getLength(); ++i) {
103 Node node = nodeList.item(i);
104 node.getParentNode().removeChild(node);
106 // End of code to remove whitespace outside of tags
108 // the factory pattern supports different XSLT processors
109 TransformerFactory transformerFactory = TransformerFactory.newInstance();
110 Transformer transformer = transformerFactory.newTransformer(xsltSource);
112 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
113 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
114 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
115 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
117 StringWriter writer = new StringWriter();
118 transformer.transform(new DOMSource(doc), new StreamResult(writer));
119 return writer.toString().trim();
123 * Encodes a value so it can be used inside an XML text element.
124 * @param s the string to encode
125 * @return the encoded string
127 public static String encode(Object value) {
132 String s = String.valueOf(value);
133 StringBuilder out = new StringBuilder();
134 boolean modified = false;
136 for (int i = 0; i < s.length(); i++) {
137 char c = s.charAt(i);
142 } else if (c == '>') {
145 } else if (c == '&') {
148 } else if (c < 32 || c > 126) {
149 out.append("&#" + (int)c + ";");
157 return out.toString();
164 * Encodes a value so it can be used inside an XML attribute.
165 * @param s the string to encode
166 * @return the encoded string
168 public static String encodeAttr(Object value) {
173 String s = String.valueOf(value);
174 StringBuilder out = new StringBuilder();
175 boolean modified = false;
177 for (int i = 0; i < s.length(); i++) {
178 char c = s.charAt(i);
183 } else if (c == '>') {
186 } else if (c == '"') {
187 out.append(""");
189 } else if (c == '&') {
192 } else if (c < 32 || c > 126) {
193 out.append("&#" + (int)c + ";");
201 return out.toString();
208 * Decodes XML entities in a string value
209 * @param value a value with embedded XML entities
210 * @return the decoded string
212 public static String decode(Object value) {
217 String s = String.valueOf(value);
219 StringBuilder out = new StringBuilder(s.length());
220 int ampIndex = s.indexOf("&");
223 while (ampIndex >= 0) {
224 int nextAmpIndex = s.indexOf("&", ampIndex + 1);
225 int nextSemiIndex = s.indexOf(";", ampIndex + 1);
226 if (nextSemiIndex != -1 && (nextAmpIndex == -1 || nextSemiIndex < nextAmpIndex)) {
228 String entity = s.substring(ampIndex + 1, nextSemiIndex);
231 if (entity.startsWith("#")) {
232 code = Integer.parseInt(entity.substring(1), 10);
234 if (ENTITIES.containsKey(entity)) {
235 code = ENTITIES.get(entity);
238 } catch (NumberFormatException x) {
242 out.append(s.substring(lastEnd, ampIndex));
243 lastEnd = nextSemiIndex + 1;
244 if (code >= 0 && code <= 0xffff) {
245 out.append((char) code);
253 ampIndex = nextAmpIndex;
256 out.append(s.substring(lastEnd));
257 return out.toString();
261 * Removes the preamble, if present, from an XML document.
262 * @param xml the XML document
263 * @return a possibly modified document
265 public static String removePreamble(Object xml) {
270 return String.valueOf(xml).replaceAll("(<\\?[^<]*\\?>\\s*[\\r\\n]*)?", "");
274 * Removes namespaces and namespace declarations from an XML document.
275 * @param xml the XML document
276 * @return a possibly modified document
278 public static String removeNamespaces(Object xml) {
280 LOGGER.debug("removeNamespaces input object is null , returning null");
284 String text = String.valueOf(xml);
286 // remove xmlns declaration
287 text = text.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
288 // remove opening tag prefix
289 text = text.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
290 // remove closing tags prefix
291 text = text.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
292 // remove extra spaces left when xmlns declarations are removed
293 text = text.replaceAll("\\s+>", ">");
300 * Reads the specified resource file and return the contents as a string.
301 * @param file Name of the resource file
302 * @return the contents of the resource file as a String
303 * @throws IOException if there is a problem reading the file
305 private static String readResourceFile(String file) throws IOException {
306 InputStream stream = null;
308 stream = XmlTool.class.getClassLoader().getResourceAsStream(file);
310 if (stream == null) {
311 throw new FileNotFoundException("No such resource file: " + file);
314 Reader reader = new InputStreamReader(stream, "UTF-8");
315 StringBuilder out = new StringBuilder();
316 char[] buf = new char[1024];
319 while ((n = reader.read(buf)) >= 0) {
320 out.append(buf, 0, n);
325 return out.toString();
327 if (stream != null) {
330 } catch (Exception e) {
331 LOGGER.debug("Exception at readResourceFile close stream: " + e);
338 * Instantiation is not allowed.