blob: 9c29b12d3169403f54388173ba5861a9cba32d58 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 Standards for Technology in Automotive Retail
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* David Carver - STAR - bug 224197 - initial API and implementation
* based on work from Apache Xalan 2.7.0
*******************************************************************************/
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed 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.
*/
/*
* $Id: ElemApplyTemplates.java,v 1.3 2008/03/28 02:38:15 dacarver Exp $
*/
package org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates;
import java.util.Vector;
import javax.xml.transform.TransformerException;
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.transformer.StackGuard;
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.transformer.TransformerImpl;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xml.serializer.SerializationHandler;
import org.apache.xml.utils.IntStack;
import org.apache.xml.utils.QName;
import org.apache.xpath.VariableStack;
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.xpath.XPath;
import org.apache.xpath.XPathContext;
import org.apache.xpath.objects.XObject;
import org.xml.sax.SAXException;
/**
* Implement xsl:apply-templates.
*
* <pre>
* &amp;!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*&gt;
* &amp;!ATTLIST xsl:apply-templates
* select %expr; &quot;node()&quot;
* mode %qname; #IMPLIED
* &amp;
* </pre>
*
* @see <a
* href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules
* in XSLT Specification</a>
* @xsl.usage advanced
*/
public class ElemApplyTemplates extends ElemCallTemplate {
static final long serialVersionUID = 2903125371542621004L;
/**
* mode %qname; #IMPLIED
*
* @serial
*/
private QName m_mode = null;
/**
* Set the mode attribute for this element.
*
* @param mode
* reference, which may be null, to the <a
* href="http://www.w3.org/TR/xslt#modes">current mode</a>.
*/
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;
}
/**
* Tells if this belongs to a default template, in which case it will act
* different with regard to processing modes.
*
* @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in
* XSLT Specification</a>
* @serial
*/
private boolean m_isDefaultTemplate = false;
// /**
// * List of namespace/localname IDs, for identification of xsl:with-param
// to
// * xsl:params. Initialized in the compose() method.
// */
// private int[] m_paramIDs;
/**
* Set if this belongs to a default template, in which case it will act
* different with regard to processing modes.
*
* @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in
* XSLT Specification</a>
*
* @param b
* boolean value to set.
*/
public void setIsDefaultTemplate(boolean b) {
m_isDefaultTemplate = b;
}
/**
* Get an int constant identifying the type of element.
*
* @see org.apache.xalan.templates.Constants
*
* @return Token ID for this element types
*/
@Override
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.
*/
@Override
public void compose(StylesheetRoot sroot) throws TransformerException {
super.compose(sroot);
}
/**
* Return the node name.
*
* @return Element name
*/
@Override
public String getNodeName() {
return Constants.ELEMNAME_APPLY_TEMPLATES_STRING;
}
/**
* Apply the context node to the matching templates.
*
* @see <a
* href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules
* in XSLT Specification</a>
*
* @param transformer
* non-null reference to the the current transform-time state.
*
* @throws TransformerException
*/
@Override
public void execute(TransformerImpl transformer)
throws TransformerException {
transformer.pushCurrentTemplateRuleIsNull(false);
boolean pushMode = false;
try {
// %REVIEW% Do we need this check??
// if (null != sourceNode)
// {
// boolean needToTurnOffInfiniteLoopCheck = false;
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().fireTraceEvent(this);
transformSelectedNodes(transformer);
} finally {
if (transformer.getDebug())
transformer.getTraceManager().fireTraceEndEvent(this);
if (pushMode)
transformer.popMode();
transformer.popCurrentTemplateRuleIsNull();
}
}
/**
* Perform a query if needed, and call transformNode for each child.
*
* @param transformer
* non-null reference to the the current transform-time state.
*
* @throws TransformerException
* Thrown in a variety of circumstances.
* @xsl.usage advanced
*/
@Override
public void transformSelectedNodes(TransformerImpl transformer)
throws TransformerException {
final XPathContext xctxt = transformer.getXPathContext();
final int sourceNode = xctxt.getCurrentNode();
DTMIterator sourceNodes = m_selectExpression.asIterator(xctxt,
sourceNode);
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);
final Vector keys = (m_sortElems == null) ? null : transformer
.processSortKeys(this, sourceNode);
// Sort if we need to.
if (null != keys)
sourceNodes = sortNodes(xctxt, keys, sourceNodes);
if (transformer.getDebug()) {
transformer.getTraceManager().fireSelectedEvent(sourceNode,
this, "select", new XPath(m_selectExpression),
new org.apache.xpath.objects.XNodeSet(sourceNodes));
}
final SerializationHandler rth = transformer
.getSerializationHandler();
// ContentHandler chandler = rth.getContentHandler();
final StylesheetRoot sroot = transformer.getStylesheet();
final TemplateList tl = sroot.getTemplateListComposed();
final boolean quiet = transformer.getQuietConflictWarnings();
// Should be able to get this from the iterator but there must be a
// bug.
DTM dtm = xctxt.getDTM(sourceNode);
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);
for (int i = 0; i < nParams; i++) {
ElemWithParam ewp = m_paramElems[i];
if (transformer.getDebug())
transformer.getTraceManager().fireTraceEvent(ewp);
XObject obj = ewp.getValue(transformer, sourceNode);
if (transformer.getDebug())
transformer.getTraceManager().fireTraceEndEvent(ewp);
vars.setLocalVariable(i, obj, argsFrame);
}
vars.setStackFrame(argsFrame);
}
xctxt.pushContextNodeList(sourceNodes);
pushContextNodeListFlag = true;
IntStack currentNodes = xctxt.getCurrentNodeStack();
IntStack currentExpressionNodes = xctxt
.getCurrentExpressionNodeStack();
// pushParams(transformer, xctxt);
int child;
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);
final QName mode = transformer.getMode();
ElemTemplate template = tl.getTemplateFast(xctxt, child,
exNodeType, mode, -1, quiet, dtm);
// If that didn't locate a node, fall back to a default template
// rule.
// See http://www.w3.org/TR/xslt#built-in-rule.
if (null == template) {
switch (nodeType) {
case DTM.DOCUMENT_FRAGMENT_NODE:
case DTM.ELEMENT_NODE:
template = sroot.getDefaultRule();
// %OPT% direct faster?
break;
case DTM.ATTRIBUTE_NODE:
case DTM.CDATA_SECTION_NODE:
case DTM.TEXT_NODE:
// if(rth.m_elemIsPending || rth.m_docPending)
// rth.flushPending(true);
transformer.pushPairCurrentMatched(sroot
.getDefaultTextRule(), child);
transformer.setCurrentElement(sroot
.getDefaultTextRule());
// dtm.dispatchCharactersEvents(child, chandler, false);
dtm.dispatchCharactersEvents(child, rth, false);
transformer.popCurrentMatched();
continue;
case DTM.DOCUMENT_NODE:
template = sroot.getDefaultRootRule();
break;
default:
// No default rules for processing instructions and the
// like.
continue;
}
} else {
transformer.setCurrentElement(template);
}
transformer.pushPairCurrentMatched(template, child);
if (check)
guard.checkForInfinateLoop();
int currentFrameBottom; // See comment with unlink, below
if (template.m_frameSize > 0) {
xctxt.pushRTFContext();
currentFrameBottom = vars.getStackFrame(); // See comment
// with unlink,
// below
vars.link(template.m_frameSize);
// You can't do the check for nParams here, otherwise the
// xsl:params might not be nulled.
if (/* nParams > 0 && */template.m_inArgsSize > 0) {
int paramIndex = 0;
for (ElemTemplateElement elem = template
.getFirstChildElem(); null != elem; elem = elem
.getNextSiblingElem()) {
if (Constants.ELEMNAME_PARAMVARIABLE == elem
.getXSLToken()) {
ElemParam ep = (ElemParam) elem;
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);
vars.setLocalVariable(paramIndex, obj);
break;
}
}
if (i == nParams)
vars.setLocalVariable(paramIndex, null);
} else
break;
paramIndex++;
}
}
} else
currentFrameBottom = 0;
// Fire a trace event for the template.
if (transformer.getDebug())
transformer.getTraceManager().fireTraceEvent(template);
// And execute the child templates.
// Loop through the children of the template, calling execute on
// each of them.
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().fireTraceEndEvent(template);
if (template.m_frameSize > 0) {
// See Frank Weiss bug around 03/19/2002 (no Bugzilla report
// yet).
// While unlink will restore to the proper place, the real
// position
// may have been changed for xsl:with-param, so that
// variables
// can be accessed.
// of right now.
// More:
// When we entered this function, the current
// frame buffer (cfb) index in the variable stack may
// have been manually set. If we just call
// unlink(), however, it will restore the cfb to the
// previous link index from the link stack, rather than
// the manually set cfb. So,
// the only safe solution is to restore it back
// to the same position it was on entry, since we're
// really not working in a stack context here. (Bug4218)
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().fireSelectedEndEvent(sourceNode,
this, "select", new XPath(m_selectExpression),
new org.apache.xpath.objects.XNodeSet(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();
}
}
}