/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.apache.xalan.xslt.util;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.IntStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;

import org.apache.xalan.templates.Constants;
import org.apache.xalan.templates.ElemFunction;
import org.apache.xalan.templates.ElemTemplate;
import org.apache.xalan.templates.Stylesheet;
import org.apache.xalan.templates.StylesheetRoot;
import org.apache.xalan.templates.TemplateList;
import org.apache.xalan.templates.XMLNSDecl;
import org.apache.xalan.transformer.TransformerImpl;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMCursorIterator;
import org.apache.xml.dtm.DTMManager;
import org.apache.xml.serializer.CharacterMapConfig;
import org.apache.xml.utils.QName;
import org.apache.xml.utils.XMLString;
import org.apache.xpath.Expression;
import org.apache.xpath.ExpressionNode;
import org.apache.xpath.XPathCollationSupport;
import org.apache.xpath.XPathContext;
import org.apache.xpath.axes.LocPathIterator;
import org.apache.xpath.composite.XPathExprFunctionCallSuffix;
import org.apache.xpath.composite.XPathForExpr;
import org.apache.xpath.composite.XPathSequenceConstructor;
import org.apache.xpath.functions.Function;
import org.apache.xpath.functions.XSL3FunctionService;
import org.apache.xpath.functions.XSLFunctionBuilder;
import org.apache.xpath.objects.ResultSequence;
import org.apache.xpath.objects.XBoolean;
import org.apache.xpath.objects.XBooleanStatic;
import org.apache.xpath.objects.XMLNodeCursorImpl;
import org.apache.xpath.objects.XNumber;
import org.apache.xpath.objects.XObject;
import org.apache.xpath.objects.XPathArray;
import org.apache.xpath.objects.XString;
import org.apache.xpath.objects.XdmAttributeItem;
import org.apache.xpath.objects.XdmNamespaceItem;
import org.apache.xpath.operations.Operation;
import org.apache.xpath.operations.Range;
import org.apache.xpath.operations.SimpleMapOperator;
import org.apache.xpath.operations.Variable;
import org.apache.xpath.patterns.NodeTest;
import org.apache.xpath.types.DateTimeUtil;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;

import xml.xpath31.processor.types.XSAnyAtomicType;
import xml.xpath31.processor.types.XSAnyType;
import xml.xpath31.processor.types.XSBoolean;
import xml.xpath31.processor.types.XSDateTime;
import xml.xpath31.processor.types.XSDecimal;
import xml.xpath31.processor.types.XSDouble;
import xml.xpath31.processor.types.XSNumericType;
import xml.xpath31.processor.types.XSUntyped;
import xml.xpath31.processor.types.XSUntypedAtomic;

/**
 * A class definition, that has few utility methods that provide 
 * support for Xalan-J's XSLT 3.0 transformation processor implementation.
 * 
 * @author Mukul Gandhi <mukulg@apache.org>
 * 
 * @xsl.usage advanced
 */
public class XslTransformEvaluationHelper {
    
    /**
     * Given an xdm input sequence, expand that sequence to produce a new sequence
     * none of whose items are sequence or array with cardinality greater than one.
     * 
     * The caller of this method, needs to supply an xdm sequence to be expanded
     * as an argument, and another argument reference to get the result from this 
     * method.
     */
    public static void expandResultSequence(ResultSequence seqToBeExpanded, 
                                                                  ResultSequence result) {               
    	int size1 = seqToBeExpanded.size();
    	for (int idx = 0; idx < size1; idx++) {
          XObject seqItem = seqToBeExpanded.item(idx);
          if (seqItem instanceof ResultSequence) {
             expandResultSequence((ResultSequence)seqItem, result); 
          }
          if (seqItem instanceof XPathArray) {
             ResultSequence rSeq = ((XPathArray)seqItem).atomize();
             int size2 = rSeq.size();
             for (int idx2 = 0; idx2 < size2; idx2++) {
            	XObject xObject2 = rSeq.item(idx2);            	
            	result.add(xObject2);
             }
          }
          else {
             result.add(seqItem);
          }
       }
    }
    
    /**
     * Given an XPath expression string, replace XML namespace uri references within it,
     * with the corresponding declared XML namespace prefixes, using information from
     * the list object 'nsPrefixTable' passed to this method.
     */
    public static String replaceNsUrisWithPrefixesOnXPathStr(String xpathExprStr, 
                                                                          List<XMLNSDecl> nsPrefixTable) {
       String replacedXPathExprStr = xpathExprStr;
       
       int size1 = nsPrefixTable.size();
       for (int idx = 0; idx < size1; idx++) {
          XMLNSDecl xmlNSDecl = nsPrefixTable.get(idx);
          String prefix = xmlNSDecl.getPrefix();
          String uri = xmlNSDecl.getURI();
          replacedXPathExprStr = replacedXPathExprStr.replace(uri + ":", prefix + ":");
       }
       
       return replacedXPathExprStr; 
    }
    
    /**
     * Get prefix from XML namespace uri, declared within XSL transformation 
     * context. 
     */
    public static String getPrefixFromNsUri(String nsUri, List<XMLNSDecl> nsPrefixTable) {    	
    	String xmlSchemaNsPrefix = null;

    	if (nsUri != null) {
    		int size1 = nsPrefixTable.size();
    		for (int idx = 0; idx < size1; idx++) {
    			XMLNSDecl xmlNSDecl = nsPrefixTable.get(idx);
    			if (nsUri.equals(xmlNSDecl.getURI())) {
    				xmlSchemaNsPrefix = xmlNSDecl.getPrefix();
    				break;
    			}
    		}
    	}

    	return xmlSchemaNsPrefix; 
    }
    
    /**
     * Method definition, to get XML namespace uri from prefix, declared 
     * within XSL transformation context. 
     */
    public static String getNsUriFromPrefix(String prefix, List<XMLNSDecl> nsPrefixTable) {
    	
    	String result = null;

    	if (prefix != null) {    		
    		int size1 = nsPrefixTable.size();
    		for (int idx = 0; idx < size1; idx++) {
    			XMLNSDecl xmlNSDecl = nsPrefixTable.get(idx);
    			if (prefix.equals(xmlNSDecl.getPrefix())) {
    				result = xmlNSDecl.getURI();
    				break;
    			}
    		}
    	}

    	return result; 
    }
    
    /**
     * Method definition, to get string value of XPath 3.1 xdm object. 
     */
    public static String getStrVal(XObject xObj) {       
       
       String result = null;
       
       if (xObj instanceof XSDecimal) {    	  
    	  result = (((XSDecimal)xObj).getValue()).toPlainString();
    	  int i = result.indexOf('.');
    	  if (i > -1) {
    		  // Delete trailing 0's to RHS of decimal point, for string value
    		  String prefix = result.substring(0, i);
    		  String suffix = result.substring(i + 1);
    		  int suffixLength = suffix.length();
    		  int j = suffixLength;
    		  for (j = (suffixLength - 1); j > -1; j--) {
    			  if (suffix.charAt(j) != '0') {
    				  break; 
    			  }
    		  }    		
    		  suffix = suffix.substring(0, j + 1);
    		  result = (suffix.length() > 0) ? (prefix + "." + suffix) : prefix;
    	  }
       }
       else if (xObj instanceof XSDateTime) {
    	  result = DateTimeUtil.getXSDateTimeStrValue((XSDateTime)xObj);    	
       }
       else if (xObj instanceof XSAnyType) {
          result = ((XSAnyType)xObj).stringValue();    
       }
       else {
          result = xObj.str();  
       }
       
       return result;
    }
    
    /**
     * Add an xdm input item to result sequence, if that already doesn't exist within
     * the result sequence. 
     * @throws TransformerException 
     */
    public static void addItemToResultSequence(ResultSequence resultSeq, XObject inpItem, 
                                                                     boolean cardinalityCheck) throws TransformerException {
        if (cardinalityCheck) {
            if (resultSeq.size() == 0) {                     
                resultSeq.add(inpItem);    
            }
            else if (!contains(resultSeq, inpItem, null, null)) {
                resultSeq.add(inpItem);
            }   
        }
        else {
            resultSeq.add(inpItem);   
        }
    }
    
    /**
     * Add an xdm input item to result sequence, if that already doesn't exist within
     * the result sequence. 
     * @throws TransformerException 
     */
    public static void addItemToResultSequence(ResultSequence resultSeq, XObject inpItem, 
                                                                     boolean cardinalityCheck,
                                                                     String collationUri,
                                                                     XPathCollationSupport xpathCollationSupport) 
                                                                    		                  throws TransformerException {
        if (cardinalityCheck) {
            if (resultSeq.size() == 0) {                     
                resultSeq.add(inpItem);    
            }
            else if (!contains(resultSeq, inpItem, collationUri, xpathCollationSupport)) {
                resultSeq.add(inpItem);
            }   
        }
        else {
            resultSeq.add(inpItem);   
        }
    }
    
    /**
     * Given an XObject object instance, get its contents as a ResultSequence object.  
     */
    public static ResultSequence getResultSequenceFromXObject(XObject xObject, XPathContext xctxt) {        
        
        ResultSequence resultSeq = new ResultSequence();
        
        if (xObject instanceof XMLNodeCursorImpl) {
           XMLNodeCursorImpl nodeSet = (XMLNodeCursorImpl)xObject;
            
           DTMCursorIterator dtmIter = nodeSet.iterRaw();
           int nextNode;
           while ((nextNode = dtmIter.nextNode()) != DTM.NULL) {
              XMLNodeCursorImpl xdmNode = new XMLNodeCursorImpl(nextNode, xctxt);
              resultSeq.add(xdmNode);
           }
        }
        else if (xObject instanceof ResultSequence) {
           ResultSequence rSeq = (ResultSequence)xObject;
           
           int size1 = rSeq.size();
           for (int idx = 0; idx < size1; idx++) {
              resultSeq.add(rSeq.item(idx)); 
           }
        }
        else {
           resultSeq.add(xObject);
        }
        
        return resultSeq;
    }
    
    /**
     * Method definition, to convert an xdm sequence to a node set.
     */
    public static XMLNodeCursorImpl getXNodeSetFromResultSequence(ResultSequence resultSeq, DTMManager dtmMgr) {
        
        XMLNodeCursorImpl result = null;
        
        List<Integer> dtmNodeHandleList = new ArrayList<Integer>();
        
        int rSeqLength = resultSeq.size();        
        for (int idx = 0; idx < rSeqLength; idx++) {
           XObject xObj = resultSeq.item(idx);
           if (xObj instanceof XMLNodeCursorImpl) {
              int nodeDtmHandle = (((XMLNodeCursorImpl)xObj).iter()).nextNode();
              dtmNodeHandleList.add(nodeDtmHandle);
           }
           else if (xObj instanceof XdmAttributeItem) {
        	   XdmAttributeItem xdmAttributeItem = (XdmAttributeItem)xObj;        	           	   
        	   DTM dtm = dtmMgr.getXmlShallowDTMTree(null, xdmAttributeItem, null);
        	   int docNodeHandle = dtm.getDocument();
        	   int docElemHandle = dtm.getFirstChild(docNodeHandle);
        	   String attrNamespace = xdmAttributeItem.getAttrNodeNs();
        	   String localName = xdmAttributeItem.getAttrLocalName();
        	   int attNodeHandle = dtm.getAttributeNode(docElemHandle, attrNamespace, localName);
        	   dtmNodeHandleList.add(attNodeHandle);
           }
           else if (xObj instanceof XdmNamespaceItem) {
        	   XdmNamespaceItem xdmNamespaceItem = (XdmNamespaceItem)xObj;
        	   DTM dtm = dtmMgr.getXmlShallowDTMTree(null, null, xdmNamespaceItem);
        	   int docNodeHandle = dtm.getDocument();
        	   int docElemHandle = dtm.getFirstChild(docNodeHandle);
        	   String localName = xdmNamespaceItem.getNamespaceNodeName();
        	   int nsNodeHandle = dtm.getAttributeNode(docElemHandle, "http://www.w3.org/2000/xmlns/", localName);
        	   dtmNodeHandleList.add(nsNodeHandle);
           }
           else {
              break; 
           }
        }
        
        if (dtmNodeHandleList.size() == rSeqLength) {
           result = new XMLNodeCursorImpl(dtmNodeHandleList, dtmMgr);
        }
        
        return result; 
    }
    
    /**
     * Given a compiled XPath expression and an XPath context object, find
     * the sum of values of xdm items represented by the provided compiled 
     * XPath expression object. 
     */
    public static XNumber getSumOfValues(Expression expr, XPathContext xctxt) throws 
                                                                  javax.xml.transform.TransformerException {        
        double sum = 0.0;    
        
        if (expr instanceof Variable) {
           Variable xslVariable = (Variable)expr;
           XObject resultObj = xslVariable.execute(xctxt);           
           if (resultObj instanceof XMLNodeCursorImpl) {
        	   XMLNodeCursorImpl xmlNodeCursorImpl = (XMLNodeCursorImpl)resultObj;
        	   int pos;

        	   DTMCursorIterator nodes = xmlNodeCursorImpl.iter();

        	   while ((pos = nodes.nextNode()) != DTM.NULL) {
        		   DTM dtm = nodes.getDTM(pos);
        		   XMLString xmlStr = dtm.getStringValue(pos);

        		   if (xmlStr != null) {
        			   sum += xmlStr.toDouble();
        		   }
        	   }
           }
           else if (resultObj instanceof ResultSequence) {
        	   ResultSequence resultSeq = (ResultSequence)resultObj;
        	   sum = sumResultSequence(resultSeq);          
           }
        }
        else if (expr instanceof Function) {
           XObject resultObj = ((Function)expr).execute(xctxt);
           if (resultObj instanceof ResultSequence) {
              ResultSequence resultSeq = (ResultSequence)resultObj;
              sum = sumResultSequence(resultSeq);          
           }  
        }
        else if (expr instanceof XPathForExpr) {
           XPathForExpr forExpr = (XPathForExpr)expr;
           ResultSequence forExprResult = (ResultSequence)(forExpr.execute(xctxt));
           sum = sumResultSequence(forExprResult);
        }
        else if (expr instanceof XPathSequenceConstructor) {
           XPathSequenceConstructor simpleSeqConstructor = (XPathSequenceConstructor)expr;
           ResultSequence seqCtrEvalResult = (ResultSequence)(simpleSeqConstructor.
                                                                                execute(xctxt));
           sum = sumResultSequence(seqCtrEvalResult);
        }
        else if (expr instanceof SimpleMapOperator) {
           SimpleMapOperator simpleMapOperator = (SimpleMapOperator)expr;
           ResultSequence simpleMapOperatorResult = (ResultSequence)(simpleMapOperator.
                                                                                  execute(xctxt));
           sum = sumResultSequence(simpleMapOperatorResult);
        }
        else if (expr instanceof Range) {
        	Range rangeExpr = (Range)expr;
            ResultSequence simpleMapOperatorResult = (ResultSequence)(rangeExpr.execute(xctxt));
            sum = sumResultSequence(simpleMapOperatorResult);
        }
        else if (expr instanceof LocPathIterator) {
           int pos;
            
           DTMCursorIterator nodes = expr.asIterator(xctxt, xctxt.getCurrentNode());

           while ((pos = nodes.nextNode()) != DTM.NULL) {
              DTM dtm = nodes.getDTM(pos);
              XMLString xmlStr = dtm.getStringValue(pos);

              if (xmlStr != null) {
                 sum += xmlStr.toDouble();
              }
           }
           
           nodes.detach();
        }

        return new XNumber(sum);    
    }
    
    /**
     * Given a compiled XPath expression object and an XPath context object, find
     * the count of xdm items represented by the provided compiled XPath expression 
     * object.  
     */
    public static XNumber getSequenceItemCount(Expression expr, XPathContext xctxt) throws 
                                                                                  javax.xml.transform.TransformerException {
        int xdmSequenceSize = 0;
        
        if (expr instanceof Function) {
            XObject evalResult = ((Function)expr).execute(xctxt);
            if (evalResult instanceof XMLNodeCursorImpl) {
                xdmSequenceSize = ((XMLNodeCursorImpl)evalResult).getLength();   
            }
            else if (evalResult instanceof ResultSequence) {
               xdmSequenceSize = ((ResultSequence)evalResult).size();
            }
            else {
               // Here, evalResult is probably of types XSAnyAtomicType, XString, XNumber etc
          	   xdmSequenceSize = 1; 
            }
        }
        else if (expr instanceof Variable) {
           XObject evalResult = ((Variable)expr).execute(xctxt);
           if (evalResult instanceof XMLNodeCursorImpl) {
        	   xdmSequenceSize = ((XMLNodeCursorImpl)evalResult).getLength();   
           }
           else if (evalResult instanceof ResultSequence) {
        	   xdmSequenceSize = ((ResultSequence)evalResult).size();
           }
           else {
        	   // Here, evalResult is probably of types XSAnyAtomicType, XString, XNumber etc
        	   xdmSequenceSize = 1; 
           }
        }
        else if (expr instanceof XPathSequenceConstructor) {
           XPathSequenceConstructor simpleSeqConstructor = (XPathSequenceConstructor)expr;
           ResultSequence seqCtrEvalResult = (ResultSequence)(simpleSeqConstructor.
                                                                                execute(xctxt));
           xdmSequenceSize = seqCtrEvalResult.size();
        }
        else if (expr instanceof Expression) {
            if (expr instanceof Range) {
                ResultSequence resultSeq = (ResultSequence)(((Range)expr).execute(xctxt));
                xdmSequenceSize = resultSeq.size();
            }
            else if (expr instanceof XPathForExpr) {
                ResultSequence resultSeq = (ResultSequence)(((XPathForExpr)expr).execute(xctxt));
                xdmSequenceSize = resultSeq.size();   
            }
            else if (expr instanceof Operation) {
            	Operation opn1 = (Operation)expr;
            	Expression lOpn = opn1.getLeftOperand();
            	Expression rOpn = opn1.getRightOperand();            	
            	XObject lObj1 = lOpn.execute(xctxt);
            	boolean isLEmpty = false;
            	if (lObj1 instanceof ResultSequence) {
            		if (((ResultSequence)lObj1).size() == 0) {
            			isLEmpty = true;
            		}
            	}
            	else if (lObj1 instanceof XMLNodeCursorImpl) {
            		XMLNodeCursorImpl nodeRef1 = (XMLNodeCursorImpl)lObj1;
            		if (nodeRef1.getLength() == 0) {
            			isLEmpty = true;
            		}
            	}

            	XObject rObj1 = rOpn.execute(xctxt);
            	boolean isREmpty = false;
            	if (rObj1 instanceof ResultSequence) {
            		if (((ResultSequence)rObj1).size() == 0) {
            			isREmpty = true;
            		}
            	}
            	else if (rObj1 instanceof XMLNodeCursorImpl) {
            		XMLNodeCursorImpl nodeRef1 = (XMLNodeCursorImpl)rObj1;
            		if (nodeRef1.getLength() == 0) {
            			isREmpty = true;
            		}
            	}

            	if (isLEmpty || isREmpty) {
            		// If one or both of the LHS and RHS of an XPath binary 
            		// operation is empty, then count of result sequence is zero.            		
            		xdmSequenceSize = 0;
            	}
            	else {
            		XObject xObj1 = opn1.execute(xctxt);
            		if (xObj1 instanceof ResultSequence) {
            		   xdmSequenceSize = ((ResultSequence)xObj1).size(); 
            		}
            		else if (xObj1 instanceof XMLNodeCursorImpl) {
            		   xdmSequenceSize = ((XMLNodeCursorImpl)xObj1).getLength(); 
            		}
            		else {
            		   xdmSequenceSize = 1;	
            		}
            	}
            }
            else if (expr instanceof XPathExprFunctionCallSuffix) {
            	XPathExprFunctionCallSuffix xpathExprFunctionCallSuffix = (XPathExprFunctionCallSuffix)expr;
            	XObject xObject = xpathExprFunctionCallSuffix.execute(xctxt);            	
            	if (xObject instanceof ResultSequence) {
            	   xdmSequenceSize = ((ResultSequence)xObject).size(); 	
            	}
            }
            else {
                DTMCursorIterator nl = expr.asIterator(xctxt, xctxt.getCurrentNode());
                xdmSequenceSize = nl.getLength(); 
                nl.detach();
            }
        }
    
        return new XNumber((double)xdmSequenceSize);
    }
    
    /**
     * Check whether a 'ResultSequence' object, contains a specific xdm item.
     */
    public static boolean contains(ResultSequence resultSeq, XObject srch, String collationUri,
    		                                                                   XPathCollationSupport xpathCollationSupport) 
    		                                                                		               throws TransformerException {
       
       boolean isSeqContains = false;
       
       int size1 = resultSeq.size();
       for (int idx = 0; idx < size1; idx++) {
          XObject resultSeqItem = resultSeq.item(idx);
          if ((resultSeqItem instanceof XSUntyped) && (srch instanceof XSUntyped)) {
             if (((XSUntyped)resultSeqItem).equals((XSUntyped)srch, collationUri, xpathCollationSupport)) {
                 isSeqContains = true;
                 break;    
             }
          }
          else if ((resultSeqItem instanceof XSUntypedAtomic) && (srch instanceof XSUntypedAtomic)) {
              if (((XSUntypedAtomic)resultSeqItem).equals((XSUntypedAtomic)srch, collationUri, xpathCollationSupport)) {
                 isSeqContains = true;
                 break;    
              } 
          }
          else if ((resultSeqItem instanceof XSUntyped) && (srch instanceof XSUntypedAtomic)) {
              if (((XSUntyped)resultSeqItem).equals((XSUntypedAtomic)srch, collationUri, xpathCollationSupport)) {
                 isSeqContains = true;
                 break;    
              } 
          }
          else if ((resultSeqItem instanceof XSUntypedAtomic) && (srch instanceof XSUntyped)) {
              if (((XSUntypedAtomic)resultSeqItem).equals((XSUntyped)srch, collationUri, xpathCollationSupport)) {
                 isSeqContains = true;
                 break;    
              }
          }
          else if ((resultSeqItem instanceof XSNumericType) && (srch instanceof XSNumericType)) {
        	  // When comparing numeric values, collationUri is not used
        	  String lStr = ((XSNumericType)resultSeqItem).stringValue();
         	  XSDouble lDouble = new XSDouble(lStr);
         	  
         	  String rStr = ((XSNumericType)srch).stringValue();
        	  XSDouble rDouble = new XSDouble(rStr);
        	  
        	  if (lDouble.equals(rDouble)) {
        		 isSeqContains = true;
                 break;  
        	  }
          }
          else if ((resultSeqItem instanceof XSNumericType) && (srch instanceof XNumber)) {
        	  // When comparing numeric values, collationUri is not used
        	  String lStr = ((XSNumericType)resultSeqItem).stringValue();
         	  XSDouble lDouble = new XSDouble(lStr);
         	  
         	  double rdbl = ((XNumber)srch).num();
         	  XSDouble rDouble = new XSDouble(rdbl);
         	  
         	 if (lDouble.equals(rDouble)) {
        		isSeqContains = true;
                break;  
        	 }
          }
          else if ((resultSeqItem instanceof XNumber) && (srch instanceof XSNumericType)) {
        	 // When comparing numeric values, collationUri is not used
        	 double ldbl = ((XNumber)resultSeqItem).num();
          	 XSDouble lDouble = new XSDouble(ldbl);
          	  
          	 String rStr = ((XSNumericType)srch).stringValue();
         	 XSDouble rDouble = new XSDouble(rStr);
          	  
         	 if (lDouble.equals(rDouble)) {
          		isSeqContains = true;
                break;  
          	 } 
         }
         else if ((resultSeqItem instanceof XNumber) && (srch instanceof XNumber)) {
        	 // When comparing numeric values, collationUri is not used
        	 double num1 = ((XNumber)resultSeqItem).num();
        	 double num2 = ((XNumber)srch).num();

        	 if ((num1 == num2) || (Double.isNaN(num1) && Double.isNaN(num2))) {
        		isSeqContains = true;
        		break; 
        	 }
         }
         else if ((resultSeqItem instanceof XSAnyType) && (srch instanceof XSAnyType)) {
             if (((XSAnyType)resultSeqItem).equals((XSAnyType)srch, collationUri, xpathCollationSupport)) {
                isSeqContains = true;
                break;    
             }   
         }
         else if (resultSeqItem.equals(srch, collationUri, xpathCollationSupport)) {
             isSeqContains = true;
             break;    
         }
      }
       
      return isSeqContains;       
   }
    
    /*
     * Serialize an XML DOM element node, to XML string value.
     */
    public static String serializeXmlDomElementNode(Node node) throws Exception {
    	String resultStr = null;

    	DOMImplementationLS domImplLS = (DOMImplementationLS)((DOMImplementationRegistry.
    																				 newInstance()).getDOMImplementation("LS"));    	
    	LSSerializer lsSerializer = domImplLS.createLSSerializer();
    	DOMConfiguration domConfig = lsSerializer.getDomConfig();
    	domConfig.setParameter(XSL3FunctionService.XML_DOM_FORMAT_PRETTY_PRINT, Boolean.TRUE);
    	resultStr = lsSerializer.writeToString(node);
    	resultStr = resultStr.replaceFirst(XSL3FunctionService.UTF_16, XSL3FunctionService.UTF_8);
        		
    	return resultStr;
    }
    
    /**
     * This method checks whether, an XSL xsl:with-param/xsl:param 
     * tunnel attribute's value is true (i.e, having string 
     * values "yes", "true", or "1").
     */
    public static boolean isTunnelAttributeYes(String val) {
       boolean result = false;
       
       if (val != null) {
    	  result = (val.equals("yes") || val.equals("true") || val.equals("1"));
       }
       
       return result;
    }
    
    /**
     * Method definition to check whether, all the values of the 
     * supplied input sequence are XPath atomic values.
     * 
     * @param resultSeq				The supplied XPath sequence object
     * @return						boolean value true or false
     */
    public static boolean isSequenceContainsAllXdmAtomicValues(ResultSequence resultSeq) {
    	
    	boolean result = true;
    	
    	int size1 = resultSeq.size();
    	for (int idx = 0; idx < size1; idx++) {
    		XObject seqItem = resultSeq.item(idx);
    		if (!((seqItem instanceof XNumber) || (seqItem instanceof XBooleanStatic) || (seqItem instanceof XBoolean) || 
						        				  (seqItem instanceof XSBoolean) || (seqItem instanceof XString) || 
						        				  (seqItem instanceof XSAnyAtomicType))) {
    			result = false;

    			break;
    		}
    	}
    	
    	return result;
    }
    
    /**
     * Method definition to check whether, all the values of the 
     * supplied input sequence are XPath node references.
     * 
     * @param resultSeq				The supplied XPath sequence object
     * @return						boolean value true or false
     */
    public static boolean isSequenceContainsAllXdmNodes(ResultSequence resultSeq) {
    	
    	boolean result = true;
    	
    	int size1 = resultSeq.size();
    	for (int idx = 0; idx < size1; idx++) {
    		XObject seqItem = resultSeq.item(idx);
    		if (!(seqItem instanceof XMLNodeCursorImpl)) {
    			result = false;

    			break;
    		}
    	}
    	
    	return result;
    }
    
    /**
     * This method definition does the xsl:character-map transformation
     * on the supplied string value, using a CharacterMapConfig 
     * run-time object. 
     * 
     * @param strValue					Supplied string value
     * @param charMapConfig				CharacterMapConfig run-time object
     * @return							The modified string value after doing
     *                                  xsl:character-map transformation.
     */
    public static String characterMapTransformation(String strValue, CharacterMapConfig charMapConfig) {    	
    	
    	String result = strValue;    	
    	
    	Map<Integer, String> hashMap1 = charMapConfig.getCharMap();
    	Set<Integer> charSet = hashMap1.keySet();
    	Iterator<Integer> iter = charSet.iterator();    	
    	while (iter.hasNext()) {
    		Integer targetCodePoint = iter.next();
    		String replacementStr = hashMap1.get(targetCodePoint);    	    		    		    		
    		StringBuffer strBuffer = new StringBuffer();
    		IntStream resultCodePointIntStream = result.codePoints();
    		int[] resultCodePointArr = resultCodePointIntStream.toArray();
    		for (int idx = 0; idx < resultCodePointArr.length; idx++) {
    			int codePoint = resultCodePointArr[idx];
    			if (codePoint == targetCodePoint) { 
    				strBuffer.append(replacementStr);
    			} 
    			else {
    				strBuffer.appendCodePoint(codePoint);
    			}
    		}
    		
    		result = strBuffer.toString(); 
    	}

    	return result;
    }
    
    /**
     * Function definition to get xsl:function's compiled ElemFunction object, 
     * given a NodeTest expression.  
     * 
     * @param nodeTest							   A NodeTest object instance constructed from XPath 
     *                                             named function reference like fn0:abc#1.
     * @param transformerImpl					   An XSL transform TransformerImpl object
     * @param srcLocator						   SourceLocator object in XPath context
     * @return									   An ElemFunction object if available, otherwise null
     * 
     * @throws javax.xml.transform.TransformerException
     */
    public static ElemFunction getElemFunctionFromNodeTestExpression(NodeTest nodeTest, TransformerImpl transformerImpl, 
  		                                                             SourceLocator srcLocator) throws javax.xml.transform.TransformerException {

  	  ElemFunction result = null;

  	  String funcNameRef = nodeTest.getLocalName();
  	  String funcNamespace = nodeTest.getNamespace();

  	  ExpressionNode expressionNode = nodeTest.getExpressionOwner();
  	  ExpressionNode stylesheetRootNode = null;
  	  while (expressionNode != null) {
  		  stylesheetRootNode = expressionNode;
  		  expressionNode = expressionNode.exprGetParent();                     
  	  }

  	  StylesheetRoot stylesheetRoot = (StylesheetRoot)stylesheetRootNode;  	    	  

  	  if (stylesheetRoot != null) {
  		  TemplateList templateList = stylesheetRoot.getTemplateListComposed();  		  
  		  XSL3FunctionService xslFunctionService = XSLFunctionBuilder.getXSLFunctionService();  		  
  		  if (xslFunctionService.isFuncArityWellFormed(funcNameRef)) {        	   
  			  int hashCharIdx = funcNameRef.indexOf('#');
  			  String funcNameRef2 = funcNameRef.substring(0, hashCharIdx);
  			  int funcArity = Integer.valueOf(funcNameRef.substring(hashCharIdx + 1));        		   
  			  ElemTemplate elemTemplate = templateList.getXslFunction(new QName(funcNamespace, funcNameRef2), funcArity);        		   
  			  if (elemTemplate != null) {
  				  result = (ElemFunction)elemTemplate;
  				  int xslFuncDefnParamCount = result.getArity();                      
  				  String str = funcNameRef.substring(hashCharIdx + 1);
  				  int funcRefParamCount = (Integer.valueOf(str)).intValue();
  				  if (funcRefParamCount != xslFuncDefnParamCount) {
  					  throw new javax.xml.transform.TransformerException("FORG0006 : An XPath named function reference " + funcNameRef + " cannot resolve to a function "
  																													                 + "definition.", srcLocator); 
  				  }
  			  }
  		  }
  		  else {
  			  throw new javax.xml.transform.TransformerException("FORG0006 : An XPath named function reference " + funcNameRef + " cannot resolve to a function "
  																											                 + "definition.", srcLocator);
  		  }
  	  }

  	  return result;  	  
    }
    
    /**
     * Method definition, to strip XML namespace nodes recursively
     * from the supplied XMLNodeCursorImpl node set object.
     * 
     * @param nodeSet					The supplied XMLNodeCursorImpl node set object
     * @param xctxt					    An XPath context object
     */
    public static XMLNodeCursorImpl stripNsNodesFromXdmElementNode(XMLNodeCursorImpl nodeSet, XPathContext xctxt) throws TransformerException {
 	   
 	   XMLNodeCursorImpl result = null;
 	   
 	   int nodeHandle = nodeSet.asNode(xctxt);
 	   DTM dtm = xctxt.getDTM(nodeHandle);
 	   Node node = dtm.getNode(nodeHandle);
 	   String xmlStr = null;
 	   
 	   SourceLocator srcLocator = xctxt.getSAXLocator();
 	   
 	   try {
 		   xmlStr = XslTransformEvaluationHelper.serializeXmlDomElementNode(node);
 		   
 		   System.setProperty(Constants.XML_DOCUMENT_BUILDER_FACTORY_KEY, Constants.XML_DOCUMENT_BUILDER_FACTORY_VALUE);
 		  
 		   DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
 		   dbFactory.setNamespaceAware(true);
 		   DocumentBuilder docBuilder = dbFactory.newDocumentBuilder();
 		   
 		   StringReader strReader = new StringReader(xmlStr);
 		   InputSource inpSource = new InputSource(strReader);
 		   Document document = docBuilder.parse(inpSource);
 		   stripNsNodesFromXmlDocument(document.getDocumentElement());

 		   DTMManager dtmManager = xctxt.getDTMManager();
 		   DTM resultDtm = dtmManager.getDTM(new DOMSource(document), true, null, false, false);
 		   int resultNodeHandle = resultDtm.getDocument();

 		   result = new XMLNodeCursorImpl(resultNodeHandle, xctxt);
 	   }
 	   catch (Exception ex) {
 		   throw new TransformerException(ex.getMessage(), srcLocator); 
 	   }
 	   
 	   return result;
    }
    
    /**
     * Method definition, to construct a string value of required 
     * length, comprising a sequence of character '0'.
     * 
     * @param strLength				The length of the desired string 
     *                              value.
     * @return                      The string value constructed
     */
    public static String getStrWithZeros(int strLength) {
       
       String result = null;
       
       StringBuffer strBuff = new StringBuffer();
       
       for (int idx = 0; idx < strLength; idx++) {
          strBuff.append("0");
       }
       
       result = strBuff.toString(); 
       
       return result;
    }
    
    /**
     * Method definition, to get XSL stylesheet root object, from
     * the supplied XSL stylesheet non-expression node.
     * 
     * @param expressionNode						An XSL stylesheet not-expression node
     * @return										An XSL stylesheet root object
     */
    public static StylesheetRoot getXslStylesheetRootFromXslElementRef(ExpressionNode expressionNode) {
       
    	StylesheetRoot result = null;

    	ExpressionNode stylesheetRootExprNode = null;
    	while (expressionNode != null) {
    		stylesheetRootExprNode = expressionNode;
    		expressionNode = expressionNode.exprGetParent();                     
    	}

    	if (stylesheetRootExprNode instanceof Stylesheet) {
    		Stylesheet stylesheet = (Stylesheet)stylesheetRootExprNode;
    		result = stylesheet.getStylesheetRoot();    				
    	}    			
    	else {
    		result = (StylesheetRoot)stylesheetRootExprNode;
    	}

    	return result;
    }
    
    /**
     * Method definition, to produce a random permutation of
     * the supplied xdm sequence.
     * 
     * @param rSeq                   Supplied xdm sequence, object instance
     * @return                       Random permutation of the supplied sequence
     */
    public static XObject permute(ResultSequence rSeq)
    {
    	ResultSequence result = new ResultSequence();

    	int rSeqLength = rSeq.size();
    	List<XObject> list1 = new ArrayList<XObject>();
    	for (int idx = 0; idx < rSeqLength; idx++) {
    		list1.add(rSeq.item(idx));  
    	}

    	Collections.shuffle(list1);
    	for (int idx = 0; idx < rSeqLength; idx++) {
    		result.add(list1.get(idx));  
    	}

    	return result; 
    }
    
    /**
     * Method definition, to get numerical sum from xdm sequence items.
     *  
     * @param resultSeq  				An xdm sequence object instance
     * @return           				The summation value
     */
    private static double sumResultSequence(ResultSequence resultSeq) {
       
       double sum = 0.0;
       
       int size1 = resultSeq.size();
       for (int idx = 0; idx < size1; idx++) {
          XObject xObj = resultSeq.item(idx);
          String str = null;
          if (xObj instanceof XSAnyType) {
             str = ((XSAnyType)xObj).stringValue();     
          }
          else {
             str = xObj.str();
          }
          if (str != null) {
             XString xStr = new XString(str);
             sum +=  xStr.toDouble();
          }
       }
       
       return sum;
    }
    
    /**
     * Method definition, to strip XML namespace nodes recursively
     * from the supplied XML dom element node.
     * 
     * @param elemNode					The supplied XML dom element node
     */
    private static void stripNsNodesFromXmlDocument(Element elemNode) {

 	   NamedNodeMap namedNodeMap = elemNode.getAttributes();

 	   int attrCount = namedNodeMap.getLength();
 	   for (int idx = 0; idx < attrCount; idx++) {
 		   Node node = namedNodeMap.item(idx);
 		   String nodeName = node.getNodeName();
 		   if (nodeName.startsWith(Constants.ATTRNAME_XMLNS) || (Constants.ATTRNAME_XMLNSDEF).equals(nodeName)) {
 			   elemNode.removeAttributeNode((Attr)node);
 			   NodeList nodeList = elemNode.getChildNodes();
 			   int nodeListLength = nodeList.getLength();
 			   for (int idx2 = 0; idx2 < nodeListLength; idx2++) {
 				   Node node2 = nodeList.item(idx2);
 				   if (node2.getNodeType() == Node.ELEMENT_NODE) {
 					   stripNsNodesFromXmlDocument((Element)node2); 
 				   }
 			   }
 		   }
 	   }
    }

}
