/*
 * 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.templates;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;

import javax.xml.transform.ErrorListener;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.TransformerException;

import org.apache.xalan.transformer.StackGuard;
import org.apache.xalan.transformer.TransformerImpl;
import org.apache.xalan.xslt.util.XslTransformEvaluationHelper;
import org.apache.xerces.xs.XSTypeDefinition;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMCursorIterator;
import org.apache.xml.serializer.SerializationHandler;
import org.apache.xml.utils.IntStack;
import org.apache.xml.utils.QName;
import org.apache.xpath.Expression;
import org.apache.xpath.VariableStack;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathContext;
import org.apache.xpath.axes.LocPathIterator;
import org.apache.xpath.axes.SelfIteratorNoPredicate;
import org.apache.xpath.composite.SequenceTypeData;
import org.apache.xpath.composite.SequenceTypeSupport;
import org.apache.xpath.composite.XPathSequenceConstructor;
import org.apache.xpath.objects.ElemFunctionItem;
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.XNodeSetForDOM;
import org.apache.xpath.objects.XNumber;
import org.apache.xpath.objects.XObject;
import org.apache.xpath.objects.XPathInlineFunction;
import org.apache.xpath.objects.XRTreeFrag;
import org.apache.xpath.objects.XString;
import org.apache.xpath.operations.Variable;
import org.apache.xpath.patterns.NodeTest;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import xml.xpath31.processor.types.XSAnyAtomicType;
import xml.xpath31.processor.types.XSBoolean;

/**
 * Implementation of XSLT 3.0 xsl:apply-templates element.
 * 
 * @xsl.usage advanced
 */
public class ElemApplyTemplates extends ElemCallTemplate
{
  
  static final long serialVersionUID = 2903125371542621004L;
  
  /**
   * Class field to refer to, XPath expression for subsequent 
   * processing.
   */
  private XPath m_xpath2 = null;
  
  /**
   * The "select" expression.
   */
  private Expression m_selectExpression2 = null;

  /**
   * xsl:template's mode value.
   */
  private QName m_mode = null;
  
  /**
   * Tells if this belongs to a default template,
   * in which case it will act different with
   * regard to processing XSL template modes.
   */
  private boolean m_isDefaultTemplate = false;
  
  /**
   * Class field, that represents the value of "xpath-default-namespace" 
   * attribute.
   */
  private String m_xpath_default_namespace = null;
  
  /**
   * Class field, that represents the value of "expand-text" 
   * attribute.
   */
  private boolean m_expand_text;
  
  /**
   * Set the "select" attribute.
   *
   * @param xpath The XPath expression for the "select" attribute.
   */
  public void setSelect(XPath xpath)
  {
    m_selectExpression2 = xpath.getExpression();
    m_xpath2 = xpath;    
  }

  /**
   * Get the "select" attribute.
   *
   * @return The XPath expression for the "select" attribute.
   */
  public Expression getSelect()
  {
    return m_selectExpression2;
  }

  /**
   * Set the mode attribute for this element.
   *
   * @param mode reference, which may be null.
   */
  public void setMode(QName mode)
  {
	  m_mode = mode;
  }

  /**
   * Get the mode attribute for this element.
   *
   * @return The mode attribute for this element
   */
  public QName getMode()
  {
	  return m_mode;
  }

  /**
   * Set if this belongs to a default template,
   * in which case it will act different with
   * regard to processing modes.
   *
   * @param b boolean value to set.
   */
  public void setIsDefaultTemplate(boolean b)
  {
	  m_isDefaultTemplate = b;
  }
  
  /**
   * Set the value of "xpath-default-namespace" attribute.
   *
   * @param v   Value of the "xpath-default-namespace" attribute
   */
  public void setXpathDefaultNamespace(String v)
  {
	  m_xpath_default_namespace = v; 
  }

  /**
   * Get the value of "xpath-default-namespace" attribute.
   *  
   * @return		  The value of "xpath-default-namespace" attribute 
   */
  public String getXpathDefaultNamespace() {
	  return m_xpath_default_namespace;
  }

  /**
   * Variable to indicate whether, an attribute 'expand-text'
   * is declared on xsl:apply-templates instruction.
   */
  private boolean m_expand_text_declared;

  /**
   * Set the value of "expand-text" attribute.
   *
   * @param v   Value of the "expand-text" attribute
   */
  public void setExpandText(boolean v)
  {
	  m_expand_text = v;
	  m_expand_text_declared = true;
  }

  /**
   * Get the value of "expand-text" attribute.
   *  
   * @return		  The value of "expand-text" attribute 
   */
  public boolean getExpandText() {
	  return m_expand_text;
  }
  
  /**
   * Get a boolean value indicating whether, an "expand-text" 
   * attribute has been declared. 
   */
  public boolean getExpandTextDeclared() {
	  return m_expand_text_declared;
  }
  
  private Vector m_vars;
  
  private int m_globals_size;

  /**
   * Get an int constant identifying the type of element.
   * @see org.apache.xalan.templates.Constants
   *
   * @return Token ID for this element types
   */
  public int getXSLToken()
  {
	  return Constants.ELEMNAME_APPLY_TEMPLATES;
  }
  
  /**
   * This function is called after everything else has been
   * recomposed, and allows the template to set remaining
   * values that may be based on some other property that
   * depends on recomposition.
   */
  public void compose(StylesheetRoot sroot) throws TransformerException
  {
	  super.compose(sroot);
	  
	  java.util.Vector vnames = sroot.getComposeState().getVariableNames();
	  
	  if (m_selectExpression2 != null) {
		  m_selectExpression2.fixupVariables(vnames, sroot.getComposeState().getGlobalsSize());
	  }
	  else
	  {
		  m_selectExpression2 = getStylesheetRoot().m_selectDefault.getExpression();
	  }

	  m_vars = vnames;
	  m_globals_size = sroot.getComposeState().getGlobalsSize(); 
  }
  
  /**
   * This after the template's children have been composed.
   */
  public void endCompose(StylesheetRoot sroot) throws TransformerException
  {    
     super.endCompose(sroot);
  }

  /**
   * Return the node name.
   *
   * @return Element name
   */
  public String getNodeName()
  {
	  return Constants.ELEMNAME_APPLY_TEMPLATES_STRING;
  }

  /**
   * Apply the context node to XSL stylesheet's matching templates.
   *
   * @param transformer non-null reference to the current transform-time state.
   *
   * @throws TransformerException
   */
  public void execute(TransformerImpl transformer) throws TransformerException
  {

	  transformer.pushCurrentTemplateRuleIsNull(false);

	  boolean pushMode = false;

	  try
	  {
		  QName mode = transformer.getMode();

		  if (!m_isDefaultTemplate)
		  {
			  if (((null == mode) && (null != m_mode))
					  								|| ((null != mode) &&!mode.equals(m_mode)))
			  {
				  pushMode = true;
				  
				  transformer.pushMode(m_mode);
			  }
		  }
		  if (transformer.getDebug())
			  transformer.getTraceManager().emitTraceEvent(this);

		  transformXdmItems(transformer);
	  }
	  finally
	  {
		  if (transformer.getDebug())
			  transformer.getTraceManager().emitTraceEndEvent(this);

		  if (pushMode)
			  transformer.popMode();

		  transformer.popCurrentTemplateRuleIsNull();
	  }
  }

  
  /**
   * Method definition, to do an XSL transformation of each xdm node or/and
   * other kinds of xdm items, that are selected by xsl:apply-templates
   * instruction. An xsl:apply-templates instruction, finds a suitable XSL 
   * template (user-defined, or an XSL built-in template rule) than can do 
   * XSL transformation of the selected xdm item.
   *
   * @param transformer non-null reference to the the current transform-time state
   *
   * @throws TransformerException Thrown in a variety of circumstances
   * @xsl.usage advanced
   */
  public void transformXdmItems(TransformerImpl transformer) throws TransformerException
  {

	  final XPathContext xctxt = transformer.getXPathContext();
	  final int contextNode = xctxt.getCurrentNode();	  	  

	  DTMCursorIterator sourceNodes = null;

	  SourceLocator srcLocator = xctxt.getSAXLocator();	  	  
	  
	  XObject varEvalResult = null;    
	  ResultSequence resultSeq = null;
	  QName xslTemplateInvokeMode = getMode();
	    
	  if ((m_selectExpression2 != null) && (m_xpath_default_namespace != null)) {    		
		  m_xpath2 = new XPath(m_xpath2.getPatternString(), srcLocator, xctxt.getNamespaceContext(), XPath.SELECT, null);
		  m_selectExpression2 = m_xpath2.getExpression(); 
	  }

	  XObject xpath3ContextItem = xctxt.getXPath3ContextItem();
	  if (xpath3ContextItem != null) {		  
		  if (isXdmItemAtomicValue(xpath3ContextItem)) {
			  if ((m_selectExpression2 instanceof LocPathIterator) && !(m_selectExpression2 instanceof SelfIteratorNoPredicate)) {
				  throw new TransformerException("XTTE0510 : An XPath node is expected as a context item, but the "
						                                                                          + "context item is an xdm atomic value.", this); 
			  }
			  else {
				  resultSeq = new ResultSequence();
				  resultSeq.add(xpath3ContextItem);
				  xslApplyTemplatesAtomicValueSeq(transformer, xctxt, resultSeq, xslTemplateInvokeMode);

				  return;
			  }
		  }
	  }

	  if (m_selectExpression2 instanceof XPathSequenceConstructor) {
		  resultSeq = (ResultSequence)(((XPathSequenceConstructor)m_selectExpression2).execute(xctxt));

		  if (isAllSeqItemsXdmAtomicValues(resultSeq)) {    		
			  xslApplyTemplatesAtomicValueSeq(transformer, xctxt, resultSeq, xslTemplateInvokeMode);

			  return;
		  }
		  else {			  
			  int rSeqSize = resultSeq.size();
			  for (int idx = 0; idx < rSeqSize; idx++) {
				  XObject xObj = resultSeq.item(idx);
				  if (xObj instanceof XMLNodeCursorImpl) {
					  sourceNodes = ((XMLNodeCursorImpl)xObj).iterRaw();					  
					  xslApplyTemplatesToNodes(transformer, xctxt, contextNode, sourceNodes, srcLocator);
				  }
				  else {
					  ResultSequence rSeq = new ResultSequence();
					  rSeq.add(xObj);
					  xslApplyTemplatesAtomicValueSeq(transformer, xctxt, rSeq, xslTemplateInvokeMode);
				  }
			  }
			  
			  return;
		  }
	  }    
	  else if (m_selectExpression2 instanceof Variable) {
		  varEvalResult = ((Variable)m_selectExpression2).execute(xctxt);

		  if (varEvalResult instanceof ResultSequence) {
			  resultSeq = (ResultSequence)varEvalResult;

			  if (isAllSeqItemsXdmAtomicValues(resultSeq)) {
				  xslApplyTemplatesAtomicValueSeq(transformer, xctxt, resultSeq, xslTemplateInvokeMode);

				  return;
			  }
			  else {
				  XMLNodeCursorImpl nodeSet = XslTransformEvaluationHelper.getXNodeSetFromResultSequence(resultSeq, xctxt);
				  if (nodeSet != null) {
					  sourceNodes = nodeSet.asIterator(xctxt, contextNode);   
				  }
				  else {
					  return;
				  }
			  }
		  }
		  else if ((varEvalResult instanceof XSAnyAtomicType) || (varEvalResult instanceof XString) || 
				                                                 (varEvalResult instanceof XBoolean) || 
				                                                 (varEvalResult instanceof XNumber)) {   	       	       	   
			  xslApplyTemplatesAtomicValue(transformer, xctxt, varEvalResult, xslTemplateInvokeMode);

			  return; 
		  }
		  else if (varEvalResult instanceof XMLNodeCursorImpl) {
			  sourceNodes = ((XMLNodeCursorImpl)varEvalResult).asIterator(xctxt, contextNode);
		  }       
		  else {
			  throw new TransformerException("XTTE0505 : An XSL apply-templates 'select' expression evaluation "
																								  + "resulted in a value that is not "
																								  + "supported to be processed.", srcLocator);
		  }
	  }
	  else {
		  varEvalResult = m_selectExpression2.execute(xctxt);

		  if (varEvalResult instanceof ResultSequence) {
			  resultSeq = (ResultSequence)varEvalResult;

			  if (isAllSeqItemsXdmAtomicValues(resultSeq)) {
				  xslApplyTemplatesAtomicValueSeq(transformer, xctxt, resultSeq, xslTemplateInvokeMode);

				  return;
			  }
			  else {
				  XMLNodeCursorImpl nodeSet = XslTransformEvaluationHelper.getXNodeSetFromResultSequence(resultSeq, xctxt);    		   
				  if (nodeSet != null) {
					  sourceNodes = nodeSet.asIterator(xctxt, contextNode);   
				  }
				  else {
					  return;
				  }
			  } 
		  }
		  else if (varEvalResult instanceof XMLNodeCursorImpl) {
			  XMLNodeCursorImpl nodeSet = (XMLNodeCursorImpl)varEvalResult;
			  if (".".equals(m_xpath2.getPatternString())) {
				  sourceNodes = nodeSet.asIterator(xctxt, DTM.NULL);
			  }
			  else {
				  sourceNodes = nodeSet.asIterator(xctxt, contextNode);
			  }
		  }
		  else if (isXdmItemAtomicValue(varEvalResult)) {    	   
			  xslApplyTemplatesAtomicValue(transformer, xctxt, varEvalResult, xslTemplateInvokeMode);

			  return;
		  }
		  else {
			  throw new TransformerException("XTTE0505 : An XSL apply-templates 'select' expression evaluation "
																								  + "resulted in a value that is not "
																								  + "supported to be processed.", srcLocator);
		  }
	  }

	  if (sourceNodes != null) {		 
	     xslApplyTemplatesToNodes(transformer, xctxt, contextNode, sourceNodes, srcLocator);
	  }
  }

  /**
   * Method definition, to do XSLT processing for the xsl:apply-templates 
   * instruction on xdm nodes.
   * 
   * @param transformer							   An XSL TransformerImpl object instance
   * @param xctxt                                  XPathContext object instance
   * @param contextNode                            XPath context node                             
   * @param sourceNodes                            An iterator for the xdm nodes
   * @param srcLocator                             XPath SourceLocator object instance
   * @throws TransformerException
   */
  private void xslApplyTemplatesToNodes(TransformerImpl transformer, XPathContext xctxt, int contextNode,
		                                                                        DTMCursorIterator sourceNodes, SourceLocator srcLocator) 
		                                                                        		                                             throws TransformerException {
	  
	  VariableStack vars = xctxt.getVarStack();
	  int nParams = getParamElemCount();
	  int thisframe = vars.getStackFrame();
	  StackGuard guard = transformer.getStackGuard();
	  boolean check = (guard.getRecursionLimit() > -1) ? true : false;

	  boolean pushContextNodeListFlag = false;

	  try
	  {
		  xctxt.pushCurrentNode(DTM.NULL);
		  xctxt.pushCurrentExpressionNode(DTM.NULL);
		  xctxt.pushSAXLocatorNull();
		  transformer.pushElemTemplateElement(null);
		  
		  if (m_sortElems != null) {
			  int sortElemCount = m_sortElems.size();
			  for (int idx = 0; idx < sortElemCount; idx++) {
				  ElemSort elemSort = (ElemSort)(m_sortElems.elementAt(idx));
				  AVT stableAvt = elemSort.getStable();
				  if (stableAvt != null) {
					  String stableValue = stableAvt.evaluate(xctxt, contextNode, xctxt.getNamespaceContext());
					  stableValue = stableValue.trim();
					  if (!("yes".equals(stableValue) || "1".equals(stableValue) || "true".equals(stableValue) 
																										  || "no".equals(stableValue) || "0".equals(stableValue) 
																										  || "false".equals(stableValue))) {
						  throw new javax.xml.transform.TransformerException("XTSE0020 : An XSL 'sort' instruction attribute 'stable''s value " + 
																																  stableValue + " is not valid. The allowed "
																																  + "values for attribute 'stable' are yes,1,true,no,0,false.", 
																																  elemSort);
					  }
				  }
			  }
       	  }
		  
		  final Vector keys = (m_sortElems == null)
											  ? null
											  : transformer.processSortKeys(this, contextNode);

		  // Sort if we need to
		  if (null != keys)
			  sourceNodes = sortNodes(xctxt, keys, sourceNodes);

		  if (transformer.getDebug())
		  {
			  transformer.getTraceManager().emitSelectedEvent(contextNode, this,
					  "select", new XPath(m_selectExpression2),
					  new org.apache.xpath.objects.XMLNodeCursorImpl(sourceNodes));
		  }

		  final SerializationHandler rth = transformer.getSerializationHandler();
		  final StylesheetRoot sroot = transformer.getStylesheet();
		  final TemplateList tl = sroot.getTemplateListComposed();

		  // Should be able to get this from the iterator 
		  // but there might be a codebase issue.
		  DTM dtm = xctxt.getDTM(contextNode);

		  int argsFrame = -1;
		  if (nParams > 0)
		  {
			  // This code will create a section on the stack that is all the 
			  // evaluated arguments. These will be copied into the real params 
			  // section of each called template.
			  argsFrame = vars.link(nParams);
			  vars.setStackFrame(thisframe);

			  int maxParamStackFrameIndex = -1;

			  for (int i = 0; i < nParams; i++) 
			  {
				  ElemWithParam ewp = m_paramElems[i];
				  if (transformer.getDebug())
					  transformer.getTraceManager().emitTraceEvent(ewp);

				  XObject obj = null;

				  try {
					  obj = ewp.getValue(transformer, contextNode);
				  }
				  catch (TransformerException ex) {
					  throw new TransformerException(ex.getMessage(), srcLocator);   
				  }

				  if (transformer.getDebug())
					  transformer.getTraceManager().emitTraceEndEvent(ewp);

				  String tunnelStrVal = ewp.getTunnel();
				  obj.setTunnel(tunnelStrVal);
				  QName qName = ewp.getName();
				  obj.setQName(qName);

				  vars.setLocalVariable(i, obj, argsFrame);
				  maxParamStackFrameIndex = i;
			  }

			  for (int idx = 0; idx < MAX_PARAM_LIMIT; idx++) {
				  XObject obj = vars.getLocalVariable(idx, thisframe);
				  if (obj != null) {
					  String tunnelValStr = obj.getTunnel();
					  if ((obj.getTunnel() != null) && XslTransformEvaluationHelper.isTunnelAttributeYes(tunnelValStr)) {
						  vars.setLocalVariable(++maxParamStackFrameIndex, obj, argsFrame);
					  }
				  }
				  else {
					  break; 
				  }
			  }

			  ElemTemplateElement parentElem = getParentElem();
			  while (!(parentElem instanceof ElemTemplate)) {
				  parentElem = parentElem.getParentElem(); 
			  }

			  List<XObject> tunnelParamObjList = parentElem.getTunnelParamObjList();
			  for (int idx = 0; idx < tunnelParamObjList.size(); idx++) {
				  XObject var = tunnelParamObjList.get(idx);
				  vars.setLocalVariable(++maxParamStackFrameIndex, var, argsFrame);
			  }

			  vars.setStackFrame(argsFrame);
		  }

		  xctxt.pushContextNodeList(sourceNodes);
		  pushContextNodeListFlag = true;

		  IntStack currentNodes = xctxt.getCurrentNodeStack();

		  IntStack currentExpressionNodes = xctxt.getCurrentExpressionNodeStack();		  

		  QName mode = transformer.getMode();

		  QName qnameCurrent = new QName("http://xml.apache.org/xalan/java", "current", true);
		  QName qnameUnnamed = new QName("http://xml.apache.org/xalan/java", "unnamed", true);
		  if (qnameCurrent.equals(mode)) {
			  mode = transformer.getCurrentMode();  
		  }
		  else if (qnameUnnamed.equals(mode)) {
			  mode = null; 
		  }

		  ElemMode elemMode = sroot.getElemMode(mode);

		  String onNoMatchStr = null;
		  String xslOnMultipleMatchStr = null;
		  boolean xslWarningOnMultipleMatch = false;

		  if (elemMode != null) {
			  xslOnMultipleMatchStr = elemMode.getOnMultipleMatch();				  
			  if (xslOnMultipleMatchStr != null) {
				  if (!((Constants.ATTRVAL_USE_LAST).equals(xslOnMultipleMatchStr) || (Constants.ATTRVAL_FAIL).equals(xslOnMultipleMatchStr))) {
					  throw new TransformerException("XTTE0505 : An XSL mode instruction's attribute \"on-multiple-match\" has disallowed "
							  																		+ "value '" + xslOnMultipleMatchStr + "'.", elemMode); 
				  }
			  }

			  xslWarningOnMultipleMatch = elemMode.isWarningOnMultipleMatch();

			  onNoMatchStr = elemMode.getOnNoMatch();
		  }

		  int child;
		  
		  boolean isXdmParentlessSiblingNodes = false;
		  List<Integer> nodeHandleList = new ArrayList<Integer>();
		  DTM dtm2 = dtm;
		  while (DTM.NULL != (child = sourceNodes.nextNode()))
		  {
			  nodeHandleList.add(child);
			  dtm = xctxt.getDTM(child); 
			  String str2 = dtm.getNodeValue(child);			  
			  if ((Constants.XSL_DOCUMENT_INSTRUCTION_MARKER).equals(str2)) {
				  isXdmParentlessSiblingNodes = true;	    			 	    			 
			  }
		  }
		  
		  if (!isXdmParentlessSiblingNodes) {
			  dtm = dtm2; 
		  }
		  
		  xctxt.popContextNodeList();

		  XMLNodeCursorImpl nodeList = new XMLNodeCursorImpl(nodeHandleList, xctxt);
		  sourceNodes = nodeList.iterRaw();
		  
		  xctxt.pushContextNodeList(sourceNodes);
		  
		  while (DTM.NULL != (child = sourceNodes.nextNode()))
		  {
			  currentNodes.setTop(child);
			  currentExpressionNodes.setTop(child);			  

			  if (xctxt.getDTM(child) != dtm)
			  {
				  dtm = xctxt.getDTM(child);
			  }

			  final int exNodeType = dtm.getExpandedTypeID(child);

			  final int nodeType = dtm.getNodeType(child);
			  
			  String str2 = dtm.getNodeValue(child);			  			  
    		  if ((Constants.XSL_DOCUMENT_INSTRUCTION_MARKER).equals(str2)) {
    			 continue; 
    		  }

			  ElemTemplate template = tl.getTemplateFast(xctxt, child, exNodeType, mode, -1, true, dtm, 
					  																		xslOnMultipleMatchStr, 
					  																		xslWarningOnMultipleMatch, isXdmParentlessSiblingNodes);			 

			  // If that didn't locate an XSL stylesheet user written template rule, 
			  // fall back to a default template rule.
			  if (template == null)
			  {
				  String nodeTypeStr = null;

				  switch (nodeType)
				  {
				  case DTM.DOCUMENT_FRAGMENT_NODE :
				  case DTM.ELEMENT_NODE :	
					  nodeTypeStr = Constants.ELEMNAME_ELEMENT_STRING;					  
					  if (elemMode != null) {						  						  
						  String nodeNameStr = dtm.getNodeName(child);						  
						  if (onNoMatchStr != null) {							  
							  if ((Constants.ATTRVAL_TEXT_ONLY_COPY).equals(onNoMatchStr)) {
								  template = sroot.getTextOnlyCopyRule(elemMode.getName(), nodeType, mode);								  								  								  
							  }
							  else if ((Constants.ATTRVAL_DEEP_COPY).equals(onNoMatchStr)) {
								  template = sroot.getDeepCopyRule(elemMode.getName());
							  }
							  else if ((Constants.ATTRVAL_SHALLOW_COPY).equals(onNoMatchStr)) {
								  template = sroot.getShallowCopyRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_DEEP_SKIP).equals(onNoMatchStr)) {
								  template = sroot.getDeepSkipRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_SHALLOW_SKIP).equals(onNoMatchStr)) {
								  template = sroot.getShallowSkipRule(elemMode.getName(), nodeType, mode);
							  }							  
							  else if ((Constants.ATTRVAL_FAIL).equals(onNoMatchStr)) {
								  String errMesg = "XTDE0555 : An XSL template declaration could not be found to process an XML " + nodeTypeStr + " node";
								  if (nodeNameStr != null) {
									  errMesg = (errMesg + " '" + nodeNameStr + "'."); 
								  }
								  else {
									  errMesg = (errMesg + "."); 
								  }

								  throw new TransformerException(errMesg, srcLocator);
							  }
							  else {
								  throw new TransformerException("XTTE0505 : An XSL mode instruction's attribute \"on-no-match\" has disallowed "
										  																		+ "value '" + onNoMatchStr + "'.", elemMode);
							  }
						  }

						  if (elemMode.isWarningOnNoMatch()) {
							  /**
							   * Emit an, XSL stylesheet processing warning, when xsl:mode
							   * instruction specifies an attribute 'warning-on-no-match' with
							   * boolean value true.
							   */							  
							  emitXslTransformNoRuleMatchWarning(nodeNameStr, nodeTypeStr, xctxt, srcLocator);
						  }

						  if ((Constants.ATTRVAL_TEXT_ONLY_COPY).equals(onNoMatchStr)) {
							  serializeXdmStringValueWithDefaultXslTextRule(transformer, rth, sroot, dtm, child, template);

							  continue;
						  }
					  }
					  else {
						  template = sroot.getDefaultRule();
					  }

					  break;
				  case DTM.ATTRIBUTE_NODE :
					  nodeTypeStr = Constants.ELEMNAME_ATTRIBUTE_STRING;					  
					  if (elemMode != null) {						  						  
						  String nodeNameStr = dtm.getNodeName(child);
						  if (onNoMatchStr != null) {
							  if ((Constants.ATTRVAL_TEXT_ONLY_COPY).equals(onNoMatchStr)) {
								  template = sroot.getTextOnlyCopyRule(elemMode.getName(), nodeType, mode);								  
							  }
							  else if ((Constants.ATTRVAL_DEEP_COPY).equals(onNoMatchStr)) {
								  template = sroot.getDeepCopyRule(elemMode.getName());
							  }
							  else if ((Constants.ATTRVAL_SHALLOW_COPY).equals(onNoMatchStr)) {
								  template = sroot.getShallowCopyRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_DEEP_SKIP).equals(onNoMatchStr)) {
								  template = sroot.getDeepSkipRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_SHALLOW_SKIP).equals(onNoMatchStr)) {
								  template = sroot.getShallowSkipRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_FAIL).equals(onNoMatchStr)) {
								  String errMesg = "XTDE0555 : An XSL template declaration could not be found to process an XML " + nodeTypeStr + " node";
								  if (nodeNameStr != null) {
									  errMesg = (errMesg + " '" + nodeNameStr + "'."); 
								  }
								  else {
									  errMesg = (errMesg + "."); 
								  }

								  throw new TransformerException(errMesg, srcLocator);
							  }
							  else {
								  throw new TransformerException("XTTE0505 : An XSL mode instruction's attribute \"on-no-match\" has disallowed "
										  																		+ "value '" + onNoMatchStr + "'.", elemMode);
							  }
						  }

						  if (elemMode.isWarningOnNoMatch()) {
							  /**
							   * Emit an, XSL stylesheet processing warning, when xsl:mode
							   * instruction specifies an attribute 'warning-on-no-match' with
							   * boolean value true.
							   */							  
							  emitXslTransformNoRuleMatchWarning(nodeNameStr, nodeTypeStr, xctxt, srcLocator);
						  }

						  if ((Constants.ATTRVAL_TEXT_ONLY_COPY).equals(onNoMatchStr)) {
							  serializeXdmStringValueWithDefaultXslTextRule(transformer, rth, sroot, dtm, child, template);

							  continue;
						  }
					  }
					  else {
						  template = sroot.getTextOnlyCopyRule(null, nodeType, mode);								  
						  serializeXdmStringValueWithDefaultXslTextRule(transformer, rth, sroot, dtm, child, template);

						  continue;
					  }

					  break;
				  case DTM.CDATA_SECTION_NODE :
				  case DTM.TEXT_NODE :
					  nodeTypeStr = Constants.ELEMNAME_TEXT_STRING;					  
					  if (elemMode != null) {						  						  
						  String nodeNameStr = dtm.getNodeName(child);
						  if (onNoMatchStr != null) {
							  if ((Constants.ATTRVAL_TEXT_ONLY_COPY).equals(onNoMatchStr)) {
								  template = sroot.getTextOnlyCopyRule(elemMode.getName(), nodeType, mode);								  
							  }
							  else if ((Constants.ATTRVAL_DEEP_COPY).equals(onNoMatchStr)) {
								  template = sroot.getDeepCopyRule(elemMode.getName());
							  }
							  else if ((Constants.ATTRVAL_SHALLOW_COPY).equals(onNoMatchStr)) {
								  template = sroot.getDefaultTextRule();								  								  
							  }
							  else if ((Constants.ATTRVAL_DEEP_SKIP).equals(onNoMatchStr)) {
								  template = sroot.getDeepSkipRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_SHALLOW_SKIP).equals(onNoMatchStr)) {
								  template = sroot.getShallowSkipRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_FAIL).equals(onNoMatchStr)) {
								  String errMesg = "XTDE0555 : An XSL template declaration could not be found to process an XML " + nodeTypeStr + " node";
								  if (nodeNameStr != null) {
									  errMesg = (errMesg + " '" + nodeNameStr + "'."); 
								  }
								  else {
									  errMesg = (errMesg + "."); 
								  }

								  throw new TransformerException(errMesg, srcLocator);
							  }
							  else {
								  throw new TransformerException("XTTE0505 : An XSL mode instruction's attribute \"on-no-match\" has disallowed "
										  																		+ "value '" + onNoMatchStr + "'.", elemMode);
							  }
						  }

						  if (elemMode.isWarningOnNoMatch()) {
							  /**
							   * Emit an, XSL stylesheet processing warning, when xsl:mode
							   * instruction specifies an attribute 'warning-on-no-match' with
							   * boolean value true.
							   */							  
							  emitXslTransformNoRuleMatchWarning(nodeNameStr, nodeTypeStr, xctxt, srcLocator);
						  }

						  if ((Constants.ATTRVAL_TEXT_ONLY_COPY).equals(onNoMatchStr) || (Constants.ATTRVAL_SHALLOW_COPY).equals(onNoMatchStr)) {
							  serializeXdmStringValueWithDefaultXslTextRule(transformer, rth, sroot, dtm, child, template);

							  continue;
						  }
					  }
					  else {
						  template = sroot.getDefaultTextRule(); 
						  serializeXdmStringValueWithDefaultXslTextRule(transformer, rth, sroot, dtm, child, template);

						  continue;
					  }

					  break;
				  case DTM.PROCESSING_INSTRUCTION_NODE :
					  nodeTypeStr = Constants.ELEMNAME_PI_STRING;					  
					  if (elemMode != null) {						  						  
						  String nodeNameStr = dtm.getNodeName(child);
						  if (onNoMatchStr != null) {
							  if ((Constants.ATTRVAL_TEXT_ONLY_COPY).equals(onNoMatchStr)) {
								  template = sroot.getTextOnlyCopyRule(elemMode.getName(), nodeType, mode);								  
							  }
							  else if ((Constants.ATTRVAL_DEEP_COPY).equals(onNoMatchStr)) {
								  template = sroot.getDeepCopyRule(elemMode.getName());
							  }
							  else if ((Constants.ATTRVAL_SHALLOW_COPY).equals(onNoMatchStr)) {
								  template = new ElemTemplate();
								  template.setMode(elemMode.getName());
								  template.setStylesheet(sroot);
								  ErrorListener errListener = xctxt.getErrorListener();
								  XPath xpathMatch = new XPath("processing-instruction()", sroot, sroot, XPath.MATCH, errListener);
								  template.setMatch(xpathMatch);

								  ElemCopyOf elemCopyOf = new ElemCopyOf();
								  XPath xpathSelect = new XPath(".", sroot, sroot, XPath.SELECT, errListener);
								  elemCopyOf.setSelect(xpathSelect);
								  template.appendChild(elemCopyOf);
							  }
							  else if ((Constants.ATTRVAL_DEEP_SKIP).equals(onNoMatchStr)) {
								  template = sroot.getDeepSkipRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_SHALLOW_SKIP).equals(onNoMatchStr)) {
								  template = sroot.getShallowSkipRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_FAIL).equals(onNoMatchStr)) {
								  String errMesg = "XTDE0555 : An XSL template declaration could not be found to process an XML " + nodeTypeStr + " node";
								  if (nodeNameStr != null) {
									  errMesg = (errMesg + " '" + nodeNameStr + "'."); 
								  }
								  else {
									  errMesg = (errMesg + "."); 
								  }

								  throw new TransformerException(errMesg, srcLocator);
							  }
							  else {
								  throw new TransformerException("XTTE0505 : An XSL mode instruction's attribute \"on-no-match\" has disallowed "
										  																					+ "value '" + onNoMatchStr + "'.", elemMode);
							  }
						  }

						  if (elemMode.isWarningOnNoMatch()) {
							  /**
							   * Emit an, XSL stylesheet processing warning, when xsl:mode
							   * instruction specifies an attribute 'warning-on-no-match' with
							   * boolean value true.
							   */							  
							  emitXslTransformNoRuleMatchWarning(nodeNameStr, nodeTypeStr, xctxt, srcLocator);
						  }

						  break;
					  }

					  continue;	  
				  case DTM.COMMENT_NODE :
					  nodeTypeStr = Constants.ELEMNAME_COMMENT_STRING;					  
					  if (elemMode != null) {						  						  
						  String nodeNameStr = dtm.getNodeName(child);
						  if (onNoMatchStr != null) {
							  if ((Constants.ATTRVAL_TEXT_ONLY_COPY).equals(onNoMatchStr)) {
								  template = sroot.getTextOnlyCopyRule(elemMode.getName(), nodeType, mode);								  
							  }
							  else if ((Constants.ATTRVAL_DEEP_COPY).equals(onNoMatchStr)) {
								  template = sroot.getDeepCopyRule(elemMode.getName());
							  }
							  else if ((Constants.ATTRVAL_SHALLOW_COPY).equals(onNoMatchStr)) {
								  template = new ElemTemplate();
								  template.setMode(elemMode.getName());
								  template.setStylesheet(sroot);
								  ErrorListener errListener = xctxt.getErrorListener();
								  XPath xpathMatch = new XPath("comment()", sroot, sroot, XPath.MATCH, errListener);
								  template.setMatch(xpathMatch);

								  ElemCopyOf elemCopyOf = new ElemCopyOf();
								  XPath xpathSelect = new XPath(".", sroot, sroot, XPath.SELECT, errListener);
								  elemCopyOf.setSelect(xpathSelect);
								  template.appendChild(elemCopyOf);
							  }
							  else if ((Constants.ATTRVAL_DEEP_SKIP).equals(onNoMatchStr)) {
								  template = sroot.getDeepSkipRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_SHALLOW_SKIP).equals(onNoMatchStr)) {
								  template = sroot.getShallowSkipRule(elemMode.getName(), nodeType, mode);
							  }
							  else if ((Constants.ATTRVAL_FAIL).equals(onNoMatchStr)) {
								  String errMesg = "XTDE0555 : An XSL template declaration could not be found to process an XML " + nodeTypeStr + " node";
								  if (nodeNameStr != null) {
									  errMesg = (errMesg + " '" + nodeNameStr + "'."); 
								  }
								  else {
									  errMesg = (errMesg + "."); 
								  }

								  throw new TransformerException(errMesg, srcLocator);
							  }							  
							  else {
								  throw new TransformerException("XTTE0505 : An XSL mode instruction's attribute \"on-no-match\" has disallowed "
										  																					+ "value '" + onNoMatchStr + "'.", elemMode);
							  }
						  }

						  if (elemMode.isWarningOnNoMatch()) {
							  /**
							   * Emit an, XSL stylesheet processing warning, when xsl:mode
							   * instruction specifies an attribute 'warning-on-no-match' with
							   * boolean value true.
							   */							  
							  emitXslTransformNoRuleMatchWarning(nodeNameStr, nodeTypeStr, xctxt, srcLocator);
						  }

						  break;
					  }

					  continue;
				  case DTM.NAMESPACE_NODE :
					  nodeTypeStr = Constants.ELEMNAME_NAMESPACE_STRING;					  
					  if (elemMode != null) {						  						  
						  String nodeNameStr = dtm.getNodeName(child);
						  if (onNoMatchStr != null) {
							  if ((Constants.ATTRVAL_TEXT_ONLY_COPY).equals(onNoMatchStr) || (Constants.ATTRVAL_DEEP_COPY).equals(onNoMatchStr)
									                                                      || (Constants.ATTRVAL_SHALLOW_COPY).equals(onNoMatchStr)
									                                                      || (Constants.ATTRVAL_DEEP_SKIP).equals(onNoMatchStr)
									                                                      || (Constants.ATTRVAL_SHALLOW_SKIP).equals(onNoMatchStr)) {
								  // Not processing an XML namespace node, for now.
								  // Handling this, further down this method.
							  }
							  else if ((Constants.ATTRVAL_FAIL).equals(onNoMatchStr)) {
								  String errMesg = "XTDE0555 : An XSL template declaration could not be found to process an XML " + nodeTypeStr + " node";
								  if (nodeNameStr != null) {
									  errMesg = (errMesg + " '" + nodeNameStr + "'."); 
								  }
								  else {
									  errMesg = (errMesg + "."); 
								  }

								  throw new TransformerException(errMesg, srcLocator);
							  }
							  else {
								  throw new TransformerException("XTTE0505 : An XSL mode instruction's attribute \"on-no-match\" has disallowed "
										  																					+ "value '" + onNoMatchStr + "'.", elemMode);
							  }
						  }

						  if (elemMode.isWarningOnNoMatch()) {
							  /**
							   * Emit an, XSL stylesheet processing warning, when xsl:mode
							   * instruction specifies an attribute 'warning-on-no-match' with
							   * boolean value true.
							   */							  
							  emitXslTransformNoRuleMatchWarning(nodeNameStr, nodeTypeStr, xctxt, srcLocator);
						  }
						  
						  if ((Constants.ATTRVAL_SHALLOW_COPY).equals(onNoMatchStr) || (Constants.ATTRVAL_DEEP_COPY).equals(onNoMatchStr)) {
							  String xslNamespaceName = dtm.getNodeName(child);
							  String data = dtm.getNodeValue(child);
							  int idx = xslNamespaceName.indexOf(':');
							  if (idx > -1) {
								 xslNamespaceName = xslNamespaceName.substring(idx + 1); 
							  }
							  
							  transformer.getResultTreeHandler().namespaceAfterStartElement(xslNamespaceName, data);
						  }
					  }

					  continue;					  
				  case DTM.DOCUMENT_NODE :
					  template = sroot.getDefaultRootRule();					  
					  break;
				  default :
					  continue;
				  }
			  }
			  else
			  {
				  transformer.setCurrentElement(template);
			  }

			  transformer.pushPairCurrentMatched(template, child);
			  if (check)
				  guard.checkForInfiniteLoop();

			  int currentFrameBottom;

			  XPath templateMatchXPath = template.getMatch();
			  String templateMatchPatternStr = templateMatchXPath.getPatternString();

			  if (template.m_frameSize > 0)
			  {
				  xctxt.pushRTFContext();
				  currentFrameBottom = vars.getStackFrame();
				  vars.link(template.m_frameSize);

				  if (template.m_inArgsSize > 0)
				  {
					  int paramIndex = 0;

					  List<QName> paramNameList = new ArrayList<QName>();

					  for (ElemTemplateElement elem = template.getFirstChildElem(); 
							  null != elem; elem = elem.getNextSiblingElem()) 
					  {
						  if (Constants.ELEMNAME_PARAMVARIABLE == elem.getXSLToken())
						  {
							  ElemParam ep = (ElemParam)elem;
							  QName paramQName = ep.getName();

							  paramNameList.add(paramQName);
							  if (paramNameList.size() > 1) {
								  List<QName> prevList = paramNameList.subList(0, paramNameList.size() - 1);
								  if (prevList.contains(paramQName)) {
									  throw new TransformerException("XTSE0580 : An XSL template parameter name '" + 
											  															paramQName.toString() + "' is not unique.", srcLocator);
								  }
							  }

							  int i;
							  for (i = 0; i < nParams; i++) 
							  {
								  ElemWithParam ewp = m_paramElems[i];
								  if (ewp.m_qnameID == ep.m_qnameID)                  
								  {
									  XObject obj = vars.getLocalVariable(i, argsFrame);									  
									  String paramAsAttrStrVal = ep.getAs();
									  if (paramAsAttrStrVal != null) {
										  XObject argConvertedVal = getParamValueAsAttributeProcessing(obj, templateMatchPatternStr, elem.getPrefixTable(), 
												  																				i, paramAsAttrStrVal, transformer, srcLocator);
										  if (argConvertedVal != null) {
											  if ((obj instanceof XMLNodeCursorImpl) && !(argConvertedVal instanceof XSAnyAtomicType)) {
												  obj = obj.getFresh();
												  vars.setLocalVariable(paramIndex, obj);
											  }
											  else {
												  vars.setLocalVariable(paramIndex, argConvertedVal); 
											  }
										  }
										  else {
											  throw new TransformerException("XPTY0004 : An XSL apply-templates with-param value doesn't match the specified "
                                                                                                                                     + "xdm sequence type " + paramAsAttrStrVal + ".", srcLocator); 
										  }
									  }
									  else {
										  vars.setLocalVariable(paramIndex, obj);
									  }
									  
									  break;
								  }
							  }              
						  }
						  else
							  break;
						  paramIndex++;
					  }                        
				  }                    
			  }
			  else {
				  currentFrameBottom = 0;
			  }

			  if (transformer.getDebug()) {
				  transformer.getTraceManager().emitTraceEvent(template);
			  }

			  String templateAsAttrVal = template.getAs();

			  if (templateAsAttrVal != null) {         
				  try {
					  /**
					   * Check whether an xsl:template element's result contents conform to the
					   * SequenceType expression specified as value of xsl:template element's
					   * 'as' attribute.
					   */

					  int dtmNodeHandle = transformer.transformToGlobalRTF(template);

					  NodeList nodeList2 = (new XRTreeFrag(dtmNodeHandle, xctxt, template)).convertToNodeset();             
					  XObject templateEvalResultForAsAttr = new XNodeSetForDOM(nodeList2, xctxt);

					  templateEvalResultForAsAttr = SequenceTypeSupport.castXdmValueToAnotherType(templateEvalResultForAsAttr, templateAsAttrVal, 
							  																													null, xctxt);
					  if (templateEvalResultForAsAttr != null) {
						  SerializationHandler handler = transformer.getSerializationHandler();        

						  try {
							  if (templateEvalResultForAsAttr instanceof XMLNodeCursorImpl) {
								  ElemCopyOf.copyOfActionOnNodeSet((XMLNodeCursorImpl)templateEvalResultForAsAttr, transformer, 
										  																						handler, xctxt);
							  }
							  else {
								  ElemCopyOf.copyOfActionOnResultSequence((ResultSequence)templateEvalResultForAsAttr, 
										  																			transformer, handler, xctxt, false, this); 
							  }
						  } 
						  catch (TransformerException ex) {
							  throw new TransformerException(ex.getMessage(), srcLocator); 
						  } 
						  catch (SAXException ex) {
							  transformer.getErrorListener().fatalError(new TransformerException(ex)); 
						  }   
					  }
					  else {
						  QName m_name = template.getName();
						  String m_matchPatternStr = (template.getMatch()).getPatternString();
						  String errTemplateStr = (m_name != null) ? m_name.toString() : m_matchPatternStr; 
						  throw new TransformerException("XTTE0505 : The required result type of XSL template '" + errTemplateStr 
																												 + "' is " + templateAsAttrVal + ". But XSL template's "
																												 + "result doesn't conform to this required type.", srcLocator);  
					  }
				  }
				  catch (TransformerException ex) {
					  String trfExpMessage = ex.getMessage();
					  if ((trfExpMessage != null) && trfExpMessage.startsWith("XTTE0590")) {
						  throw ex;   
					  }
					  else {
						  QName m_name = template.getName();
						  String m_matchPatternStr = (template.getMatch()).getPatternString();
						  String errTemplateStr = (m_name != null) ? m_name.toString() : m_matchPatternStr; 
						  throw new TransformerException("XTTE0505 : The required result type of XSL template '" + errTemplateStr 
																											     + "' is " + templateAsAttrVal + ". But XSL template's "
																											     + "result doesn't conform to this required type.", srcLocator);
					  }
				  }  
			  }
			  else {
				  // For the XSL template that matched the context node,
				  // evaluate this XSL stylesheet template's child elements.
				  for (ElemTemplateElement t = template.m_firstChild; 
						  											t != null; t = t.m_nextSibling) {
					  xctxt.setSAXLocator(t);

					  try
					  {
						  transformer.pushElemTemplateElement(t);
						  t.execute(transformer);
					  }
					  finally
					  {
						  transformer.popElemTemplateElement();
					  }
				  }
			  }

			  if (transformer.getDebug()) {
				  transformer.getTraceManager().emitTraceEndEvent(template);
			  }

			  if (template.m_frameSize > 0)
			  {
				  vars.unlink(currentFrameBottom);
				  xctxt.popRTFContext();
			  }

			  transformer.popCurrentMatched();

		  } // end while (DTM.NULL != (child = sourceNodes.nextNode()))
	  }
	  catch (SAXException se)
	  {
		  transformer.getErrorListener().fatalError(new TransformerException(se));
	  }
	  finally
	  {
		  if (transformer.getDebug())
			  transformer.getTraceManager().emitSelectedEndEvent(contextNode, this,
																 "select", new XPath(m_selectExpression2),
																 new org.apache.xpath.objects.XMLNodeCursorImpl(sourceNodes));

		  // Unlink to the original stack frame  
		  if (nParams > 0)
			  vars.unlink(thisframe);
		  xctxt.popSAXLocator();
		  if (pushContextNodeListFlag) xctxt.popContextNodeList();
		  transformer.popElemTemplateElement();
		  xctxt.popCurrentExpressionNode();
		  xctxt.popCurrentNode();
		  sourceNodes.detach();
	  }
  }

  /**
   * Method definition, to do type checking and required modification of xsl:template's xsl:param
   * value using the XPath sequence type expression from xsl:param's 'as' attribute.
   * 
   * @param srcValue								      An xdm value on which type checking and 
   *                                                      value conversion is required.
   * @param templateMatchPatternStr                       xsl:template element's 'match' attribute's value
   * @param prefixTable									  An XSL transformation's run-time XML namespace
   *                                                      prefix table list.
   * @param paramIdx									  An xsl:param's relative index value within sibling 
   *                                                      xsl:param elements. 
   * @param paramAsAttrStrVal							  String value of xsl:param's 'as' attribute
   * @param transformer                                   An XSL transformation run-time TransformerImpl object
   * @param srcLocator									  An XSL transformation SourceLocator object 
   * 
   * @return											  An xdm value produced after conversion using xsl:param's 
   *                                                      XPath sequence type 'as' attribute.
   * @throws TransformerException
   */
  private XObject getParamValueAsAttributeProcessing(XObject srcValue, String templateMatchPatternStr, List prefixTable, 
		                                             int paramIdx, String paramAsAttrStrVal, TransformerImpl transformer, 
		                                             SourceLocator srcLocator) throws TransformerException {

	  XObject argConvertedVal = null;
	  
	  XPathContext xctxt = transformer.getXPathContext();

	  try {
		  XPath seqTypeXPath = new XPath(paramAsAttrStrVal, srcLocator, xctxt.getNamespaceContext(), XPath.SELECT, null, true);            
		  XObject seqTypeExpressionEvalResult = seqTypeXPath.execute(xctxt, xctxt.getContextNode(), xctxt.getNamespaceContext());
		  SequenceTypeData seqExpectedTypeData = (SequenceTypeData)seqTypeExpressionEvalResult;

		  XMLNodeCursorImpl nodeSet = SequenceTypeSupport.getNodeReference(srcValue);
		  if (nodeSet != null) {
			  XSTypeDefinition typeDef = nodeSet.getXsTypeDefinition();                    	                      	                      	  
			  if (typeDef != null) {                    		  
				  XSTypeDefinition typeDef2 = seqExpectedTypeData.getXsTypeDefinition();
				  if (typeDef2 != null && isTypeXsDefinitionEqual(typeDef, typeDef2)) {                            	  
					  argConvertedVal = nodeSet;                      			
				  }
				  else {
					  throw new TransformerException("XTTE0590 : An XSL template's parameter value at index " + (paramIdx + 1) + " for "
																							  + "template {" + templateMatchPatternStr + "} doesn't "
																							  + "match the declared parameter type " + paramAsAttrStrVal + ".", srcLocator);
				  }
			  }                    	  
			  else if (seqExpectedTypeData.getSequenceTypeFunctionTest() != null) {                    		  
				  DTMCursorIterator dtmIter = nodeSet.getContainedIter();                    		  
				  if (dtmIter instanceof NodeTest) {
					  try {
						  ElemFunction elemFunction = XslTransformEvaluationHelper.getElemFunctionFromNodeTestExpression(
								  																					(NodeTest)dtmIter, transformer, srcLocator);
						  if (elemFunction != null) {
							  // REVISIT : To check for elemFunction object's conformance with details in 
							  // the object seqExpectedTypeData.getSequenceTypeFunctionTest()  
							  argConvertedVal = new ElemFunctionItem(elemFunction);
						  }
					  }
					  catch (TransformerException ex) {
						  // no op
					  }
				  }
			  }
		  }
		  else if ((seqExpectedTypeData.getSequenceTypeFunctionTest() != null) && (srcValue instanceof XPathInlineFunction)) {
			  // REVISIT : To check for argValue's conformance with details in 
			  // the object seqExpectedTypeData.getSequenceTypeFunctionTest()
			  argConvertedVal = srcValue; 
		  }

		  if (argConvertedVal == null) {
			  argConvertedVal = SequenceTypeSupport.castXdmValueToAnotherType(srcValue, paramAsAttrStrVal, null, xctxt, prefixTable);
		  }

		  if (argConvertedVal == null) {
			  throw new TransformerException("XTTE0590 : An XSL template's parameter value at index " + (paramIdx + 1) + " for "
																								  + "template {" + templateMatchPatternStr + "} doesn't "
																								  + "match the declared parameter type " + paramAsAttrStrVal + ".", srcLocator);
		  }
	  }
	  catch (TransformerException ex) {
		  if ((SequenceTypeSupport.INLINE_FUNCTION_PARAM_TYPECHECK_COUNT_ERROR).equals(ex.getMessage())) {
			  throw new TransformerException("XTTE0590 : The number of XPath inline function parameters, is not equal to "
					  																			  + "the number of expected type specifications for them.", srcLocator);   
		  }
		  else {
			  throw new TransformerException("XTTE0590 : An XSL template's parameter value at index " + (paramIdx + 1) + " for "
																								  + "template {" + templateMatchPatternStr + "} doesn't "
																								  + "match the declared parameter type " + paramAsAttrStrVal + ".", srcLocator);
		  }
	  }
	  
	  return argConvertedVal;	  
  }

  /**
   * Method definition, to do an xsl:apply-template transformation on 
   * a sequence of xdm atomic values.
   */
  private void xslApplyTemplatesAtomicValueSeq(TransformerImpl transformer, XPathContext xctxt,
		  					                   ResultSequence resultSeq, QName xslTemplateInvokeMode) throws TransformerException {
	  
	  final StylesheetRoot sroot = transformer.getStylesheet();
	  final TemplateList tl = sroot.getTemplateListComposed();
	  
	  Hashtable templateListHashTable = tl.getPatternTable();
	  Set hashTableEntrySet = templateListHashTable.entrySet();
	  
	  int rSeqSize = resultSeq.size();
	  for (int idx = 0; idx < rSeqSize; idx++) {
		  XObject xObj = resultSeq.item(idx);		  
		  xslApplyTemplatesXdmAtomicValue(transformer, xctxt, xObj, hashTableEntrySet, xslTemplateInvokeMode);	  
	  }
  }
  
  /**
   * Method definition, to do an xsl:apply-template transformation on 
   * an xdm atomic value.
   */
  private void xslApplyTemplatesAtomicValue(TransformerImpl transformer, XPathContext xctxt,
		  									XObject contextItem, QName xslTemplateInvokeMode) throws TransformerException {
	  
	  final StylesheetRoot sroot = transformer.getStylesheet();
	  final TemplateList tl = sroot.getTemplateListComposed();

	  Hashtable templateListHashTable = tl.getPatternTable();
	  Set hashTableEntrySet = templateListHashTable.entrySet();

	  xslApplyTemplatesXdmAtomicValue(transformer, xctxt, contextItem, hashTableEntrySet, xslTemplateInvokeMode);
  }

  /**
   * Method definition, to do an xsl:apply-template transformation on 
   * an xdm atomic value.
   */
  private void xslApplyTemplatesXdmAtomicValue(TransformerImpl transformer, XPathContext xctxt, XObject contextItem,
		  									   Set hashTableEntrySet, QName xslTemplateInvokeMode) throws TransformerException {
	  
	  Iterator iter = hashTableEntrySet.iterator();	  
	  
	  ElemTemplate elemTemplate = null;	  
	  
	  SourceLocator srcLocator = xctxt.getSAXLocator();	  
	  
	  XObject prevCtxItem = xctxt.getXPath3ContextItem();
	  int prevContextPos = xctxt.getXPath3ContextPosition();
	  int prevContextSize = xctxt.getXPath3ContextSize();
	  
	  try {
		  xctxt.setXPath3ContextItem(contextItem);

		  // A list of TemplateDefnPriorityPair object instances, is needed to sort 
		  // ElemTemplate object instances according to numeric priority (if this priority 
		  // is not specified within an XSL stylesheet, then its value is 
		  // java double -Infinity which is the min value of java double as 
		  // provided by JDK).  
		  List<TemplateDefnPriorityPair> templateDefnPriorityPairList = new ArrayList<TemplateDefnPriorityPair>();

		  // This while loop finds an ElemTemplate object instance, that will be
		  // used to do an XSL transformation of an XPath data model (xdm) atomic 
		  // value (e.g, xs:string, xs:integer etc) that has been provided as an 
		  // argument to this method.

		  // An xsl:template instance selected for this purpose, is chosen 
		  // according to xsl:template's mode and priority. At run-time, an 
		  // xsl:template instance will always have priority value (which will be 
		  // java lang double -Infinity value if not specified within an 
		  // XSL stylesheet. Otherwise, xsl:template's priority shall be an xs:decimal 
		  // finite numeric value). At run-time, an xsl:template's mode will 
		  // be null when not specified with an XML attribute named 'mode', otherwise 
		  // xsl:template's mode will be an XML QName typed non-null value.

		  boolean templateSelectedWithoutPriority = false;

		  while (iter.hasNext()) {
			  Entry<Object, Object> hashTableEntry = (Entry<Object, Object>)iter.next();
			  Object hashTableKeyObj = hashTableEntry.getKey();
			  TemplateSubPatternAssociation hashTableValueObj = (TemplateSubPatternAssociation)(hashTableEntry.getValue());		
			  elemTemplate = hashTableValueObj.getTemplate();

			  QName templateDefnMode = elemTemplate.getMode();		  		   		   

			  if ((xslTemplateInvokeMode != null) && (templateDefnMode != null)) {
				  if (xslTemplateInvokeMode.equals(templateDefnMode)) {			  
					  templateSelectedWithoutPriority = true;
				  }
			  }
			  else if ((xslTemplateInvokeMode != null) && (templateDefnMode == null)) {
				  templateSelectedWithoutPriority = false; 
			  }
			  else if ((xslTemplateInvokeMode == null) && (templateDefnMode != null)) {
				  templateSelectedWithoutPriority = false; 
			  }
			  else {
				  templateSelectedWithoutPriority = true; 
			  }

			  if (!templateSelectedWithoutPriority) {
				  continue;  
			  }

			  String keyValueStr = (String)hashTableKeyObj;	  
			  String[] strArray1 = keyValueStr.split(Constants.XSL_PATTERN_TABLE_DELIM);
			  if (keyValueStr.startsWith(".[") && (strArray1.length == 3)) {
				  String xslTemplatePriorityStr = strArray1[2];			  
				  String xpathStr = keyValueStr.substring(0, keyValueStr.indexOf(Constants.XSL_PATTERN_TABLE_DELIM));
				  xpathStr = xpathStr.substring(2);
				  xpathStr = xpathStr.substring(0, xpathStr.length() - 1);

				  XPath xpath = new XPath(xpathStr, srcLocator, xctxt.getNamespaceContext(), XPath.SELECT, null);						
				  XObject xpathEvalResult = xpath.execute(xctxt, DTM.NULL, xctxt.getNamespaceContext());						
				  if (xpathEvalResult instanceof XBooleanStatic) {
					  XBooleanStatic xBoolStatic = (XBooleanStatic)xpathEvalResult;
					  templateSelectedWithoutPriority = xBoolStatic.bool(); 
				  }
				  else if (xpathEvalResult instanceof XBoolean) {
					  XBoolean xBoolean = (XBoolean)xpathEvalResult;
					  templateSelectedWithoutPriority = xBoolean.bool(); 
				  }
				  else if (xpathEvalResult instanceof XSBoolean) {
					  XSBoolean xsBoolean = (XSBoolean)xpathEvalResult;
					  templateSelectedWithoutPriority = xsBoolean.bool();
				  }

				  if (templateSelectedWithoutPriority) {
					  TemplateDefnPriorityPair templateDefnPriorityPair = new TemplateDefnPriorityPair(keyValueStr, elemTemplate, 
							                                                                                    (Double.valueOf(xslTemplatePriorityStr)).doubleValue());
					  templateDefnPriorityPairList.add(templateDefnPriorityPair);
				  }
			  }

		  } // while (iter.hasNext())

		  if (templateDefnPriorityPairList.size() == 0) {		  
			  // Reset iterator
			  iter = hashTableEntrySet.iterator();

			  while (iter.hasNext()) {
				  Entry<Object, Object> hashTableEntry = (Entry<Object, Object>)iter.next();
				  Object hashTableKeyObj = hashTableEntry.getKey();
				  TemplateSubPatternAssociation hashTableValueObj = (TemplateSubPatternAssociation)(hashTableEntry.getValue());		
				  elemTemplate = hashTableValueObj.getTemplate();

				  QName templateDefnMode = elemTemplate.getMode();		  		   		   

				  if ((xslTemplateInvokeMode != null) && (templateDefnMode != null)) {
					  if (xslTemplateInvokeMode.equals(templateDefnMode)) {			  
						  templateSelectedWithoutPriority = true;
					  }
				  }
				  else if ((xslTemplateInvokeMode != null) && (templateDefnMode == null)) {
					  templateSelectedWithoutPriority = false; 
				  }
				  else if ((xslTemplateInvokeMode == null) && (templateDefnMode != null)) {
					  templateSelectedWithoutPriority = false; 
				  }
				  else {
					  templateSelectedWithoutPriority = true; 
				  }

				  if (!templateSelectedWithoutPriority) {
					  continue;  
				  }

				  String keyValueStr = (String)hashTableKeyObj;			  
				  String[] strArray1 = keyValueStr.split(Constants.XSL_PATTERN_TABLE_DELIM);
				  if (".".equals(strArray1[0]) && (strArray1.length == 3)) {
					  String xslTemplatePriorityStr = strArray1[2];
					  TemplateDefnPriorityPair templateDefnPriorityPair = new TemplateDefnPriorityPair(keyValueStr, elemTemplate, 
							                                                                                     (Double.valueOf(xslTemplatePriorityStr)).doubleValue());
					  templateDefnPriorityPairList.add(templateDefnPriorityPair);

				  }

			  } // while (iter.hasNext())
		  }

		  templateDefnPriorityPairList.sort(null);

		  TemplateDefnPriorityPair templateDefnPriorityPair = templateDefnPriorityPairList.get(templateDefnPriorityPairList.size() - 1);

		  elemTemplate = templateDefnPriorityPair.getElemTemplate();

		  for (ElemTemplateElement t1 = elemTemplate.m_firstChild; t1 != null; 
				                                                           t1 = t1.m_nextSibling) {
			  xctxt.setSAXLocator(t1);
			  transformer.pushElemTemplateElement(t1);    		   
			  t1.execute(transformer);
			  transformer.popElemTemplateElement();
		  }
      }
      finally {
	     xctxt.setXPath3ContextItem(prevCtxItem);
	     xctxt.setXPath3ContextPosition(prevContextPos);
	     xctxt.setXPath3ContextSize(prevContextSize);
      }
  }

  /**
   * Method definition, to check whether all xdm items within the 
   * supplied ResultSequence object are atomic values. 
   */
  private boolean isAllSeqItemsXdmAtomicValues(ResultSequence resultSeq) {
	  boolean result = true;
	
	  for (int idx = 0; idx < resultSeq.size(); idx++) {
		 XObject xObj = resultSeq.item(idx);
		 if (!isXdmItemAtomicValue(xObj)) {
			result = false;
			break;
		 }
	  }
	  
	  return result;
  }
  
  /**
   * Method definition, to check whether the supplied xdm item 
   * is an atomic value.
   */
  private boolean isXdmItemAtomicValue(XObject xObj) {
	  boolean result = false;
	  
	  if ((xObj instanceof XNumber) || (xObj instanceof XBoolean) || 
		  (xObj instanceof XBooleanStatic) || (xObj instanceof XString) || (xObj instanceof XSAnyAtomicType)) {
	      result = true;
	  }
	  
	  return result;
  }
  
  /**
   * Method definition, to check whether two XSTypeDefinition objects, 
   * represent the same schema type. 
   */
  private boolean isTypeXsDefinitionEqual(XSTypeDefinition typeDef1, XSTypeDefinition typeDef2) {	  
	  boolean result = false;

	  String typeName1 = typeDef1.getName();
	  String typeNamespace1 = typeDef1.getNamespace();

	  String typeName2 = typeDef2.getName();
	  String typeNamespace2 = typeDef2.getNamespace();
	  
	  if ((typeNamespace1 == null) && (typeNamespace2 == null)) {
		 result = typeName1.equals(typeName2);   
	  }
	  else if (((typeNamespace1 == null) && (typeNamespace2 != null)) || ((typeNamespace1 != null) && (typeNamespace2 == null))) {
		 result = false; 
	  }
	  else if (typeNamespace1.equals(typeNamespace2) && typeName1.equals(typeName2)) {
		 result = true; 
	  }

	  return result;
  }
  
  /**
   * A list containing objects of this class, is sortable in an ascending
   * order by numeric xs:double priority values.
   */
  class TemplateDefnPriorityPair implements Comparable<TemplateDefnPriorityPair> {
	  
	  private String m_keyValueStr = null;
	  
	  private ElemTemplate m_elemTemplate = null;
	  
	  private double m_priority;
	  
	  /**
	   * Default constructor.
	   */
	  public TemplateDefnPriorityPair() {
		 // no op  
	  }
	  
	  /**
	   * Class constructor.
	   */
	  public TemplateDefnPriorityPair(String keyValueStr, ElemTemplate elemTemplate, double priority) {
		 this.m_keyValueStr = keyValueStr;
		 this.m_elemTemplate = elemTemplate;
		 this.m_priority = priority;
	  }

	  public ElemTemplate getElemTemplate() {
		  return m_elemTemplate;
	  }

	  public void setElemTemplate(ElemTemplate elemTemplate) {
		  this.m_elemTemplate = elemTemplate;
	  }

	  public String getKeyValueStr() {
		  return m_keyValueStr;
	  }

	  public void setKeyValueStr(String keyValueStr) {
		  this.m_keyValueStr = keyValueStr;
	  }

	  public double getPriority() {
		  return m_priority;
	  }

	  public void setPriority(double priority) {
		  this.m_priority = priority;
	  }

	  @Override
	  public int compareTo(TemplateDefnPriorityPair obj1) {
		 int result = 0;
		 
		 if (this.m_priority < obj1.getPriority()) {
			result = -1; 
		 }
		 else if (this.m_priority > obj1.getPriority()) {
			result = 1;  
		 }
		 
		 return result;
	  }

  }
  
  /**
   * Method definition, to serialize an xdm string value to XSL 
   * result tree using XSLT template default rule for text nodes.
   * 
   * @param transformer							   TransformerImpl object instance 
   * @param rth									   SerializationHandler object instance		
   * @param sroot								   StylesheetRoot object instance	
   * @param dtm									   DTM object instance for an xdm node	
   * @param nodeHandle                             Node handle for an xdm node
   * @param template                               XSL template default rule for text nodes     
   * @throws SAXException
   */
  private void serializeXdmStringValueWithDefaultXslTextRule(TransformerImpl transformer, SerializationHandler rth,
                                                             StylesheetRoot sroot, DTM dtm, int nodeHandle, 
                                                             ElemTemplate template) throws SAXException {
	  transformer.pushPairCurrentMatched(template, nodeHandle);
	  transformer.setCurrentElement(template);
	  dtm.dispatchCharactersEvents(nodeHandle, rth, false);
	  transformer.popCurrentMatched();
  }
  
  /**
   * Method definition, to emit an, XSL transformation processing warning, when 
   * an xdm input node is not matched by XSL transformation user-written 
   * template rule.
   * 
   * @param nodeNameStr							  An Xdm node name
   * @param nodeTypeStr                           An xdm node type string value
   * @param xctxt                                 XPathContext object instance
   * @param srcLocator                            XSL transformation SourceLocator object
   * @throws TransformerException
   */
  private void emitXslTransformNoRuleMatchWarning(String nodeNameStr, String nodeTypeStr, 
		                                          XPathContext xctxt, SourceLocator srcLocator) throws TransformerException {	  	  	  
	  ErrorListener errListener = xctxt.getErrorListener();									 
	  String errMesg = "Warning : An XSL template declaration could not be found to process an XML " + nodeTypeStr + " node";
	  if (nodeNameStr != null) {
		  errMesg = (errMesg + " '" + nodeNameStr + "'."); 
	  }
	  else {
		  errMesg = (errMesg + "."); 
	  }

	  errListener.warning(new TransformerException(errMesg, srcLocator));
  }

}
