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 Pattern XPATH_PROPERTY_REGEX =
55 Pattern.compile("\\[@(\\S{1,100})=['\\\"](\\S{1,100})['\\\"]\\]");
58 * Prepare XML content.
60 * @param xmlContent XML content sent to store
61 * @param schemaContext schema context
63 * @return XML content wrapped by root node (if needed)
65 public static String prepareXmlContent(final String xmlContent, final SchemaContext schemaContext)
66 throws IOException, ParserConfigurationException, TransformerException, SAXException {
67 return addRootNodeToXmlContent(xmlContent, schemaContext.getModules().iterator().next().getName(),
68 YangUtils.DATA_ROOT_NODE_NAMESPACE);
72 * Prepare XML content.
74 * @param xmlContent XML content sent to store
75 * @param parentSchemaNode Parent schema node
76 * @param xpath Parent xpath
78 * @return XML content wrapped by root node (if needed)
80 public static String prepareXmlContent(final String xmlContent,
81 final DataSchemaNode parentSchemaNode,
83 throws IOException, ParserConfigurationException, TransformerException, SAXException {
84 final String namespace = parentSchemaNode.getQName().getNamespace().toString();
85 final String parentXpathPart = xpath.substring(xpath.lastIndexOf('/') + 1);
86 final Matcher regexMatcher = XPATH_PROPERTY_REGEX.matcher(parentXpathPart);
87 if (regexMatcher.find()) {
88 final HashMap<String, String> rootNodePropertyMap = new HashMap<>();
89 rootNodePropertyMap.put(regexMatcher.group(1), regexMatcher.group(2));
90 return addRootNodeToXmlContent(xmlContent, parentSchemaNode.getQName().getLocalName(), namespace,
94 return addRootNodeToXmlContent(xmlContent, parentSchemaNode.getQName().getLocalName(), namespace);
97 private static String addRootNodeToXmlContent(final String xmlContent,
98 final String rootNodeTagName,
99 final String namespace,
100 final Map<String, String> rootNodeProperty)
101 throws IOException, SAXException, ParserConfigurationException, TransformerException {
102 final DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
103 final StringBuilder xmlStringBuilder = new StringBuilder();
104 xmlStringBuilder.append(xmlContent);
105 final Document document = documentBuilder.parse(
106 new ByteArrayInputStream(xmlStringBuilder.toString().getBytes(StandardCharsets.UTF_8)));
107 final Element root = document.getDocumentElement();
108 if (!root.getTagName().equals(rootNodeTagName)
109 && !root.getTagName().equals(YangUtils.DATA_ROOT_NODE_TAG_NAME)) {
110 final Document documentWithRootNode = addDataRootNode(root, rootNodeTagName, namespace, rootNodeProperty);
111 documentWithRootNode.setXmlStandalone(true);
112 final TransformerFactory transformerFactory = TransformerFactory.newInstance();
113 transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
114 transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
115 final Transformer transformer = transformerFactory.newTransformer();
116 final StringWriter stringWriter = new StringWriter();
117 transformer.transform(new DOMSource(documentWithRootNode), new StreamResult(stringWriter));
118 return stringWriter.toString();
124 * Add root node to XML content.
126 * @param xmlContent XML content to add root node into
127 * @param rootNodeTagName Root node tag name
128 * @return XML content with root node tag added (if needed)
130 public static String addRootNodeToXmlContent(final String xmlContent,
131 final String rootNodeTagName,
132 final String namespace)
133 throws IOException, ParserConfigurationException, TransformerException, SAXException {
134 return addRootNodeToXmlContent(xmlContent, rootNodeTagName, namespace, new HashMap<>());
138 * Add root node into DOM element.
140 * @param node DOM element to add root node into
141 * @param tagName Root tag name to add
142 * @return DOM element with a root node
144 static Document addDataRootNode(final Element node,
145 final String tagName,
146 final String namespace,
147 final Map<String, String> rootNodeProperty) {
149 final DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
150 final Document document = documentBuilder.newDocument();
151 final Element rootElement = document.createElementNS(namespace, tagName);
152 for (final Map.Entry<String, String> entry : rootNodeProperty.entrySet()) {
153 final Element propertyElement = document.createElement(entry.getKey());
154 propertyElement.setTextContent(entry.getValue());
155 rootElement.appendChild(propertyElement);
157 rootElement.appendChild(document.adoptNode(node));
158 document.appendChild(rootElement);
160 } catch (final ParserConfigurationException exception) {
161 throw new DataValidationException("Can't parse XML", "XML can't be parsed", exception);
165 private static DocumentBuilderFactory getDocumentBuilderFactory() {
166 if (isNewDocumentBuilderFactoryInstance) {
167 documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
168 documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
169 isNewDocumentBuilderFactoryInstance = false;
172 return documentBuilderFactory;