All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.aemds.guide.utils.XMLUtils Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2014 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/

package com.adobe.aemds.guide.utils;

import com.adobe.aemds.guide.service.GuideException;
import com.adobe.forms.common.service.FormDataXMLProviderRegistry;
import com.adobe.forms.foundation.util.ContentConverterUtils;
import com.adobe.granite.resourceresolverhelper.ResourceResolverHelper;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.NonExistingResource;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.*;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.xml.namespace.QName;
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.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 javax.xml.XMLConstants;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

/**
 * @pad.exclude Exclude from Published API.
 */
public class XMLUtils {

    private static Logger logger = LoggerFactory.getLogger(XMLUtils.class);

    /**
     * @pad.exclude Exclude from Published API.
     */
    public static boolean checkIfStringHasIndexOperator(String bindRef) {
        boolean bContainsIndex = false;
        // Since XML cannot have a node name with "[" in it
        // Also, as of now we support only constant inside [] operator, doesn't make to support conditional's inside []
        if(StringUtils.contains(bindRef, "[")){
            bContainsIndex = true;
        }
        return bContainsIndex;
    }

    public static String extractXsdRootElement(JSONObject guideJson) {
        String xsdRootEl = "";
        try {
            if(guideJson.has(GuideConstants.XSD_ROOT_EL_LOCAL_NAME)){
                xsdRootEl = guideJson.getString(GuideConstants.XSD_ROOT_EL_LOCAL_NAME);
            } else {
                logger.debug("Unable to find xsdRootElement in guide JSON");
            }
        } catch (JSONException ex) {
            logger.debug("Unable to read xsdRootElement from guide JSON", ex);
        }
        return xsdRootEl;
    }

    /**
     * Returns the normalized bindref for XSD binded field. It would return the bindRef relative to root node of data document.
     * For example if bindRef is /a/b/c/d then it would return b/c/d as /a would always bind to document root.
     * @param jsonObject
     * @throws JSONException
     * @pad.exclude Exclude from Published API.
     */
    public static String getXSDRootBindRef(JSONObject jsonObject, String xsdRoot) throws JSONException {
        String effectiveBindRef = "";
        if (isXsd(jsonObject)) {
            effectiveBindRef = getRelativeXpath(jsonObject.optString(GuideConstants.BIND_REF, ""), xsdRoot);
            //TODO:Check case when root XSD element is directly dragged
        }
        return effectiveBindRef;
    }

    public static String getRelativeXpath(String bindRef, String root) {
        String xpath = "";
        if (StringUtils.isNotBlank(bindRef))
        {
            xpath = bindRef;
            if ((isValidXsdRoot(root)
                   && xpath.matches("/" + root + "(?:/.+)?"))
                || StringUtils.equals(root, GuideConstants.UNKNOWN_XSD_ROOT_ELEMENT))
            {
                xpath = xpath.replaceFirst("^/[^/]+", "");  // drop 1st part from xpath
            }
        }
        return StringUtils.removeStart(xpath, "/");
    }

    /**
     *
     * @param doc
     * @pad.exclude Exclude from Published API.
     */
    public static String getXMLfromXsdDom(Element doc) {
        return getXMLfromXsdDom(doc, false);
    }

    /**
     *
     * @param doc
     * @param omitXmlDeclaration
     * @pad.exclude Exclude from Published API.
     */
    public static String getXMLfromXsdDom(Element doc, Boolean omitXmlDeclaration) {
        // CQ-4263698 : Replacing LSSerializer with Transformer for serializing xml
        // due to bug in default LSSerializer implementation since jdk 9
        // that misses "xmlns" attribute while serializing a DOM Element to xml
        String xmlStr = "";
        try {
            if (doc != null) {
                TransformerFactory transfac = TransformerFactory.newInstance();
                transfac.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

                Transformer trans = transfac.newTransformer();
                trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, omitXmlDeclaration ? "yes" : "no");
                trans.setOutputProperty(OutputKeys.METHOD, "xml");
                trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

                StringWriter sw = new StringWriter();
                StreamResult result = new StreamResult(sw);
                DOMSource source = new DOMSource(doc);

                trans.transform(source, result);
                xmlStr = sw.toString();
            }
        } catch (Exception e) {
            throw new GuideException(e);
        }
        return xmlStr;
    }

    /**
     * Utility to add child nodes of parent directly under root node. Parent and root nodes belong to different documents.
     * @param parent node whose child nodes are to added under root node
     * @param root node under which child nodes have to be directly inserted
     * @pad.exclude Exclude from Published API.
     */
    public static void addChildNodesOfParentUnderRoot(Node parent, Node root) {
        // Walk through the child nodes of parent and add them as child nodes of root
        Document document = root.getOwnerDocument();
        NodeList children = parent.getChildNodes();
        int length = children.getLength();
        for (int i = 0; i < length; i++) {
            Node child = children.item(i);
            if(child.getNodeType() == Node.ELEMENT_NODE) {
                root.appendChild(document.importNode(child, true));
            }
        }
    }

    /**
     * Returns the DOR part of data XML
     * Dor Part is:
     * a) ExcludeFromDorIfHidden Tag
     * b) Unbound part of guide data
     * c) bound part of guide data
     * d) afSubmissionInfo of guide data
     * @param dorDoc document representing dor
     * @param doc document representing the data xml
     * @param excludeFromDorIfHidden
     * @param bSetXfaNameSpace this is true if xdpRef is not present
     * @param guideJson
     * @return string representing the data xml
     * @see org.w3c.dom.Document
     *
     * @pad.exclude Exclude from Published API.

     */
    public static String getDorDataXmlPart(Document dorDoc, Document doc, String excludeFromDorIfHidden, boolean bSetXfaNameSpace, JSONObject guideJson) {
        try {
            Node root = null;
            Element nakedBoundXml = getBoundDataXmlElement(doc);
            Element nakedUnBoundXml = getUnboundDataXmlElement(doc);

            if (nakedBoundXml != null) {
                root = dorDoc.importNode(nakedBoundXml, false);
            } else if (nakedUnBoundXml != null) {
                root = dorDoc.importNode(nakedUnBoundXml, false);
            } else {
                root = dorDoc.createElement("data");
            }

            if (isWrappedXml(doc)) {
                if (nakedUnBoundXml != null) {
                    // remove data tag and make it flat
                    addChildNodesOfParentUnderRoot(nakedUnBoundXml, root);
                }

                if (nakedBoundXml != null) {
                    // remove data tag and keep it flat
                    addChildNodesOfParentUnderRoot(nakedBoundXml, root);
                }
            } else {
                root = dorDoc.importNode(doc.getDocumentElement(), true);
            }

            if (bSetXfaNameSpace) {
                setXfaNameSpace((Element) root);
                setXmlSchemaInstanceNamespace((Element) root);
            }

            Element nakedExcludeFromDoRXml = getSubmissionInfoDataXmlElement(doc, GuideConstants.EXCLUDE_FROM_DOR);
            if(nakedExcludeFromDoRXml != null){
                root.appendChild(dorDoc.importNode(nakedExcludeFromDoRXml, true));
            }

            Element nakedStateOverrideXml = getSubmissionInfoDataXmlElement(doc, GuideConstants.STATE_OVERRIDE);
            // only set nakedstateoverride for non xfa based form, bSetXfaNameSpace is true if no xdp ref is present
            if(nakedStateOverrideXml != null && bSetXfaNameSpace){
                nakedStateOverrideXml.setAttribute(GuideConstants.XFA_DATANODE, GuideConstants.DATAGROUP);
                root.appendChild(dorDoc.importNode(nakedStateOverrideXml, true));
            }

            handleXfaDataNode((Element)root, guideJson, StringUtils.isNotEmpty(guideJson.optString(GuideConstants.XSD_ROOT_EL_LOCAL_NAME)));
            convertRTEValXfaCmplntHTML(root);
            return getXMLfromXsdDom((Element)root);
        } catch (Exception e) {
            throw new GuideException("Error in getting dor data xml "+e.getMessage(),e);
        }

    }

    private static void handleXfaDataNode(Element root, JSONObject guideJson, boolean schemaHasRoot) {
        try {
            Iterator iterator = guideJson.keys();
            while (iterator.hasNext()) {
                String key = iterator.next();
                Object value = guideJson.get(key);

                if (value instanceof JSONObject) {
                    handleXfaDataNode(root, (JSONObject) value, schemaHasRoot);
                    continue;
                }

                if (StringUtils.equals(key, GuideConstants.GUIDE_NODE_CLASS)) {
                    String guideNodeClass = (String) value;
                    boolean isContainer = guideNodeClass.equals(GuideConstants.GUIDE_PANEL)
                            || guideNodeClass.equals(GuideConstants.ROOTPANEL_NODECLASS)
                            || guideNodeClass.equals(GuideConstants.GUIDE_TABLE)
                            || guideNodeClass.equals(GuideConstants.GUIDE_TABLE_ROW);

                    if (isContainer) {
                        String xPathExpression = null;

                        if (guideJson.has(GuideConstants.BIND_REF)) {
                            // bound panel - get xpath expression for it
                            // get node in xml corresponding to bindRef
                            xPathExpression = getXPathExprForBindRef(guideJson.getString(GuideConstants.BIND_REF), schemaHasRoot);
                        } else {
                            // unbound panel - use name to get xpath expression
                            // get node in xml corresponding to name - assume flat hierarchy
                            xPathExpression = guideJson.optString("name");
                        }

                        addDataNodeAttribute(xPathExpression, root, GuideConstants.DATAGROUP);
                    }
                }

                if (StringUtils.equals(key, GuideConstants.BIND_REF) && StringUtils.contains((String) value, "/text()")) {
                    // content binding
                    String xPathExpression = getXPathExprForBindRef(StringUtils.substringBefore((String) value, "/text()"), schemaHasRoot);
                    addDataNodeAttribute(xPathExpression, root, GuideConstants.DATAVALUE);
                }
            }

        } catch (JSONException e) {
            logger.error("Exception while parsing JSON ", e);
        } catch (XPathExpressionException e) {
            logger.error("Exception while adding data node attribute in XML " + e);
        }
    }

    private static String getXPathExprForBindRef(String bindRef, Boolean schemaHasRoot) {
        String xPathExpression = null;
        if (StringUtils.isNotEmpty(bindRef)) {
            if (!schemaHasRoot) {
                bindRef = "/data" + bindRef;
            }

            int index = bindRef.indexOf('/', 1);
            xPathExpression = index != -1 ? StringUtils.substring(bindRef, index + 1) : null;
        }

        return xPathExpression;
    }

    private static void addDataNodeAttribute(String xPathExpression, Element element, String attributeValue)
            throws XPathExpressionException {
        XPath xPath = XPathFactory.newInstance().newXPath();

        NodeList targetNodes = null;
        if (StringUtils.isNotEmpty(xPathExpression)) {
            targetNodes = (NodeList) xPath.evaluate(xPathExpression, element, XPathConstants.NODESET);
        }

        if (targetNodes != null) {
            int length = targetNodes.getLength();
            for (int i = 0; i < length; i++) {
                ((Element)targetNodes.item(i)).setAttribute(GuideConstants.XFA_DATANODE, attributeValue);
            }
        }
    }

    /**
     * Returns the html compliant dor xml.
     * @param node Node node representing the dor xml.
     * @return void
     * @see org.w3c.dom.Node
     *
     * @pad.exclude Exclude from Published API.
     */
    public static void convertRTEValXfaCmplntHTML(Node node) {
        try {
            XPath xPath = XPathFactory.newInstance().newXPath();
            //get all the body nodes from node.
            NodeList rteNodes = (NodeList) xPath.evaluate("//body", node, XPathConstants.NODESET);
            for (int i = 0 ; i < rteNodes.getLength() ; i++) {
                Node rteNode = rteNodes.item(i);
                //convert nodes to string and pass to the converter util and again convert to nodelist to append to parent node.
                if (GuideConstants.XML_HTML_NAMESPACE.equals(rteNode.getAttributes().getNamedItem(GuideConstants.XML_NAMESPACE_ATTR).getNodeValue())) {
                    // body tag has to go to the convertToXFAHTML api, hence sending the parent node of rte
                    // since getStringFromNode API walks through all the child nodes
                    String rteString = getStringFromNode(rteNode.getParentNode(), true); // omit whitespace nodes
                    if(StringUtils.isNotBlank(rteString)) {
                        // if there are new lines remove them manually before calling converter utility
                        String resolvedRT = rteString.replaceAll("\r\n", "").replaceAll("\n", "");
                        NodeList modifiedRteNodeList = getNodeListFromXmlString(ContentConverterUtils.convertToXFAHTML(resolvedRT));
                        Node rteParentNode = rteNode.getParentNode();
                        rteParentNode.removeChild(rteNode);
                        appendNodeListToNode(rteParentNode, modifiedRteNodeList);
                    }
                }
            }
        } catch (Exception e) {
            throw new GuideException("Error in converting rte to html compliant xml."+e.getMessage(),e);
        }
        return ;
    }


    /**
     * Returns the Submission info part of data XML as element object
     * The returned element must be cloned to do further modification
     * @param doc document representing the data xml
     * @param submitInfoPart child tag of submission info if any, null would return the submission info tag of data xml
     * @return string representing the submission info element object
     * @see org.w3c.dom.Document
     * @pad.exclude Exclude from Published API.
     */
    public static Element getSubmissionInfoDataXmlElement(Document doc, String submitInfoPart) {
        try {

            //if the dataxml is wrapped, get the bound part
            if(isWrappedXml(doc)) {
                XPath xPath = XPathFactory.newInstance().newXPath();
                String xPathString = GuideConstants.AF_SUBMISSION_INFO;
                if(submitInfoPart != null && submitInfoPart.length() > 0) {
                    xPathString = xPathString + "/" + submitInfoPart;
                } else {
                    xPathString = xPathString + "/*";
                }
                Node afSubmissionInfo = (Node) xPath.evaluate(xPathString, doc.getDocumentElement(), XPathConstants.NODE);
                return (Element)afSubmissionInfo;
            } else {
                // todo: what if the doc is a naked xml ? do we need to add the excludeFromDoR tag in that case
                //if data xml is not already wrapped
                //doc.getDocumentElement();
                return null;
            }

        } catch (Exception e) {
            throw new GuideException("Error in getting bound xml part"+e.getMessage(),e);
        }
    }

    /**
     * Returns the submission info part of data XML
     * @param doc document representing the data xml
     * @param submitInfoPart child tag of submission info if any, null would return the submission info tag of data xml
     * @return string representing the submission info of data xml
     * @see org.w3c.dom.Document
     *
     * @pad.exclude Exclude from Published API.
     */
    public static String getSubmissionInfoDataXmlPart(Document doc, String submitInfoPart) {
        return getXMLfromXsdDom(getSubmissionInfoDataXmlElement(doc, submitInfoPart));

    }

    /**
     * Set XFA namespace to the element object passed
     * @param data  element object
     *
     * @pad.exclude Exclude from Published API.
     */
    public static void setXfaNameSpace(Element data){
        if(data != null){
            //Set namespace for xsd case
            data.setAttribute(GuideConstants.XML_NS_XFA_KEY, GuideConstants.XML_NS_XFA_VALUE);
        }
    }

    /**
     * Set XML Schema Instance namespace to the element object passed
     * @param data element object
     *
     * @pad.exclude Exclude from Published API.
     */
    public static void setXmlSchemaInstanceNamespace(Element data) {
        if (data != null) {
            //Set namespace for nillable attribute
            data.setAttribute(GuideConstants.XML_NS_SCHEMA_INSTANCE_KEY, GuideConstants.XML_NS_SCHEMA_INSTANCE_VALUE);
        }
    }


    /**
     * Returns the bound part(portion bound to schema XSD/XFA) of data XML
     * @param doc document representing the data xml
     * @return string representing the bound portion of data xml
     * @see org.w3c.dom.Document
     */
    public static String getBoundDataXmlPart(Document doc) {
        return getXMLfromXsdDom(getBoundDataXmlElement(doc));
    }

    /**
     * Returns the bound part(portion bound to schema XSD/XFA) of data XML as element object
     * @param doc document representing the data xml
     * @return Element object representing the bound portion of data xml
     * @see org.w3c.dom.Document
     *
     * @pad.exclude Exclude from Published API.
     */
    public static Element getBoundDataXmlElement(Document doc) {
        try {
            //if the dataxml is wrapped, get the bound part
            if(isWrappedXml(doc)) {
                XPath xPath = XPathFactory.newInstance().newXPath();
                Node afBoundData = (Node) xPath.evaluate(GuideConstants.WRAPPED_SUBMIT_BOUND_ROOT+"/*", doc.getDocumentElement(), XPathConstants.NODE);
                return (Element)afBoundData;
            } else {
                //if data xml is not already wrapped
                return doc.getDocumentElement();
            }

        } catch (Exception e) {
            throw new GuideException("Error in getting bound xml part"+e.getMessage(),e);
        }

    }

    /**
     *
     * Returns the unbound part of data XML
     * @param doc document representing the data xml
     * @return string representing the unbound portion of data xml
     * @see org.w3c.dom.Document
     *
     */
    public static String getUnboundDataXmlPart(Document doc) {
        return getXMLfromXsdDom(getUnboundDataXmlElement(doc));
    }

    /**
     *
     * Returns the unbound part of data XML as element object
     * @param doc document representing the data xml
     * @return Element object representing the unbound portion of data xml
     * @see org.w3c.dom.Document
     *
     * @pad.exclude Exclude from Published API.
     */
    public static Element getUnboundDataXmlElement(Document doc) {
        try {
            //if the dataxml is wrapped, get the un bound part
            if(isWrappedXml(doc)) {
                XPath xPath = XPathFactory.newInstance().newXPath();
                Node afUnboundData = (Node) xPath.evaluate(GuideConstants.WRAPPED_SUBMIT_UNBOUND_ROOT+"/*", doc.getDocumentElement(), XPathConstants.NODE);
                return (Element)afUnboundData;
            } else {
                //if data xml is not already wrapped
                return doc.getDocumentElement();
            }

        } catch (Exception e) {
            throw new GuideException("Error in getting unbound xml part"+e.getMessage(),e);
        }

    }
    
    /**
    *
    * Returns the signers part of data XML as element object
    * @param doc document representing the data xml
    * @return Element object representing the signers portion of data xml
    * @see org.w3c.dom.Document
    *
    * @pad.exclude Exclude from Published API.
    */
   public static Element getSignersDataXmlElement(Document doc) {
	   return getSubmissionInfoDataXmlElement(doc, GuideConstants.WRAPPED_SIGNERS_ROOT);
   }

    /**
     * Utility API to get a child node by name
     * @param node      node under which the given childName is to be found
     * @param childName name of the child to be found
     * @return  node representing the given child
     * @pad.exclude
     */
   public static Node getChildNodeByName(Node node, String childName){
       Node retNode = null;
       NodeList children = node.getChildNodes();
       int length = children.getLength();
       for (int i = 0; i < length; i++) {
           Node child = children.item(i);
           // check if afSubmissionInfo
           if(StringUtils.equals(child.getNodeName(), childName)) {
               retNode = child;
           }
       }
       return retNode;
   }

    private static void populateMap(Node data, Map map) {
        NodeList children = data.getChildNodes();
        int length = children.getLength();
        if (length > 1 && !isRichTextDataNode(children)) {
            for (int i = 1; i < length; i++) {
                populateMap(children.item(i), map);
            }
        } else {
            String value = data.getTextContent();
            if (StringUtils.isNotEmpty(value)) {
                map.put(data.getNodeName(), value);
            }
        }
    }

    /**
     * Returns Map of all the fields and their values
     * @param document representing the data xml
     * @return Map of all the fields and their values
     */
    public static Map getDataMap(Document document) {
        Map dataMap = new HashMap();
        Element unboundData = XMLUtils.getUnboundDataXmlElement(document);
        if (unboundData != null) {
            populateMap(unboundData, dataMap);
        }
        Element boundData = XMLUtils.getBoundDataXmlElement(document);
        if (boundData != null) {
            populateMap(boundData, dataMap);
        }
        return dataMap;
   }

    /**
     * Returns JSON representation of unbound part of Data XML
     * @param doc document representing the data xml
     * @return jsonobject of unbound part of data xml
     * @see org.w3c.dom.Document
     */
    public static  JSONObject getMapOfUnboundData(Document doc) {
        JSONObject xmlJSONObj = null;
        XPath xPath = XPathFactory.newInstance().newXPath();
        StringWriter stringWriter = new StringWriter();
        CustomJSONWriter jsonWriter = new CustomJSONWriter(stringWriter);

        try {
            Document unboundData = strToDoc(getUnboundDataXmlPart(doc));
            jsonWriter.object();
            jsonWriter.key(GuideConstants.UNWRAPPED_SUBMIT_ROOT).object();

            convertUnboundedNodeToJson(GuideConstants.UNWRAPPED_SUBMIT_ROOT, xPath, unboundData, jsonWriter);
            jsonWriter.endObject();
            jsonWriter.endObject();

            String jsonStr = stringWriter.toString();
            // the hidden \r\n in xmls served from win file systems creeps into multivalued fields
            xmlJSONObj  = new JSONObject(jsonStr);
        } catch(Exception e) {
            throw new GuideException("Error in getting map of unbound data"+e.getMessage(),e);
        }
        return xmlJSONObj;
    }

    private static void convertUnboundedNodeToJson(String rootNodeName, XPath xPath, Document unboundedXMLData, CustomJSONWriter jsonWriter) throws XPathExpressionException {
        NodeList nodes = (NodeList) xPath.evaluate(rootNodeName + "/*", unboundedXMLData, XPathConstants.NODESET);
        if (nodes != null && nodes.getLength() > 0) {
            for (int i = 0; i < nodes.getLength(); i++) {
                Element node = (Element) nodes.item(i);
                String nodeName = node.getNodeName();
                String nodeValue = null;
                String childNodeName = rootNodeName + "/" + nodeName;
                NodeList childrenNodes = (NodeList) xPath.evaluate(childNodeName + "/*", unboundedXMLData, XPathConstants.NODESET);
                /* Values could be rich Text XML, extract the Rich text Value as String from the document */
                if( childrenNodes.getLength() == 0 || isRichTextDataNode(childrenNodes) ) {
                    //single or rich text node case;
                    nodeValue = getXMLfromXsdDom((Element) xPath.evaluate(childNodeName + "/*", unboundedXMLData, XPathConstants.NODE),true);
                    if(nodeValue.length() == 0) {
                        nodeValue = node.getTextContent();
                    }
                    jsonWriter.key(nodeName).value(nodeValue);
                } else {
                    //Composite node case.
                    jsonWriter.key(nodeName).object();
                    convertUnboundedNodeToJson(childNodeName, xPath, unboundedXMLData, jsonWriter);
                    jsonWriter.endObject();
                }
            }
        }
    }

    private static boolean isRichTextDataNode(NodeList childrenNodes) {
        boolean isRichText = false;
        if( childrenNodes != null && childrenNodes.item(0) != null
                && GuideConstants.RICH_TEXT_FIRST_TAG_NAME.equalsIgnoreCase(childrenNodes.item(0).getNodeName()) ) {
            isRichText = true;
        }
        return isRichText;
    }

    /*
    * Returns the data xml of the bound part(portion bound to schema) of data xml.
    * Note: The returned data xml will not have the bound tag() in it
    * @param doc document representing the data xml
    * @see org.w3c.dom.Document
    */
    public static String getPrefillXmlWithoutBoundPart(Document doc) {
        try {
            if(isWrappedXml(doc)) {
                XPath xPath;
                xPath = XPathFactory.newInstance().newXPath();
                //NOCHECKMARX - No user input preventing XPath Injection. DataRef goes through prefill service to create doc.
                Node afBoundData = (Node) xPath.evaluate(GuideConstants.WRAPPED_SUBMIT_BOUND_ROOT, doc.getDocumentElement(), XPathConstants.NODE);
                if(afBoundData!=null) {
                    doc.getDocumentElement().removeChild(afBoundData);
                }
                return getXMLfromXsdDom((Element)doc.getDocumentElement());
            } else {
                return getXMLfromXsdDom((Element)doc.getDocumentElement());
            }
        } catch (Exception e) {
            throw new GuideException("Error in getting bound xml part"+e.getMessage(),e);
        }

    }


    /**
     * Returns the XML String of the Node whose tag name is provided from a Document object
     * @param doc Document Object where to search for the tag Name
     * @param tagName tag name of the XML Element whose String representation has to be returned
     * @pad.exclude Exclude from Published API.
     */
    public static String getXMLFromDom(Document doc, String tagName) {
        try {
            XPath xPath = XPathFactory.newInstance().newXPath();
            Node xmlNode = (Node) xPath.evaluate(tagName + "/*", doc.getDocumentElement(), XPathConstants.NODE);
            return getXMLfromXsdDom((Element) xmlNode);
        } catch(Exception e) {
            throw new GuideException("Error in Getting XML of the element <" + tagName + "> From DOM", e);
        }
    }

    /**
     * Get the Input Stream of the URI provided in dataRef.
     * @param dataRef
     * @param formDataXMLProviderRegistry
     * @param resourceResolverHelper
     * @throws Exception
     * @pad.exclude Exclude from Published API.
     * @deprecated
     */
    @Deprecated
    public static InputStream getDataRefInputStream(String dataRef,
                                                    FormDataXMLProviderRegistry formDataXMLProviderRegistry,
                                                    ResourceResolverHelper resourceResolverHelper) {
        String xml = null;
        try {
            if (dataRef.startsWith(GuideConstants.PROTOCOL_CRX)) {
                Resource fileResource = resourceResolverHelper.getResourceResolver().resolve(dataRef.substring(6));
                javax.jcr.Node jcrNode = fileResource.adaptTo(javax.jcr.Node.class);
                javax.jcr.Node jcrContent = jcrNode.getNode("jcr:content");

                InputStream is = jcrContent.getProperty("jcr:data").getBinary().getStream();
                return is;
            }
            /*
             * should use this code only if it is an unsupported protocol.
             */
            if (!(dataRef.startsWith(GuideConstants.PROTOCOL_HTTPS) || dataRef.startsWith(GuideConstants.PROTOCOL_HTTP) || dataRef.startsWith(GuideConstants.PROTOCOL_FILE))) {
                xml = formDataXMLProviderRegistry.getDataXMLFromService(dataRef);
                logger.info("[AEMForm] XML Recieved from Prefill Service = " + xml);
                if (xml != null && xml.length() > 0) {
                    return new ByteArrayInputStream(xml.getBytes("UTF-8"));
                }
                return null;
            } else {
            /*
             * to enable compatibility with the already working protocols
             * should be invoked if document could be formed as yet.
             */
                URL url = new URL(dataRef);
                InputStream in = url.openStream();
                return in;
            }
        } catch (PathNotFoundException e) {
            logger.error("[AEMForm] unable to locate Data XML in the repository " + dataRef + ": " + e.getMessage(), e);
        } catch (RepositoryException e) {
            logger.error("[AEMForm] Exception while reading Data XML in the repository " + dataRef + ": " + e.getMessage(), e);
        } catch (UnsupportedEncodingException e) {
            logger.error("[AEMForm] Unable to read xml using UTF-8 encoding for the " + dataRef + "," + xml + ": " + e.getMessage(), e);
        } catch (MalformedURLException e) {
            logger.error("[AEMForm] Malformed URL passed for getting the xml " + dataRef + ": " + e.getMessage(), e);
        } catch (IOException e) {
            logger.error("[AEMForm] Unable to read from the dataRef URL " + dataRef + ": " + e.getMessage(), e);
        }
        return null;
    }

    /**
     * @param dataRef, document builder and reference to the service
     * @param builder
     * @param formDataXMLProviderRegistry
     * @param resourceResolverHelper
     * @return document obtained from the service
     * @deprecated
     * @throws Exception
     * @pad.exclude Exclude from Published API.
     */
    @Deprecated
    public static Document exportDocumentFromDataRef(String dataRef,DocumentBuilder builder,
                 FormDataXMLProviderRegistry formDataXMLProviderRegistry,ResourceResolverHelper resourceResolverHelper)throws Exception{
        Document doc = null;
        if(dataRef.startsWith(GuideConstants.PROTOCOL_CRX)) {
            Resource fileResource = resourceResolverHelper.getResourceResolver().resolve(dataRef.substring(6));
            if (fileResource instanceof NonExistingResource) {
                return null;
            }
            javax.jcr.Node jcrNode = fileResource.adaptTo(javax.jcr.Node.class);
            javax.jcr.Node jcrContent = jcrNode.getNode("jcr:content");

            InputStream is = jcrContent.getProperty("jcr:data").getBinary().getStream();
            byte[] fileBytes = IOUtils.toByteArray(is);
            is.close();
            doc = builder.parse(new ByteArrayInputStream(fileBytes));
        }
        /*
         * should use this code only if it is an unsupported protocol.
         */
        else if(!(dataRef.startsWith(GuideConstants.PROTOCOL_HTTPS)||dataRef.startsWith(GuideConstants.PROTOCOL_HTTP)|| dataRef.startsWith(GuideConstants.PROTOCOL_FILE))){
            InputSource inputSource = new InputSource(new StringReader(formDataXMLProviderRegistry.getDataXMLFromService(dataRef)));
            doc= builder.parse(inputSource);
        }
        /*
         * to enable compatibility with the already working protocols
         * should be invoked if document could be formed as yet.
         * e.g. http, https and file protocol
         */
        if(doc==null){
            URL url = new URL(dataRef);
            InputStream in = url.openStream();
            doc = builder.parse(in);
            in.close();
        }
        return doc;
    }

    /*
     * This API adds a node to the XML document.
     * If a refNode is there then it adds the new node before the refNode.
     * If there is no refNode, then it adds the new node at the end.
     */
    private static void addNodeToDocument(Node currentNode, Node toAdd, Node refNode) {
        if (refNode != null) {
            currentNode.insertBefore(toAdd, refNode);
        } else {
            currentNode.appendChild(toAdd);
        }
    }

    /**
     *
     * @param dataDoc
     * @param xPath
     * @param node
     * @param path
     * @throws XPathExpressionException
     * @pad.exclude Exclude from Published API.
     */
    public static Node createNode(Document dataDoc, XPath xPath, Node node, String path) throws XPathExpressionException {
        return XMLUtils.createNode(dataDoc, xPath, node, path, null);
    }

    /**
     *
     * @param dataDoc
     * @param xPath
     * @param node
     * @param path
     * @param refNode
     * @throws XPathExpressionException
     * @pad.exclude Exclude from Published API.
     */
    public static Node createNode(Document dataDoc, XPath xPath, Node node, String path, Node refNode) throws XPathExpressionException {
        Document document = dataDoc;
        String[] pathTokens = path.split("/");
        Node currentNode = node;
        int pathTokensSize = pathTokens.length;
        for(int i = 0; i < pathTokensSize; i++){
            String pathToken = pathTokens[i];
            boolean isLastToken = (i == pathTokensSize -1);
            //NOCHECKMARX - No user input preventing XPath Injection.
            Node newNode = (Node) xPath.evaluate(pathToken, currentNode, XPathConstants.NODE);
            if(newNode == null){
                if (pathToken.charAt(0) == '@') {
                    //if current node to be created is attribute
                    Attr newAttribute = document.createAttribute(pathToken.substring(1));
                    ((Element)currentNode).setAttributeNode(newAttribute);
                    newNode = newAttribute;
                } else if ("text()".equals(pathToken)) {
                    //if current node to be created is text
                    //TODO: check what would happen in createTextNode is passed null
                    Node textNode = document.createTextNode(null);
                    XMLUtils.addNodeToDocument(currentNode, textNode, refNode);
                    newNode = textNode;
                } else {
                    //if current node to created is element
                    // check if pathToken has index
                    if(XMLUtils.checkIfStringHasIndexOperator(pathToken)){
                        // Please Note: In xpath, the index should start with 1 and not 0
                        // If index starts with 0, then XML wouldn't generate as desired
                        // Also, substring before should work since name of a node in xml cannot have "[" as per grammar
                        // Also other generic operator like "(" would not be present while writing data xml before "[" operator
                        pathToken = StringUtils.substringBefore(pathToken, "[");
                    }
                    Node elNode = document.createElement(pathToken);
                    XMLUtils.addNodeToDocument(currentNode, elNode, refNode);
                    newNode = elNode;
                }
            } else if (isLastToken && !(pathToken.charAt(0) == '@' || "text()".equals(pathToken))) {
                //if it is last token which is not an attribute or text, always force create it.
                Node elNode = document.createElement(pathToken);
                XMLUtils.addNodeToDocument(currentNode, elNode, refNode);
                newNode = elNode;
            }
            currentNode = newNode;
        }
        return currentNode;
    }

    /**
     *
     * @param xPath
     * @param currentNode
     * @param path
     * @pad.exclude Exclude from Published API.
     */
    public static void removeFromDocument(XPath xPath, Node currentNode, String path) {
        try {
            Node toDelete = (Node) xPath.evaluate(path, currentNode, XPathConstants.NODE);
            if(toDelete != null) {
                Node parentNode = toDelete.getParentNode();
                if(parentNode != null) {
                    parentNode.removeChild(toDelete);
                }
            }
        } catch (XPathExpressionException ex){
            logger.error("Invalid Xpath Syntax: " + path, ex);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    /**
     * Create XML tag for File attachment component
     *
     * @param document
     * @param fileComponentNode
     * @param jsonObject
     * @pad.exclude Exclude from Published API.
     */
    public static void createAttachmentTag(Document document, Node fileComponentNode, JSONObject jsonObject) {
        try {
            Node fileAttachments = document.createElement(GuideConstants.GUIDE_FILE_ATTACHMENT);
            fileComponentNode.appendChild(fileAttachments);
            fileAttachments.setTextContent(jsonObject.getString(GuideConstants.FILES));
            JSONObject items = jsonObject.getJSONObject(GuideConstants.ITEMS_NODENAME);
            if (items.has(GuideConstants.GUIDE_FILE_COMMENT)) {
                JSONObject commentsJson = items.getJSONObject(GuideConstants.GUIDE_FILE_COMMENT);
                if (commentsJson.has(GuideConstants._VALUE)) {
                    Node comments = document.createElement(GuideConstants.GUIDE_FILE_COMMENT);
                    comments.setTextContent(commentsJson.getString(GuideConstants._VALUE));
                    fileComponentNode.appendChild(comments);
                }
            }
        } catch (Exception e) {
            logger.error("Error in creating file attachment tag", e);
        }
    }

    /**
     *
     * @param dataDoc
     * @param xPath
     * @param currentNode
     * @param path
     * @param value
     * @return Node created at the specified path
     * @pad.exclude Exclude from Published API.
     */
    public static Node addToDocument(Document dataDoc, XPath xPath, Node currentNode, String path, Object value) {
        try {
            //NOCHECKMARX - No user input preventing XPath Injection.
            Node toUpdate = (Node) xPath.evaluate(path, currentNode, XPathConstants.NODE);
            if(toUpdate == null) {
                toUpdate = createNode(dataDoc, xPath, currentNode, path);
            }
            //update the value
            _updateValue(dataDoc, toUpdate, value);
            return toUpdate;
        } catch (XPathExpressionException ex) {
            logger.warn("Invalid Xpath Syntax: " + path, ex);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
        return null;
    }

    private static void _updateValue(Document dataDoc, Node toUpdate, Object value){
        switch (toUpdate.getNodeType()) {
            case Node.ATTRIBUTE_NODE:
                if(value instanceof String) {
                    ((Attr) toUpdate).setValue((String) value);
                }
                break;
            case Node.ELEMENT_NODE:
                if(value instanceof String) {
                    toUpdate.setTextContent((String) value);
                }
                else if(value instanceof Node) {
                    value = dataDoc.importNode((Node)value, true);
                    while (toUpdate.hasChildNodes()) {
                        toUpdate.removeChild(toUpdate.getFirstChild());
                    }
                    toUpdate.appendChild((Node)value);
                }
                else {
                    logger.debug("Unsupported type of value");
                }
                break;
            case Node.TEXT_NODE:
                if(value instanceof String) {
                    toUpdate.setNodeValue((String) value);
                }
                break;
            default:
                break;

        }
    }

    /**
     * This API adds a node in the document before the refNode
     *
     * @param dataDoc
     * @param xPath
     * @param currentNode
     * @param path
     * @param value
     * @param refNode reference node with respect to which the new node is to be inserted
     * @return Node created at the specified path
     * @pad.exclude Exclude from Published API.
     */
    public static Node addToDocument(Document dataDoc, XPath xPath, Node currentNode, String path, Object value, Node refNode) {
        try {
            //NOCHECKMARX - No user input preventing XPath Injection.
            Node toUpdate = (Node) xPath.evaluate(path, currentNode, XPathConstants.NODE);
            if (toUpdate == null) {
                toUpdate = createNode(dataDoc, xPath, currentNode, path, refNode);
            }
            //update the value
            _updateValue(dataDoc, toUpdate, value);
            return toUpdate;
        } catch (XPathExpressionException ex){
            logger.warn("Invalid Xpath Syntax: " + path, ex);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
        return null;
    }

    public static boolean isXsd(JSONObject obj) {
        String bindRef = obj.optString(GuideConstants.BIND_REF);
        return StringUtils.isNotBlank(bindRef) && StringUtils.startsWith(bindRef, "/");
    }

    public static boolean isWrappedXml(Document doc) {
        try {
            return (GuideConstants.WRAPPED_SUBMIT_XML_ROOT.equals(doc.getDocumentElement().getTagName()));
        } catch (Exception e) {
            logger.debug("Error while trying to check document root tag : " + e.getMessage(), e);
        }
        return false;
    }

    public static boolean isWrappedXmlStr(String docStr) {
        if (StringUtils.isNotBlank(docStr)) {
            try {
                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                XMLUtils.disableExternalEntities(dbf);
                DocumentBuilder db = dbf.newDocumentBuilder();
                Document doc = db.parse(new InputSource(new StringReader(docStr)));
                return isWrappedXml(doc);

            } catch (SAXException e) {
                logger.error("Error while parsing Guide Prefill Xml String : " + e.getMessage(), e);
            } catch (IOException e) {
                logger.error("Error while parsing Guide Prefill Xml String : " + e.getMessage(), e);
            } catch (ParserConfigurationException e) {
                logger.error("Error while parsing Guide Prefill Xml String : " + e.getMessage(), e);
            }
        }
        return false;
    }

    public static boolean isValidXsdRoot(String root) {
        return StringUtils.isNotBlank(root) && root.matches("[a-zA-Z].*");
    }

    public static final String xmlSkeleton = "\n" +
            "\n" +
            "  \n" +
            "    \n" +
            "  \n" +
            "  \n" +
            "    \n" +
            "  \n" +
            "\n";

    public static Document getEmptySubmitDoc() {
        Document doc = strToDoc(xmlSkeleton);
        if(!(doc instanceof Document)) {
            logger.debug("Unable to Generate Empty Xml Doc");
            return null;
        }
        return doc;
    }

    public static String getChildBoundRootXpath(String fsRoot, String bindRefPrefix, String childRoot) {
        String xpath = "";
        if (StringUtils.isBlank(bindRefPrefix))
        {
            if (isValidXsdRoot(childRoot)
                && !StringUtils.equals(fsRoot, childRoot))
            {
                xpath = childRoot;
            }
        } else {
            xpath = getRelativeXpath(bindRefPrefix, fsRoot);
        }
        return xpath;
    }

    /**
     * Returns list of child and attribute nodes which have passed name
     * @param parent node whose children and attributes are searched
     * @param childNode name of child to be returned
     * @return list of child nodes having childNode as name
     */
    public static List getNamedChildNodes(Node parent, String childNode) {
        List nodes = new ArrayList<>();
        if (parent == null || StringUtils.isEmpty(childNode)) {
            return nodes;
        }

        NodeList children = parent.getChildNodes();
        int count = children.getLength();
        for (int i = 0; i < count; i++) {
            Node node = children.item(i);
            if (StringUtils.equals(childNode, node.getNodeName())) {
                nodes.add(node);
            }
        }

        NamedNodeMap attributeMap = parent.getAttributes();
        Node attributeNode = attributeMap.getNamedItem(childNode);
        if (attributeNode != null) {
            nodes.add(attributeNode);
        }

        return nodes;
    }

    public static Object evaluateXPath(String xPathQuery, Node contextNode, QName type) {
        try {
            if (StringUtils.isBlank(xPathQuery)) {
                return contextNode;
            }
            XPathFactory xpFactory = XPathFactory.newInstance();
            XPath xPath = xpFactory.newXPath();
            return xPath.evaluate(xPathQuery, contextNode, type);
        } catch (XPathExpressionException e) {
            logger.debug("Unable to evaluate XPath" + xPathQuery, e);
        }
        return null;
    }

    public static void copyChildren(Node source, Node target) {
        if (source == null || target == null) return;

        Document destination = target.getOwnerDocument();
        DocumentFragment container = destination.createDocumentFragment();
        NodeList children = source.getChildNodes();
        int length = children.getLength();
        for (int i = 0; i < length; i++) {
            container.appendChild(destination.importNode(children.item(i), true));
        }

        target.appendChild(container);
    }

    public static Document getChildXmlDoc(Element fsUnBoundDataRoot, Element fsBoundDataRoot, String childBoundRootXpath, boolean keepBoundPart) {
        Document childDoc = getEmptySubmitDoc();

        copyChildren(fsUnBoundDataRoot, getUnboundDataXmlElement(childDoc)); // copy unbound data

        if (keepBoundPart) {
            Element childBoundRoot = (Element) evaluateXPath(childBoundRootXpath, fsBoundDataRoot, XPathConstants.NODE);
            if (childBoundRoot == null) {
                childBoundRoot = fsBoundDataRoot;
            }
            copyChildren(childBoundRoot, getBoundDataXmlElement(childDoc));  // copy bound data
        }

        return childDoc;
    }

    private static String getStringFromNode(Node node) {
        return getStringFromNode(node, false);
    }

    private static String getStringFromNode(Node node, boolean ignoreWhitespace) {
        DOMImplementationLS domImplementation = (DOMImplementationLS)node.getOwnerDocument().getImplementation();
        LSSerializer lsSerializer = domImplementation.createLSSerializer();
        // set the xml declaration to false
        lsSerializer.getDomConfig().setParameter("xml-declaration", false);
        NodeList childNodes = node.getChildNodes();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node child = childNodes.item(i);
            if (!ignoreWhitespace || !isWhitespaceNode(child)) {
                sb.append(lsSerializer.writeToString(child));
            }
        }

        return sb.toString();
    }

    private static boolean isWhitespaceNode(Node node) {
        if (node.getNodeType() == Node.TEXT_NODE) {
            String val = node.getNodeValue();
            return val.trim().length() == 0;
        } else {
            return false;
        }
    }

    /**
     * convert node object to string.
     * @param node Node to be converted to string.
     * @param omitXmlDeclaration Boolean to check if xml declaration tag should be there in converted string or not.
     * @throws Exception
     * @pad.exclude Exclude from Published API.
     */
    public static String nodeToStr(Node node, Boolean omitXmlDeclaration) {
        String xmlStr = "";
        try {
            if (node != null) {
                TransformerFactory transfac = TransformerFactory.newInstance();
                Transformer trans = transfac.newTransformer();
                trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, omitXmlDeclaration ? "yes" : "no");
                trans.setOutputProperty(OutputKeys.METHOD, "xml");
                trans.setOutputProperty(OutputKeys.INDENT, "yes");
                trans.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", Integer.toString(2));

                StringWriter sw = new StringWriter();
                StreamResult result = new StreamResult(sw);
                DOMSource source = new DOMSource(node);

                trans.transform(source, result);
                xmlStr = sw.toString();
            }
        } catch (Exception e) {
            logger.error("Exception while converting document to xml", e);
        }
        return xmlStr;
    }

    /**
     * convert document object to string.
     * This api is deprecated and new api to be used is {@link #nodeToStr(Node, Boolean) nodeToStr}
     * @param doc Document to be converted to string.
     * @throws Exception
     * @pad.exclude Exclude from Published API.
     * @deprecated
     */
    @Deprecated
    public static String docToStr(Document doc) {
        String xmlStr = "";
        try {
            return nodeToStr(doc, false);
        } catch (Exception e) {
            logger.error("Exception while converting document to xml", e);
        }
        return xmlStr;
    }

    /**
     * This API converts xmlstring to node.
     *
     * @param xmlStr String xml String which needs to be converted to nodeList.
     * @return NodeList NodeList created converting xml string.
     * @pad.exclude Exclude from Published API.
     */
    public static NodeList getNodeListFromXmlString (String xmlStr) {
        NodeList nodeList = null;
        try {
            //This is done as xml wihthout root tag is unparsable by DocumentBuilder.
            String tempRootBeginTag = "";
            String tempRootEndTag = "";
            String rootAppendedString = tempRootBeginTag + xmlStr +tempRootEndTag;
            nodeList = DocumentBuilderFactory
                    .newInstance()
                    .newDocumentBuilder()
                    .parse(new ByteArrayInputStream(rootAppendedString.getBytes(GuideConstants.UTF_8)))
                    .getDocumentElement()
                    .getChildNodes();
        } catch (Exception e) {
            throw new GuideException("Error in getting node list from xml "+e.getMessage(),e);
        }
        return nodeList;
    };

    /**
     * This API adds a nodelist to the root node.
     *
     * @param rootNode Node root node to which the nodelist needs to be appended.
     * @param nodeList NodeList created converting xml string.
     * @return void
     * @pad.exclude Exclude from Published API.
     */
    public static void appendNodeListToNode (Node rootNode, NodeList nodeList) {
        Document doc = rootNode.getOwnerDocument();
        for (int j = 0 ; j < nodeList.getLength() ; j++) {
            rootNode.appendChild(doc.importNode(nodeList.item(j), true));
        }
        return;
    }

    public static Document strToDoc(String xmlStr) {
        Document doc = null;

        try {
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            XMLUtils.disableExternalEntities(dbFactory);
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            doc = dBuilder.parse(new InputSource(new StringReader(xmlStr)));
            doc.getDocumentElement().normalize();
        } catch (Exception e) {
            logger.debug("Unable to Generate Xml Doc from String : " + xmlStr, e);
        }

        return doc;
    }

    public static List extractAttachmentNames(Document doc) {
        List attachmentNames = new ArrayList();
        NodeList attachments = (NodeList) evaluateXPath("//" + GuideConstants.GUIDE_FILE_ATTACHMENT, doc.getDocumentElement(), XPathConstants.NODESET);

        if (attachments != null) {
            int length = attachments.getLength();
            for (int i = 0; i < length; i++) {
                Element attachment = (Element) attachments.item(i);
                String combinedNames = attachment.getTextContent();
                if (StringUtils.isNotBlank(combinedNames)) {
                    String[] fileNames = combinedNames.split("\\r?\\n");
                    if (fileNames != null && fileNames.length>0) {
                        attachmentNames.addAll(Arrays.asList(fileNames));
                    }
                }
            }
        }
        return attachmentNames;
    }

    public static void disableExternalEntities(DocumentBuilderFactory factory){
        String FEATURE;
        try {
            factory.setValidating(false);
            FEATURE = "http://xml.org/sax/features/validation";
            factory.setFeature(FEATURE, false);
            FEATURE = "http://xml.org/sax/features/external-general-entities";
            factory.setFeature(FEATURE, false);
            FEATURE = "http://xml.org/sax/features/external-parameter-entities";
            factory.setFeature(FEATURE, false);
            FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
            factory.setFeature(FEATURE, false);
        }
        catch (ParserConfigurationException e) {
            logger.warn("Error in disabling external entities ", e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy