class XMLUtil extends AbstractCaller {

    @Description("Returns a xml document with new value of selected node.<br>" +
            "<b>content</b> - xml document<br>" +
            "<b>xpath</b> - path to node<br>" +
            "<b>value</b> - new value of node<br>" +
            "Example:<br/>" +
            "#{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);
        }
    }

}