org.wso2.carbon.bpmn.core.types.datatypes.xml.api.XMLDocument.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.bpmn.core.types.datatypes.xml.api.XMLDocument.java

Source

/*
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you 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 org.wso2.carbon.bpmn.core.types.datatypes.xml.api;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
import org.w3c.dom.UserDataHandler;
import org.wso2.carbon.bpmn.core.types.datatypes.xml.BPMNXmlException;
import org.wso2.carbon.bpmn.core.types.datatypes.xml.Utils;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import java.io.IOException;

/**
 * Wrap class wrapping Document object implementing org.w3c.dom.Document as WSO2 BPMN XML datatype adding new functions
 */
public class XMLDocument implements Document {

    private Document doc;
    private static final Log log = LogFactory.getLog(XMLDocument.class);

    public XMLDocument(Document document) {
        this.doc = document;
    }

    /*******************************************************************************************************************
     * introduced functions for BPMN START
     * ****************************************************************************************************************/

    /**
     * Function to evaluate xPath query and retrieve relevant element
     *
     * @param xpathStr xpath expression to evaluate
     * @return Returns org.w3c.dom.NodeList if there are more than one elements in the result, Otherwise org.w3c.dom.Node Object is returned
     * @throws XPathExpressionException If expression cannot be evaluated
     */
    public Object xPath(String xpathStr) throws BPMNXmlException {

        if (log.isDebugEnabled()) {
            log.debug("Evaluating xPath: " + xpathStr + " on XML :" + this.toString());
        }

        return Utils.evaluateXPath(doc, xpathStr);
    }

    /**
     * Function to evaluate xPath query, and return specified return type
     *
     * @param xpathStr xpath expression to evaluate
     * @param returnType The desired return type of xpath evaluation. Supported retrun types : "NODESET", "NODE", "STRING", "NUMBER", "BOOLEAN"
     * @return result of xpath evaluation in specified return type
     * @throws BPMNXmlException
     * @throws XPathExpressionException
     */
    public Object xPath(String xpathStr, String returnType) throws BPMNXmlException, XPathExpressionException {

        if (returnType.equals(XPathConstants.NODESET.getLocalPart())) {
            Utils.evaluateXPath(doc, xpathStr, XPathConstants.NODESET);
        } else if (returnType.equals(XPathConstants.NODE.getLocalPart())) {
            Utils.evaluateXPath(doc, xpathStr, XPathConstants.NODE);
        } else if (returnType.equals(XPathConstants.STRING.getLocalPart())) {
            Utils.evaluateXPath(doc, xpathStr, XPathConstants.STRING);
        } else if (returnType.equals(XPathConstants.NUMBER.getLocalPart())) {
            Utils.evaluateXPath(doc, xpathStr, XPathConstants.NUMBER);
        } else if (returnType.equals(XPathConstants.BOOLEAN.getLocalPart())) {
            Utils.evaluateXPath(doc, xpathStr, XPathConstants.BOOLEAN);
        } else {
            //Unknown return type
            throw new BPMNXmlException("Unknown return type : " + returnType);
        }

        return null;
    }

    /**
     * Function to set/replace/update an object (String / Element) to matching the xPath provided. (In case new element
     * is added, this api will clone it and merge the new node to the target location pointed by xPath and return the new cloned node)
     *
     * @param xPathStr xPath to the location object need to set
     * @param obj      String or Node
     * @return returns the node get updated when the set object is String, or returns newly added Node in case object is Element
     * @throws XPathExpressionException If expression cannot be evaluated
     * @throws BPMNXmlException         is thrown due to : Provided XPath and object does not match, provided object is not a Node or String
     *                                  result is NodeList, not a Text node or Element
     */
    public Node set(String xPathStr, Object obj) throws XPathExpressionException, BPMNXmlException {

        NodeList evalResult = (NodeList) Utils.evaluateXPath(this.doc, xPathStr, XPathConstants.NODESET);
        if (evalResult.getLength() == 1) {
            Node targetNode = evalResult.item(0);

            if (obj instanceof String && targetNode instanceof Text) { //if string is provided, assume that user
                //need to replace the node value
                targetNode.setNodeValue((String) obj);
                //return updated Text Node
                return targetNode;

            } else if ((obj instanceof Integer || obj instanceof Byte || obj instanceof Character
                    || obj instanceof Short || obj instanceof Long || obj instanceof Float || obj instanceof Double
                    || obj instanceof Boolean) && targetNode instanceof Text) {
                //need to replace the node value
                targetNode.setNodeValue(obj.toString());
                //return updated Text Node
                return targetNode;

            } else if (obj instanceof Element && targetNode instanceof Element
                    && targetNode.getParentNode() != null) { //if the user provides Node object,
                // assume that need to replace the target node
                Node targetParent = targetNode.getParentNode();
                Node nextSibling = targetNode.getNextSibling();
                //remove the target node
                targetParent.removeChild(targetNode);
                //add new node
                Node newNode = doc.importNode((Node) obj, true);

                if (nextSibling != null) {
                    //If next sibling exists we have to put the new node before it
                    targetParent.insertBefore(newNode, nextSibling);
                } else {
                    targetParent.appendChild(newNode);
                }
                //return new node
                return newNode;

            } else { //provided XPath and object to set does not match
                throw new BPMNXmlException("Provided XPath and provided object does not match");
            }

        } else if (evalResult.getLength() > 0) {

            throw new BPMNXmlException(
                    "Error in provided xPath. Evaluation result is NodeList, not a Text node or Element");

        } else {
            throw new BPMNXmlException("Error in provided xPath. Evaluation result is not a Text node or Element");
        }

    }

    /**
     * Function to append child element to target element
     *
     * @param xPathToParent xPath to parent node
     * @param element       element to append
     * @return returns the node get appended or returns newly added Node in case object is Element
     * @throws XPathExpressionException If expression cannot be evaluated
     * @throws BPMNXmlException         If no parent node found, the resulting NodeList empty,
     *                                  Error in provided xPath. Evaluation result is not a Node or NodeList
     */
    public Node appendChild(String xPathToParent, Element element)
            throws XPathExpressionException, BPMNXmlException {

        Object evalResult = Utils.evaluateXPath(doc, xPathToParent);

        if (evalResult instanceof Node && evalResult instanceof Element) {
            //If xpath evaluated to an Element, will add as child element
            Node newNode = doc.importNode((Node) element, true);
            return ((Node) evalResult).appendChild(newNode);

        } else if (evalResult instanceof NodeList) {
            throw new BPMNXmlException(
                    (((NodeList) evalResult).getLength() > 0 ? "xpath does not evaluated to a unique parent node"
                            : "xPath evaluation failed. Node does not exists for xPath: " + xPathToParent));
        } else {
            throw new BPMNXmlException("Error in provided xPath. Evaluation result is not a Node."
                    + "The evaluation result is in type:" + evalResult.getClass().getName());
        }
    }

    /**
     * Inserts the node newChild node before the existing node
     * @param xPathToTargetNode xPath to target node
     * @param element element to insert
     * @return returns the node get inserted on success
     * @throws XPathExpressionException
     * @throws BPMNXmlException
     */
    public Node insertBefore(String xPathToTargetNode, Element element)
            throws XPathExpressionException, BPMNXmlException {

        Object evalResult = Utils.evaluateXPath(doc, xPathToTargetNode);

        if (evalResult instanceof Node && evalResult instanceof Element) {

            Node parentNode = ((Node) evalResult).getParentNode();
            if (parentNode != null) {
                Node newNode = doc.importNode((Node) element, true);
                return parentNode.insertBefore(newNode, (Node) evalResult);
            }

            throw new BPMNXmlException("Target node is the root node (no parent node found).");

        } else if (evalResult instanceof NodeList) {
            throw new BPMNXmlException(
                    (((NodeList) evalResult).getLength() > 0 ? "xpath does not evaluated to a unique parent node"
                            : "xPath evaluation failed. Node does not exists for xPath: " + xPathToTargetNode));
        } else {
            throw new BPMNXmlException("Error in provided xPath. Evaluation result is not a Node."
                    + "The evaluation result is in type:" + evalResult.getClass().getName());
        }
    }

    /**
     * Function overriding Object.toString(). This will serialize the XML object to string
     *
     * @return String serializing XML object
     */
    @Override
    public String toString() {
        try {
            return StringEscapeUtils.escapeXml(Utils.stringify(this));
        } catch (TransformerException e) {
            log.error("Error occurred while serializing XMLDocument", e);
            //If error occurred while serializing we will return the object string
            return ((Object) this).toString();
        }
    }

    /**
     * Function to create new XML Element (this is a util method)
     *
     * @param elementStr xml string that need to convert to representing xml element object
     * @return returns created xml element
     * @throws IOException
     * @throws SAXException
     * @throws ParserConfigurationException
     */
    public Element createNewElement(String elementStr)
            throws IOException, SAXException, ParserConfigurationException {
        XMLDocument document = Utils.parse(elementStr);
        if (document != null) {
            return document.getDocumentElement();
        }
        return null;
    }

    /*******************************************************************************************************************
     * Implemented functions of org.w3c.dom.Document
     ****************************************************************************************************************/

    @Override
    public DocumentType getDoctype() {
        return doc.getDoctype();
    }

    @Override
    public DOMImplementation getImplementation() {
        return doc.getImplementation();
    }

    @Override
    public Element getDocumentElement() {
        return doc.getDocumentElement();
    }

    @Override
    public Element createElement(String tagName) throws DOMException {
        return doc.createElement(tagName);
    }

    @Override
    public DocumentFragment createDocumentFragment() {
        return doc.createDocumentFragment();
    }

    @Override
    public Text createTextNode(String data) {
        return doc.createTextNode(data);
    }

    @Override
    public Comment createComment(String data) {
        return doc.createComment(data);
    }

    @Override
    public CDATASection createCDATASection(String data) throws DOMException {
        return doc.createCDATASection(data);
    }

    @Override
    public ProcessingInstruction createProcessingInstruction(String target, String data) throws DOMException {
        return doc.createProcessingInstruction(target, data);
    }

    @Override
    public Attr createAttribute(String name) throws DOMException {
        return doc.createAttribute(name);
    }

    @Override
    public EntityReference createEntityReference(String name) throws DOMException {
        return doc.createEntityReference(name);
    }

    @Override
    public NodeList getElementsByTagName(String tagname) {
        return doc.getElementsByTagName(tagname);
    }

    @Override
    public Node importNode(Node importedNode, boolean deep) throws DOMException {
        return doc.importNode(importedNode, deep);
    }

    @Override
    public Element createElementNS(String namespaceURI, String qualifiedName) throws DOMException {
        return doc.createElementNS(namespaceURI, qualifiedName);
    }

    @Override
    public Attr createAttributeNS(String namespaceURI, String qualifiedName) throws DOMException {
        return doc.createAttributeNS(namespaceURI, qualifiedName);
    }

    @Override
    public NodeList getElementsByTagNameNS(String namespaceURI, String localName) {
        return doc.getElementsByTagNameNS(namespaceURI, localName);
    }

    @Override
    public Element getElementById(String elementId) {
        return doc.getElementById(elementId);
    }

    @Override
    public String getInputEncoding() {
        return doc.getInputEncoding();
    }

    @Override
    public String getXmlEncoding() {
        return doc.getXmlEncoding();
    }

    @Override
    public boolean getXmlStandalone() {
        return doc.getXmlStandalone();
    }

    @Override
    public void setXmlStandalone(boolean xmlStandalone) throws DOMException {
        doc.setXmlStandalone(xmlStandalone);
    }

    @Override
    public String getXmlVersion() {
        return doc.getXmlVersion();
    }

    @Override
    public void setXmlVersion(String xmlVersion) throws DOMException {
        doc.setXmlVersion(xmlVersion);
    }

    @Override
    public boolean getStrictErrorChecking() {
        return doc.getStrictErrorChecking();
    }

    @Override
    public void setStrictErrorChecking(boolean strictErrorChecking) {
        doc.setStrictErrorChecking(strictErrorChecking);
    }

    @Override
    public String getDocumentURI() {
        return doc.getDocumentURI();
    }

    @Override
    public void setDocumentURI(String documentURI) {
        doc.getDocumentURI();
    }

    @Override
    public Node adoptNode(Node source) throws DOMException {
        return doc.adoptNode(source);
    }

    @Override
    public DOMConfiguration getDomConfig() {
        return doc.getDomConfig();
    }

    @Override
    public void normalizeDocument() {
        doc.normalizeDocument();
    }

    @Override
    public Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException {
        return doc.renameNode(n, namespaceURI, qualifiedName);
    }

    @Override
    public String getNodeName() {
        return doc.getNodeName();
    }

    @Override
    public String getNodeValue() throws DOMException {
        return doc.getNodeValue();
    }

    @Override
    public void setNodeValue(String nodeValue) throws DOMException {
        doc.setNodeValue(nodeValue);
    }

    @Override
    public short getNodeType() {
        return doc.getNodeType();
    }

    @Override
    public Node getParentNode() {
        return doc.getParentNode();
    }

    @Override
    public NodeList getChildNodes() {
        return doc.getChildNodes();
    }

    @Override
    public Node getFirstChild() {
        return doc.getFirstChild();
    }

    @Override
    public Node getLastChild() {
        return doc.getLastChild();
    }

    @Override
    public Node getPreviousSibling() {
        return doc.getPreviousSibling();
    }

    @Override
    public Node getNextSibling() {
        return doc.getNextSibling();
    }

    @Override
    public NamedNodeMap getAttributes() {
        return doc.getAttributes();
    }

    @Override
    public Document getOwnerDocument() {
        return doc.getOwnerDocument();
    }

    @Override
    public Node insertBefore(Node newChild, Node refChild) throws DOMException {
        return doc.insertBefore(newChild, refChild);
    }

    @Override
    public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
        return doc.replaceChild(newChild, oldChild);
    }

    @Override
    public Node removeChild(Node oldChild) throws DOMException {
        return doc.removeChild(oldChild);
    }

    @Override
    public Node appendChild(Node newChild) throws DOMException {
        return doc.appendChild(newChild);
    }

    @Override
    public boolean hasChildNodes() {
        return doc.hasChildNodes();
    }

    @Override
    public Node cloneNode(boolean deep) {
        return doc.cloneNode(deep);
    }

    @Override
    public void normalize() {
        doc.normalize();
    }

    @Override
    public boolean isSupported(String feature, String version) {
        return doc.isSupported(feature, version);
    }

    @Override
    public String getNamespaceURI() {
        return doc.getNamespaceURI();
    }

    @Override
    public String getPrefix() {
        return doc.getPrefix();
    }

    @Override
    public void setPrefix(String prefix) throws DOMException {
        doc.setPrefix(prefix);
    }

    @Override
    public String getLocalName() {
        return doc.getLocalName();
    }

    @Override
    public boolean hasAttributes() {
        return doc.hasAttributes();
    }

    @Override
    public String getBaseURI() {
        return doc.getBaseURI();
    }

    @Override
    public short compareDocumentPosition(Node other) throws DOMException {
        return doc.compareDocumentPosition(other);
    }

    @Override
    public String getTextContent() throws DOMException {
        return doc.getTextContent();
    }

    @Override
    public void setTextContent(String textContent) throws DOMException {
        doc.setTextContent(textContent);
    }

    @Override
    public boolean isSameNode(Node other) {
        return doc.isSameNode(other);
    }

    @Override
    public String lookupPrefix(String namespaceURI) {
        return doc.lookupPrefix(namespaceURI);
    }

    @Override
    public boolean isDefaultNamespace(String namespaceURI) {
        return doc.isDefaultNamespace(namespaceURI);
    }

    @Override
    public String lookupNamespaceURI(String prefix) {
        return doc.lookupNamespaceURI(prefix);
    }

    @Override
    public boolean isEqualNode(Node arg) {
        return doc.isEqualNode(arg);
    }

    @Override
    public Object getFeature(String feature, String version) {
        return doc.getFeature(feature, version);
    }

    @Override
    public Object setUserData(String key, Object data, UserDataHandler handler) {
        return doc.setUserData(key, data, handler);
    }

    @Override
    public Object getUserData(String key) {
        return doc.getUserData(key);
    }
}