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 TransformerFactory transformerFactory = TransformerFactory.newInstance();
53 private static boolean isNewTransformerFactoryInstance = 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 Transformer transformer = getTransformerFactory().newTransformer();
113 final StringWriter stringWriter = new StringWriter();
114 transformer.transform(new DOMSource(documentWithRootNode), new StreamResult(stringWriter));
115 return stringWriter.toString();
121 * Add root node to XML content.
123 * @param xmlContent XML content to add root node into
124 * @param rootNodeTagName Root node tag name
125 * @return XML content with root node tag added (if needed)
127 public static String addRootNodeToXmlContent(final String xmlContent,
128 final String rootNodeTagName,
129 final String namespace)
130 throws IOException, ParserConfigurationException, TransformerException, SAXException {
131 return addRootNodeToXmlContent(xmlContent, rootNodeTagName, namespace, new HashMap<>());
135 * Add root node into DOM element.
137 * @param node DOM element to add root node into
138 * @param tagName Root tag name to add
139 * @return DOM element with a root node
141 static Document addDataRootNode(final Element node,
142 final String tagName,
143 final String namespace,
144 final Map<String, String> rootNodeProperty) {
146 final DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
147 final Document document = documentBuilder.newDocument();
148 final Element rootElement = document.createElementNS(namespace, tagName);
149 for (final Map.Entry<String, String> entry : rootNodeProperty.entrySet()) {
150 final Element propertyElement = document.createElement(entry.getKey());
151 propertyElement.setTextContent(entry.getValue());
152 rootElement.appendChild(propertyElement);
154 rootElement.appendChild(document.adoptNode(node));
155 document.appendChild(rootElement);
157 } catch (final ParserConfigurationException exception) {
158 throw new DataValidationException("Can't parse XML", "XML can't be parsed", exception);
162 private static DocumentBuilderFactory getDocumentBuilderFactory() {
164 final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
165 documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
166 documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
168 return documentBuilderFactory;
171 private static TransformerFactory getTransformerFactory() {
172 if (isNewTransformerFactoryInstance) {
173 transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
174 transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
175 isNewTransformerFactoryInstance = false;
178 return transformerFactory;