2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * Copyright (C) 2017 Huawei Technologies Co., Ltd. All rights reserved.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.so.bpmn.core.xml;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.io.Reader;
29 import java.io.StringReader;
30 import java.io.StringWriter;
31 import java.util.HashMap;
33 import java.util.Optional;
35 import javax.xml.XMLConstants;
36 import javax.xml.parsers.DocumentBuilder;
37 import javax.xml.parsers.DocumentBuilderFactory;
38 import javax.xml.parsers.ParserConfigurationException;
39 import javax.xml.transform.OutputKeys;
40 import javax.xml.transform.Source;
41 import javax.xml.transform.Transformer;
42 import javax.xml.transform.TransformerException;
43 import javax.xml.transform.TransformerFactory;
44 import javax.xml.transform.dom.DOMSource;
45 import javax.xml.transform.stream.StreamResult;
46 import javax.xml.transform.stream.StreamSource;
47 import javax.xml.xpath.XPath;
48 import javax.xml.xpath.XPathConstants;
49 import javax.xml.xpath.XPathExpressionException;
50 import javax.xml.xpath.XPathFactory;
52 import org.apache.commons.lang3.StringEscapeUtils;
53 import org.onap.so.logger.MsoLogger;
54 import org.w3c.dom.Document;
55 import org.w3c.dom.NamedNodeMap;
56 import org.w3c.dom.Node;
57 import org.w3c.dom.NodeList;
58 import org.xml.sax.InputSource;
59 import org.xml.sax.SAXException;
62 * XML transformation methods and other useful functions.
64 public final class XmlTool {
66 private static final Map<String, Integer> ENTITIES = new HashMap<>();
67 private static final MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.BPEL, XmlTool.class);
69 ENTITIES.put("amp", 38);
70 ENTITIES.put("quot", 34);
71 ENTITIES.put("lt", 60);
72 ENTITIES.put("gt", 62);
76 * Instantiation is not allowed.
82 * Normalizes and formats XML. This method consolidates and moves all namespace
83 * declarations to the root element. The result will not have an XML prolog or
85 * @param xml the XML to normalize
87 * @throws TransformerException
88 * @throws ParserConfigurationException
89 * @throws SAXException
90 * @throws XPathExpressionException
92 public static String normalize(Object xml) throws IOException, TransformerException,
93 ParserConfigurationException, SAXException, XPathExpressionException {
99 Source xsltSource = new StreamSource(new StringReader(
100 readResourceFile("normalize-namespaces.xsl")));
102 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
103 dbFactory.setNamespaceAware(true);
104 dbFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
105 dbFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
106 DocumentBuilder db = dbFactory.newDocumentBuilder();
107 InputSource source = new InputSource(new StringReader(String.valueOf(xml)));
108 Document doc = db.parse(source);
110 // Start of code to remove whitespace outside of tags
111 XPath xPath = XPathFactory.newInstance().newXPath();
112 NodeList nodeList = (NodeList) xPath.evaluate(
113 "//text()[normalize-space()='']", doc, XPathConstants.NODESET);
115 for (int i = 0; i < nodeList.getLength(); ++i) {
116 Node node = nodeList.item(i);
117 node.getParentNode().removeChild(node);
119 // End of code to remove whitespace outside of tags
121 // the factory pattern supports different XSLT processors
122 TransformerFactory transformerFactory = TransformerFactory.newInstance();
123 Transformer transformer = transformerFactory.newTransformer(xsltSource);
125 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
126 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
127 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
128 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
130 StringWriter writer = new StringWriter();
131 transformer.transform(new DOMSource(doc), new StreamResult(writer));
132 return writer.toString().trim();
136 * Encodes a value so it can be used inside an XML text element.
137 * @param value the string to encode
138 * @return the encoded string
140 public static String encode(Object value) {
144 return StringEscapeUtils.escapeXml11(value.toString());
148 * Removes the preamble, if present, from an XML document.
149 * @param xml the XML document
150 * @return a possibly modified document
152 public static String removePreamble(Object xml) {
157 return String.valueOf(xml).replaceAll("(<\\?[^<]*\\?>\\s*[\\r\\n]*)?", "");
161 * Removes namespaces and namespace declarations from an XML document.
162 * @param xml the XML document
163 * @return a possibly modified document
165 public static String removeNamespaces(Object xml) {
167 LOGGER.debug("removeNamespaces input object is null , returning null");
171 String text = String.valueOf(xml);
173 // remove xmlns declaration
174 text = text.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
175 // remove opening tag prefix
176 text = text.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
177 // remove closing tags prefix
178 text = text.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
179 // remove extra spaces left when xmlns declarations are removed
180 text = text.replaceAll("\\s+>", ">");
187 * Reads the specified resource file and return the contents as a string.
188 * @param file Name of the resource file
189 * @return the contents of the resource file as a String
190 * @throws IOException if there is a problem reading the file
192 private static String readResourceFile(String file) throws IOException {
194 try (InputStream stream = XmlTool.class.getClassLoader().getResourceAsStream(file);
195 Reader reader = new InputStreamReader(stream, "UTF-8")) {
197 StringBuilder out = new StringBuilder();
198 char[] buf = new char[1024];
201 while ((n = reader.read(buf)) >= 0) {
202 out.append(buf, 0, n);
204 return out.toString();
205 } catch (Exception e) {
206 LOGGER.debug("Exception at readResourceFile stream: " + e);
212 * Parses the XML document String for the first occurrence of the specified element tag.
213 * If found, the value associated with that element tag is replaced with the new value
214 * and a String containing the modified XML document is returned. If the XML passed is
215 * null or the element tag is not found in the document, null will be returned.
216 * @param xml String containing the original XML document.
217 * @param elementTag String containing the tag of the element to be modified.
218 * @param newValue String containing the new value to be used to modify the corresponding element.
219 * @return the contents of the modified XML document as a String or null/empty if the modification failed.
220 * @throws IOException, TransformerException, ParserConfigurationException, SAXException
222 public static Optional<String> modifyElement(String xml, String elementTag, String newValue) throws IOException, TransformerException,
223 ParserConfigurationException, SAXException {
225 if (xml == null || xml.isEmpty()) {
226 // no XML content to be modified, return empty
227 return Optional.empty();
230 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
231 dbFactory.setNamespaceAware(true);
232 dbFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
233 dbFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
234 DocumentBuilder db = dbFactory.newDocumentBuilder();
235 InputSource source = new InputSource(new StringReader(xml));
236 Document doc = db.parse(source);
238 Node modNode = doc.getElementsByTagName(elementTag).item(0);
239 if (modNode == null) {
240 // did not find the specified element to be modified, return empty
241 //System.out.println("Did not find element tag " + elementTag + " in XML");
242 return Optional.empty();
244 modNode.setTextContent(newValue);
247 TransformerFactory transformerFactory = TransformerFactory.newInstance();
248 Transformer transformer = transformerFactory.newTransformer();
249 StringWriter writer = new StringWriter();
250 transformer.transform(new DOMSource(doc), new StreamResult(writer));
251 // return the modified String representation of the XML
252 return Optional.of(writer.toString().trim());