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 final TransformerFactory transformerFactory = TransformerFactory.newInstance();
54 private static boolean isNewDocumentBuilderFactoryInstance = true;
55 private static boolean isNewTransformerFactoryInstance = true;
56 private static final Pattern XPATH_PROPERTY_REGEX =
57 Pattern.compile("\\[@(\\S{1,100})=['\\\"](\\S{1,100})['\\\"]\\]");
60 * Prepare XML content.
62 * @param xmlContent XML content sent to store
63 * @param schemaContext schema context
65 * @return XML content wrapped by root node (if needed)
67 public static String prepareXmlContent(final String xmlContent, final SchemaContext schemaContext)
68 throws IOException, ParserConfigurationException, TransformerException, SAXException {
69 return addRootNodeToXmlContent(xmlContent, schemaContext.getModules().iterator().next().getName(),
70 YangUtils.DATA_ROOT_NODE_NAMESPACE);
74 * Prepare XML content.
76 * @param xmlContent XML content sent to store
77 * @param parentSchemaNode Parent schema node
78 * @param xpath Parent xpath
80 * @return XML content wrapped by root node (if needed)
82 public static String prepareXmlContent(final String xmlContent,
83 final DataSchemaNode parentSchemaNode,
85 throws IOException, ParserConfigurationException, TransformerException, SAXException {
86 final String namespace = parentSchemaNode.getQName().getNamespace().toString();
87 final String parentXpathPart = xpath.substring(xpath.lastIndexOf('/') + 1);
88 final Matcher regexMatcher = XPATH_PROPERTY_REGEX.matcher(parentXpathPart);
89 if (regexMatcher.find()) {
90 final HashMap<String, String> rootNodePropertyMap = new HashMap<>();
91 rootNodePropertyMap.put(regexMatcher.group(1), regexMatcher.group(2));
92 return addRootNodeToXmlContent(xmlContent, parentSchemaNode.getQName().getLocalName(), namespace,
96 return addRootNodeToXmlContent(xmlContent, parentSchemaNode.getQName().getLocalName(), namespace);
99 private static String addRootNodeToXmlContent(final String xmlContent,
100 final String rootNodeTagName,
101 final String namespace,
102 final Map<String, String> rootNodeProperty)
103 throws IOException, SAXException, ParserConfigurationException, TransformerException {
104 final DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
105 final StringBuilder xmlStringBuilder = new StringBuilder();
106 xmlStringBuilder.append(xmlContent);
107 final Document document = documentBuilder.parse(
108 new ByteArrayInputStream(xmlStringBuilder.toString().getBytes(StandardCharsets.UTF_8)));
109 final Element root = document.getDocumentElement();
110 if (!root.getTagName().equals(rootNodeTagName)
111 && !root.getTagName().equals(YangUtils.DATA_ROOT_NODE_TAG_NAME)) {
112 final Document documentWithRootNode = addDataRootNode(root, rootNodeTagName, namespace, rootNodeProperty);
113 documentWithRootNode.setXmlStandalone(true);
114 final Transformer transformer = getTransformerFactory().newTransformer();
115 final StringWriter stringWriter = new StringWriter();
116 transformer.transform(new DOMSource(documentWithRootNode), new StreamResult(stringWriter));
117 return stringWriter.toString();
123 * Add root node to XML content.
125 * @param xmlContent XML content to add root node into
126 * @param rootNodeTagName Root node tag name
127 * @return XML content with root node tag added (if needed)
129 public static String addRootNodeToXmlContent(final String xmlContent,
130 final String rootNodeTagName,
131 final String namespace)
132 throws IOException, ParserConfigurationException, TransformerException, SAXException {
133 return addRootNodeToXmlContent(xmlContent, rootNodeTagName, namespace, new HashMap<>());
137 * Add root node into DOM element.
139 * @param node DOM element to add root node into
140 * @param tagName Root tag name to add
141 * @return DOM element with a root node
143 static Document addDataRootNode(final Element node,
144 final String tagName,
145 final String namespace,
146 final Map<String, String> rootNodeProperty) {
148 final DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
149 final Document document = documentBuilder.newDocument();
150 final Element rootElement = document.createElementNS(namespace, tagName);
151 for (final Map.Entry<String, String> entry : rootNodeProperty.entrySet()) {
152 final Element propertyElement = document.createElement(entry.getKey());
153 propertyElement.setTextContent(entry.getValue());
154 rootElement.appendChild(propertyElement);
156 rootElement.appendChild(document.adoptNode(node));
157 document.appendChild(rootElement);
159 } catch (final ParserConfigurationException exception) {
160 throw new DataValidationException("Can't parse XML", "XML can't be parsed", exception);
164 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 if (isNewTransformerFactoryInstance) {
176 transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
177 transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
178 isNewTransformerFactoryInstance = false;
181 return transformerFactory;