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;
23 import java.io.ByteArrayInputStream;
24 import java.io.IOException;
25 import java.io.StringWriter;
26 import java.nio.charset.StandardCharsets;
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 lombok.AccessLevel;
41 import lombok.NoArgsConstructor;
42 import org.onap.cps.spi.exceptions.DataValidationException;
43 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
45 import org.w3c.dom.Document;
46 import org.w3c.dom.Element;
47 import org.xml.sax.SAXException;
49 @NoArgsConstructor(access = AccessLevel.PRIVATE)
50 public class XmlFileUtils {
52 private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
53 private static boolean isNewDocumentBuilderFactoryInstance = true;
54 private static final TransformerFactory transformerFactory = TransformerFactory.newInstance();
55 private static final Pattern XPATH_PROPERTY_REGEX =
56 Pattern.compile("\\[@(\\S{1,100})=['\\\"](\\S{1,100})['\\\"]\\]");
59 * Prepare XML content.
61 * @param xmlContent XML content sent to store
62 * @param schemaContext schema context
64 * @return XML content wrapped by root node (if needed)
66 public static String prepareXmlContent(final String xmlContent, final SchemaContext schemaContext)
67 throws IOException, ParserConfigurationException, TransformerException, SAXException {
68 return addRootNodeToXmlContent(xmlContent, schemaContext.getModules().iterator().next().getName(),
69 YangUtils.DATA_ROOT_NODE_NAMESPACE);
73 * Prepare XML content.
75 * @param xmlContent XML content sent to store
76 * @param parentSchemaNode Parent schema node
77 * @param xpath Parent xpath
79 * @return XML content wrapped by root node (if needed)
81 public static String prepareXmlContent(final String xmlContent,
82 final DataSchemaNode parentSchemaNode,
84 throws IOException, ParserConfigurationException, TransformerException, SAXException {
85 final String namespace = parentSchemaNode.getQName().getNamespace().toString();
86 final String parentXpathPart = xpath.substring(xpath.lastIndexOf('/') + 1);
87 final Matcher regexMatcher = XPATH_PROPERTY_REGEX.matcher(parentXpathPart);
88 if (regexMatcher.find()) {
89 final HashMap<String, String> rootNodePropertyMap = new HashMap<>();
90 rootNodePropertyMap.put(regexMatcher.group(1), regexMatcher.group(2));
91 return addRootNodeToXmlContent(xmlContent, parentSchemaNode.getQName().getLocalName(), namespace,
95 return addRootNodeToXmlContent(xmlContent, parentSchemaNode.getQName().getLocalName(), namespace);
98 private static String addRootNodeToXmlContent(final String xmlContent,
99 final String rootNodeTagName,
100 final String namespace,
101 final Map<String, String> rootNodeProperty)
102 throws IOException, SAXException, ParserConfigurationException, TransformerException {
103 final DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
104 final StringBuilder xmlStringBuilder = new StringBuilder();
105 xmlStringBuilder.append(xmlContent);
106 final Document document = documentBuilder.parse(
107 new ByteArrayInputStream(xmlStringBuilder.toString().getBytes(StandardCharsets.UTF_8)));
108 final Element root = document.getDocumentElement();
109 if (!root.getTagName().equals(rootNodeTagName)
110 && !root.getTagName().equals(YangUtils.DATA_ROOT_NODE_TAG_NAME)) {
111 final Document documentWithRootNode = addDataRootNode(root, rootNodeTagName, namespace, rootNodeProperty);
112 documentWithRootNode.setXmlStandalone(true);
113 final Transformer transformer = getTransformerFactory().newTransformer();
114 final StringWriter stringWriter = new StringWriter();
115 transformer.transform(new DOMSource(documentWithRootNode), new StreamResult(stringWriter));
116 return stringWriter.toString();
122 * Add root node to XML content.
124 * @param xmlContent XML content to add root node into
125 * @param rootNodeTagName Root node tag name
126 * @return XML content with root node tag added (if needed)
128 public static String addRootNodeToXmlContent(final String xmlContent,
129 final String rootNodeTagName,
130 final String namespace)
131 throws IOException, ParserConfigurationException, TransformerException, SAXException {
132 return addRootNodeToXmlContent(xmlContent, rootNodeTagName, namespace, new HashMap<>());
136 * Add root node into DOM element.
138 * @param node DOM element to add root node into
139 * @param tagName Root tag name to add
140 * @return DOM element with a root node
142 static Document addDataRootNode(final Element node,
143 final String tagName,
144 final String namespace,
145 final Map<String, String> rootNodeProperty) {
147 final DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
148 final Document document = documentBuilder.newDocument();
149 final Element rootElement = document.createElementNS(namespace, tagName);
150 for (final Map.Entry<String, String> entry : rootNodeProperty.entrySet()) {
151 final Element propertyElement = document.createElement(entry.getKey());
152 propertyElement.setTextContent(entry.getValue());
153 rootElement.appendChild(propertyElement);
155 rootElement.appendChild(document.adoptNode(node));
156 document.appendChild(rootElement);
158 } catch (final ParserConfigurationException exception) {
159 throw new DataValidationException("Can't parse XML", "XML can't be parsed", exception);
163 private static DocumentBuilderFactory getDocumentBuilderFactory() {
165 if (isNewDocumentBuilderFactoryInstance) {
166 documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
167 documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
168 isNewDocumentBuilderFactoryInstance = false;
171 return documentBuilderFactory;
174 private static TransformerFactory getTransformerFactory() {
175 return transformerFactory;