All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.exactpro.sf.actions.XMLUtil Maven / Gradle / Ivy
/******************************************************************************
* Copyright 2009-2020 Exactpro (Exactpro Systems Limited)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.exactpro.sf.actions;
import com.exactpro.sf.aml.Description;
import com.exactpro.sf.common.util.EPSCommonException;
import com.exactpro.sf.configuration.ResourceAliases;
import com.exactpro.sf.scriptrunner.AbstractCaller;
import com.exactpro.sf.scriptrunner.utilitymanager.UtilityMethod;
import com.google.common.collect.Lists;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
@MatrixUtils
@ResourceAliases("XMLUtil")
public class XMLUtil extends AbstractCaller {
private final XPathFactory xpathFactory = XPathFactory.newInstance();
private final DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
@Description("Returns a xml document with new value of selected node. " +
"content - xml document " +
"xpath - path to node " +
"value - new value of node " +
"Example: " +
"#{setValueByXPath(content, xpath, value)}")
@UtilityMethod
public String setValueByXPath(String content, String xpath, String value) {
try {
DocumentBuilder docBuilder = documentFactory.newDocumentBuilder();
Document doc = docBuilder.parse(new InputSource(new StringReader(content)));
XPath xPath = xpathFactory.newXPath();
NodeList nodes = (NodeList)xPath.compile(xpath).evaluate(doc, XPathConstants.NODESET);
if (nodes.getLength() != 1) {
throw new EPSCommonException("Should be selected only one node to set value.");
}
if (!hasOnlyTextNode(nodes.item(0))) {
throw new EPSCommonException("The node has child nodes." + System.lineSeparator() + nodeToString(nodes.item(0)));
}
Node node = nodes.item(0);
Node oldChild = nodeListToList(node.getChildNodes(), x -> x.getNodeType() != Node.COMMENT_NODE).get(0);
Node newChild = doc.createTextNode(value);
node.replaceChild(newChild, oldChild);
return nodeToString(doc);
} catch (SAXException | IOException e) {
throw new EPSCommonException("There is a problem with input data.", e);
} catch (ParserConfigurationException | XPathExpressionException e) {
throw new EPSCommonException(e);
}
}
@Description("Returns a value of node by xpath. " +
"content - xml document " +
"xpath - path to node " +
"Example: " +
"#{getObjectByXPath(content, xpath)}")
@UtilityMethod
public String getValueByXPath(String content, String xpath) {
NodeList nodes = getNodesByXPath(content, xpath);
if (nodes.getLength() != 1 || !hasOnlyTextNode(nodes.item(0))) {
throw new EPSCommonException("The node has child nodes." + System.lineSeparator() + nodeToString(nodes.item(0)));
}
return nodes.item(0).getTextContent();
}
@Description("Returns a object by xpath. " +
"content - xml document " +
"xpath - path to object " +
"Example: " +
"#{getObjectByXPath(content, xpath)}")
@UtilityMethod
public Map getObjectByXPath(String content, String xpath) {
NodeList nodes = getNodesByXPath(content, xpath);
if (nodes.getLength() == 0) {
return Collections.emptyMap();
}
if (nodes.getLength() != 1) {
throw new EPSCommonException("There are several nodes for this xpath \"" + xpath + "\".");
}
if (hasOnlyTextNode(nodes.item(0))) {
throw new EPSCommonException("Node isn't object.");
}
return nodeToMap(nodes.item(0));
}
@Description("Returns a list values by xpath. " +
"content - xml document " +
"xpath - path to list " +
"Example: " +
"#{getListByXPath(content, xpath)}")
@UtilityMethod
public List getListByXPath(String content, String xpath) {
NodeList nodes = getNodesByXPath(content, xpath);
if (nodes.getLength() == 0) {
return Collections.emptyList();
}
List result = new ArrayList<>();
for(Node elem : nodeListToList(nodes, x -> x.getNodeType() != Node.COMMENT_NODE)) {
if (!hasOnlyTextNode(elem)) {
throw new EPSCommonException("The node has child nodes." + System.lineSeparator() + nodeToString(elem));
}
result.add(elem.getTextContent());
}
return result;
}
private NodeList getNodesByXPath(String content, String xpath) {
try {
DocumentBuilder docBuilder = documentFactory.newDocumentBuilder();
Document doc = docBuilder.parse(new InputSource(new StringReader(content)));
XPath xPath = xpathFactory.newXPath();
return (NodeList)xPath.compile(xpath).evaluate(doc, XPathConstants.NODESET);
} catch (SAXException | IOException e) {
throw new EPSCommonException("There is a problem with input data.", e);
} catch (ParserConfigurationException | XPathExpressionException e) {
throw new EPSCommonException(e);
}
}
private static boolean hasOnlyTextNode(Node node) {
if (!node.hasChildNodes()) {
return false;
}
List nodes = nodeListToList(node.getChildNodes(),
x -> x.getNodeType() != Node.COMMENT_NODE);
return nodes.size() == 1 && nodes.get(0).getNodeType() == Node.TEXT_NODE;
}
private static String nodeToString(Node node) {
StringWriter sw = new StringWriter();
try {
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.transform(new DOMSource(node), new StreamResult(sw));
return sw.toString();
} catch (TransformerException e) {
throw new EPSCommonException("Problem with transforming a node to a string.");
}
}
private static Map nodeToMap(Node node) {
Map map = new HashMap<>();
if (node.hasChildNodes()) {
NodeList nodeList = node.getChildNodes();
for (int i = 0, len = nodeList.getLength(); len > i; i++) {
Node thisNode = nodeList.item(i);
if (thisNode.getNodeType() == Node.COMMENT_NODE) {
continue;
}
Object value = null;
if (hasOnlyTextNode(thisNode)) {
value = thisNode.getTextContent();
} else {
value = nodeToMap(thisNode);
}
if (map.containsKey(thisNode.getNodeName())) {
Object mValue = map.get(thisNode.getNodeName());
if (mValue instanceof List) {
((List) mValue).add(value);
} else {
map.put(thisNode.getNodeName(), Lists.newArrayList(mValue, value));
}
} else {
map.put(thisNode.getNodeName(), value);
}
}
}
return map;
}
private static List nodeListToList(NodeList nodeList, Predicate ... filters) {
if (nodeList.getLength() == 0) {
return Collections.emptyList();
}
Stream stream = Stream.iterate(nodeList.item(0), Node::getNextSibling)
.limit(nodeList.getLength());
for (Predicate filter : filters) {
stream = stream.filter(filter);
}
return stream.collect(toList());
}
}