2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2022 Deutsche Telekom AG
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.utils;
24 import java.io.ByteArrayInputStream;
25 import java.io.IOException;
26 import java.io.StringWriter;
27 import java.util.HashMap;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import javax.xml.XMLConstants;
32 import javax.xml.parsers.DocumentBuilder;
33 import javax.xml.parsers.DocumentBuilderFactory;
34 import javax.xml.parsers.ParserConfigurationException;
35 import javax.xml.transform.Transformer;
36 import javax.xml.transform.TransformerException;
37 import javax.xml.transform.TransformerFactory;
38 import javax.xml.transform.dom.DOMSource;
39 import javax.xml.transform.stream.StreamResult;
40 import org.onap.cps.spi.exceptions.DataValidationException;
41 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 import org.w3c.dom.Document;
44 import org.w3c.dom.Element;
45 import org.xml.sax.SAXException;
47 public class XmlFileUtils {
49 private static DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
50 private static final String DATA_ROOT_NODE_TAG_NAME = "data";
51 private static final String ROOT_NODE_NAMESPACE = "urn:ietf:params:xml:ns:netconf:base:1.0";
52 private static final Pattern XPATH_PROPERTY_REGEX = Pattern.compile(
53 "\\[@(\\S{1,100})=['\\\"](\\S{1,100})['\\\"]\\]");
56 * Prepare XML content.
58 * @param xmlContent XML content sent to store
59 * @param schemaContext schema context
60 * @return XML content wrapped by root node (if needed)
62 public static String prepareXmlContent(final String xmlContent, final SchemaContext schemaContext) {
64 return addRootNodeToXmlContent(xmlContent, schemaContext.getModules().iterator().next().getName(),
70 * Prepare XML content.
72 * @param xmlContent XML content sent to store
73 * @param parentSchemaNode Parent schema node
74 * @return XML content wrapped by root node (if needed)
76 public static String prepareXmlContent(final String xmlContent, final DataSchemaNode parentSchemaNode,
78 final String namespace = parentSchemaNode.getQName().getNamespace().toString();
79 final String parentXpathPart = xpath.substring(xpath.lastIndexOf('/') + 1);
80 final Matcher regexMatcher = XPATH_PROPERTY_REGEX.matcher(parentXpathPart);
81 if (regexMatcher.find()) {
82 final HashMap<String, String> rootNodePropertyMap = new HashMap<String, String>();
83 rootNodePropertyMap.put(regexMatcher.group(1), regexMatcher.group(2));
84 return addRootNodeToXmlContent(xmlContent, parentSchemaNode.getQName().getLocalName(), namespace,
88 return addRootNodeToXmlContent(xmlContent, parentSchemaNode.getQName().getLocalName(), namespace);
92 * Add root node to XML content.
94 * @param xmlContent xml content to add root node
95 * @param rootNodeTagName root node tag name
96 * @param namespace root node namespace
97 * @param rootNodeProperty root node properites map
98 * @return An edited content with added root node (if needed)
100 public static String addRootNodeToXmlContent(final String xmlContent, final String rootNodeTagName,
101 final String namespace,
102 final HashMap<String, String> rootNodeProperty) {
104 final DocumentBuilder documentBuilder = dbFactory.newDocumentBuilder();
105 final StringBuilder xmlStringBuilder = new StringBuilder();
106 xmlStringBuilder.append(xmlContent);
107 Document xmlDoc = documentBuilder.parse(
108 new ByteArrayInputStream(xmlStringBuilder.toString().getBytes("utf-8")));
109 final Element root = xmlDoc.getDocumentElement();
110 if (!root.getTagName().equals(rootNodeTagName) && !root.getTagName().equals(DATA_ROOT_NODE_TAG_NAME)) {
111 xmlDoc = addDataRootNode(root, rootNodeTagName, namespace, rootNodeProperty);
112 xmlDoc.setXmlStandalone(true);
113 final TransformerFactory transformerFactory = TransformerFactory.newInstance();
114 transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
115 transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
116 final Transformer transformer = transformerFactory.newTransformer();
117 final StringWriter stringWriter = new StringWriter();
118 transformer.transform(new DOMSource(xmlDoc), new StreamResult(stringWriter));
119 return stringWriter.toString();
122 } catch (SAXException | IOException | ParserConfigurationException | TransformerException exception) {
123 throw new DataValidationException("Failed to parse XML data", "Invalid xml input " + exception.getMessage(),
129 * Add root node to XML content.
131 * @param xmlContent XML content to add root node into
132 * @param rootNodeTagName Root node tag name
133 * @return XML content with root node tag added (if needed)
135 public static String addRootNodeToXmlContent(final String xmlContent, final String rootNodeTagName,
136 final String namespace) {
137 return addRootNodeToXmlContent(xmlContent, rootNodeTagName, namespace, new HashMap<String, String>());
141 * Add root node into DOM element.
143 * @param node DOM element to add root node into
144 * @param tagName Root tag name to add
145 * @return DOM element with a root node
147 static Document addDataRootNode(final Element node, final String tagName, final String namespace,
148 final HashMap<String, String> rootNodeProperty) {
150 final DocumentBuilder docBuilder = dbFactory.newDocumentBuilder();
151 final Document document = docBuilder.newDocument();
152 final Element rootElement = document.createElementNS(namespace, tagName);
153 for (final Map.Entry<String, String> entry : rootNodeProperty.entrySet()) {
154 final Element propertyElement = document.createElement(entry.getKey());
155 propertyElement.setTextContent(entry.getValue());
156 rootElement.appendChild(propertyElement);
158 rootElement.appendChild(document.adoptNode(node));
159 document.appendChild(rootElement);
161 } catch (final ParserConfigurationException exception) {
162 throw new DataValidationException("Can't parse XML", "XML can't be parsed", exception);