You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xalan.apache.org by mu...@apache.org on 2023/06/16 06:20:37 UTC
[xalan-java] branch xalan-j_xslt3.0 updated: improving xsl:iterate instruction's evaluation, as per XSLT 3.0 spec for xsl:iterate. adding few new related working test cases as well.
This is an automated email from the ASF dual-hosted git repository.
mukulg pushed a commit to branch xalan-j_xslt3.0
in repository https://gitbox.apache.org/repos/asf/xalan-java.git
The following commit(s) were added to refs/heads/xalan-j_xslt3.0 by this push:
new e964417a improving xsl:iterate instruction's evaluation, as per XSLT 3.0 spec for xsl:iterate. adding few new related working test cases as well.
e964417a is described below
commit e964417a97e490cd0daf70ecee5fd0938654749a
Author: Mukul Gandhi <ga...@gmail.com>
AuthorDate: Fri Jun 16 11:50:19 2023 +0530
improving xsl:iterate instruction's evaluation, as per XSLT 3.0 spec for xsl:iterate. adding few new related working test cases as well.
---
src/org/apache/xalan/templates/ElemIterate.java | 179 ++++-----------------
.../apache/xalan/templates/ElemIterateBreak.java | 55 +++++--
.../xalan/templates/ElemIterateNextIteration.java | 117 +++++++++++++-
.../xalan/templates/ElemTemplateElement.java | 56 +++++++
.../templates/XslIterateParamWithparamData.java | 69 ++++++++
tests/org/apache/xalan/xslt3/XslIterateTests.java | 20 +++
tests/xsl_iterate/gold/test11.out | 6 +
tests/xsl_iterate/gold/test12.out | 5 +
tests/xsl_iterate/iterate-003.xsl | 2 +-
tests/xsl_iterate/{iterate-003.xsl => test11.xsl} | 49 +++---
tests/xsl_iterate/test12.xsl | 62 +++++++
tests/xsl_iterate/test1_c.xml | 7 +
tests/xsl_iterate/test1_d.xml | 8 +
13 files changed, 444 insertions(+), 191 deletions(-)
diff --git a/src/org/apache/xalan/templates/ElemIterate.java b/src/org/apache/xalan/templates/ElemIterate.java
index 9ee7c2b1..c3bb4885 100644
--- a/src/org/apache/xalan/templates/ElemIterate.java
+++ b/src/org/apache/xalan/templates/ElemIterate.java
@@ -26,17 +26,12 @@ import org.apache.xalan.transformer.TransformerImpl;
import org.apache.xalan.xslt.util.XslTransformErrorLocatorHelper;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMIterator;
-import org.apache.xml.dtm.DTMManager;
import org.apache.xml.utils.IntStack;
import org.apache.xml.utils.QName;
import org.apache.xpath.Expression;
import org.apache.xpath.ExpressionOwner;
-import org.apache.xpath.VariableStack;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathContext;
-import org.apache.xpath.objects.XObject;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
/**
* XSLT 3.0 xsl:iterate element.
@@ -92,10 +87,11 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
private static final long serialVersionUID = -2692900882677332482L;
private static final String OTHER_ELEM = "OTHER_ELEM";
-
- private List<ParamWithparamData> fParamList = new ArrayList<ParamWithparamData>();
- private List<ParamWithparamData> fWithparamList = new ArrayList<ParamWithparamData>();
+ // revisit.
+ // can we have better way to maintain xsl:iterate->xsl:param* state, instead of having this with
+ // 'public static' visibility.
+ public static List<XslIterateParamWithparamData> fParamList = new ArrayList<XslIterateParamWithparamData>();
/**
* Construct an element representing xsl:iterate.
@@ -218,6 +214,10 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
final int sourceNode = xctxt.getCurrentNode();
+ // clear the, xsl:iterate->xsl:param* list storage before this xsl:iterate
+ // instruction's evaluation.
+ fParamList.clear();
+
validateXslElemIterateChildElementsSequence(xctxt);
DTMIterator sourceNodes = m_selectExpression.asIterator(xctxt, sourceNode);
@@ -234,8 +234,7 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
xctxt.pushSAXLocatorNull();
xctxt.pushContextNodeList(sourceNodes);
transformer.pushElemTemplateElement(null);
-
- int docID = sourceNode & DTMManager.IDENT_DTM_DEFAULT;
+
int child;
ElemIterateOnCompletion xslOnCompletionTemplate = null;
@@ -243,40 +242,23 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
while ((child = sourceNodes.nextNode()) != DTM.NULL) {
currentNodes.setTop(child);
currentExpressionNodes.setTop(child);
-
- if ((child & DTMManager.IDENT_DTM_DEFAULT) != docID)
- {
- docID = child & DTMManager.IDENT_DTM_DEFAULT;
- }
-
+
for (ElemTemplateElement elemTemplate = this.m_firstChild; elemTemplate != null;
elemTemplate = elemTemplate.m_nextSibling) {
if ((elemTemplate instanceof ElemIterateOnCompletion) &&
(xslOnCompletionTemplate == null)) {
xslOnCompletionTemplate = (ElemIterateOnCompletion)elemTemplate;
}
- else if (elemTemplate instanceof ElemIterateNextIteration) {
- VariableStack varStack = xctxt.getVarStack();
- for (int idx = 0; idx < fWithparamList.size(); idx++) {
- ParamWithparamData withParamData = fWithparamList.get(idx);
- XPath withParamSelectVal = withParamData.getSelectVal();
- XObject evalResult = withParamSelectVal.execute(xctxt, child, this);
- // update value of current xsl:next-iteration's current xsl:param
- // 'parameter'. when xsl:iterate's new iteration is entered, this
- // parameter shall have this new value.
- varStack.setLocalVariable(idx, evalResult);
- }
- }
- if (!((XslTransformErrorLocatorHelper.isXslIterateBreakEvaluated).booleanValue())) {
+ if (!(XslTransformErrorLocatorHelper.isXslIterateBreakEvaluated).booleanValue()) {
xctxt.setSAXLocator(elemTemplate);
transformer.setCurrentElement(elemTemplate);
elemTemplate.execute(transformer);
}
else {
break;
- }
- }
+ }
+ }
if ((XslTransformErrorLocatorHelper.isXslIterateBreakEvaluated).booleanValue()) {
break;
@@ -285,14 +267,14 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
if ((xslOnCompletionTemplate != null) && !(XslTransformErrorLocatorHelper.
isXslIterateBreakEvaluated).booleanValue()) {
- XslTransformErrorLocatorHelper.isXslIterateOnCompletionActive = Boolean.TRUE;
- xctxt.setSAXLocator(xslOnCompletionTemplate);
- transformer.setCurrentElement(xslOnCompletionTemplate);
- xslOnCompletionTemplate.execute(transformer);
- XslTransformErrorLocatorHelper.isXslIterateOnCompletionActive = Boolean.FALSE;
+ XslTransformErrorLocatorHelper.isXslIterateOnCompletionActive = Boolean.TRUE;
+ xctxt.setSAXLocator(xslOnCompletionTemplate);
+ transformer.setCurrentElement(xslOnCompletionTemplate);
+ xslOnCompletionTemplate.execute(transformer);
+ XslTransformErrorLocatorHelper.isXslIterateOnCompletionActive = Boolean.FALSE;
}
- XslTransformErrorLocatorHelper.isXslIterateBreakEvaluated = Boolean.FALSE;
+ XslTransformErrorLocatorHelper.isXslIterateBreakEvaluated = Boolean.FALSE;
}
finally {
xctxt.popSAXLocator();
@@ -310,9 +292,8 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
/*
* The XSLT 3.0 spec specifies constraints, about what should be the order of XSLT elements
- * xsl:param, xsl:on-completion and xsl:next-iteration within the xsl:iterate element. This
- * method ensures that, these XSLT xsl:iterate element constraints are validated during
- * an XSLT document transformation.
+ * xsl:param and xsl:on-completion within the xsl:iterate element. This method ensures that,
+ * these XSLT element constraints are validated during an XSLT stylesheet transformation.
*/
private void validateXslElemIterateChildElementsSequence(XPathContext xctxt)
throws TransformerException {
@@ -327,31 +308,23 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
}
else if (elemTemplate instanceof ElemIterateOnCompletion) {
xslElemNamesList.add(Constants.ELEMNAME_ITERATE_ONCOMPLETION_STRING);
- }
- else if (elemTemplate instanceof ElemIterateNextIteration) {
- xslElemNamesList.add(Constants.ELEMNAME_ITERATE_NEXTITERATION_STRING);
- }
+ }
else {
xslElemNamesList.add(OTHER_ELEM);
}
}
// get index of specific item's first occurrence with the list object 'xslElemNamesList'.
- // if a particular kind of xdm item that is checked is not present within the list object
- // 'xslElemNamesList', its index is returned as -1.
+ // if a particular kind of XSLT stylesheet item that is checked is not present within the
+ // list object 'xslElemNamesList', its index is returned as -1.
int paramIdx = xslElemNamesList.indexOf(Constants.ELEMNAME_PARAMVARIABLE_STRING);
- int onCompletionIdx = xslElemNamesList.indexOf(Constants.ELEMNAME_ITERATE_ONCOMPLETION_STRING);
- int nextIterationIdx = xslElemNamesList.indexOf(Constants.ELEMNAME_ITERATE_NEXTITERATION_STRING);
+ int onCompletionIdx = xslElemNamesList.indexOf(Constants.ELEMNAME_ITERATE_ONCOMPLETION_STRING);
int otherElemIdx = xslElemNamesList.indexOf(OTHER_ELEM);
if ((paramIdx != -1) && (onCompletionIdx != -1) && (paramIdx > onCompletionIdx)) {
throw new TransformerException("XTSE0010 : an xsl:param element must occur before xsl:on-completion "
+ "element.", xctxt.getSAXLocator());
- }
- else if ((paramIdx != -1) && (nextIterationIdx != -1) && (paramIdx > nextIterationIdx)) {
- throw new TransformerException("XTSE0010 : an xsl:param element must occur before xsl:next-iteration "
- + "element.", xctxt.getSAXLocator());
- }
+ }
else if ((paramIdx != -1) && (otherElemIdx != -1) && (paramIdx > otherElemIdx)) {
throw new TransformerException("XTSE0010 : an xsl:param element must occur before any other element within "
+ "xsl:iterate element.", xctxt.getSAXLocator());
@@ -360,22 +333,8 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
(otherElemIdx < onCompletionIdx)) {
throw new TransformerException("XTSE0010 : an xsl:on-completion element must be the first child element of xsl:iterate "
+ "after the xsl:param elements.", xctxt.getSAXLocator());
- }
- else if ((onCompletionIdx != -1) && (nextIterationIdx != -1) && (onCompletionIdx > nextIterationIdx)) {
- throw new TransformerException("XTSE0010 : an xsl:on-completion element must occur before xsl:next-iteration "
- + "element.", xctxt.getSAXLocator());
- }
- else if ((nextIterationIdx != -1) && (nextIterationIdx != (xslElemNamesList.size() - 1))) {
- throw new TransformerException("XTSE3120 : an xsl:next-iteration element when present, must be the last instruction within "
- + "an xsl:iterate loop.", xctxt.getSAXLocator());
- }
-
- // Validate the, xsl:iterate->xsl:param* and xsl:next-iteration->xsl:with-param* names, as per following XSLT 3.0
- // spec requirements,
- // 1) All the xsl:param names must be unique.
- // 2) All the xsl:next-iteration->xsl:with-param names must be unique.
- // 3) Value of name attribute of xsl:param's must be pair-wise equal to value of name attribute of
- // xsl:next-iteration->xsl:with-param.
+ }
+
if (paramIdx != -1) {
for (ElemTemplateElement elemTemplate = this.m_firstChild; elemTemplate != null;
elemTemplate = elemTemplate.m_nextSibling) {
@@ -383,7 +342,7 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
ElemParam paramElem = (ElemParam)elemTemplate;
QName paramNameVal = paramElem.getName();
XPath paramSelectXPath = paramElem.getSelect();
- ParamWithparamData paramWithparamDataObj = new ParamWithparamData();
+ XslIterateParamWithparamData paramWithparamDataObj = new XslIterateParamWithparamData();
paramWithparamDataObj.setNameVal(paramNameVal);
paramWithparamDataObj.setSelectVal(paramSelectXPath);
if (fParamList.contains(paramWithparamDataObj)) {
@@ -393,86 +352,8 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
else {
fParamList.add(paramWithparamDataObj);
}
- }
- else if (elemTemplate instanceof ElemIterateNextIteration) {
- ElemIterateNextIteration elemIterateNextIteration = (ElemIterateNextIteration)elemTemplate;
- NodeList nextIterationChildNodes = elemIterateNextIteration.getChildNodes();
- for (int idx = 0; idx < nextIterationChildNodes.getLength(); idx++) {
- Node nextIterChild = nextIterationChildNodes.item(idx);
- if (nextIterChild instanceof ElemWithParam) {
- ElemWithParam withParamElem = (ElemWithParam)nextIterChild;
- QName withParamNameVal = withParamElem.getName();
- XPath withParamSelectXPath = withParamElem.getSelect();
- ParamWithparamData paramWithparamDataObj = new ParamWithparamData();
- paramWithparamDataObj.setNameVal(withParamNameVal);
- paramWithparamDataObj.setSelectVal(withParamSelectXPath);
- if (fWithparamList.contains(paramWithparamDataObj)) {
- throw new TransformerException("XTSE0670 : duplicate xsl:with-param parameter name '" + withParamNameVal +
- "'", xctxt.getSAXLocator());
- }
- else {
- fWithparamList.add(paramWithparamDataObj);
- }
- }
- }
- }
- }
-
- if (fParamList.size() != fWithparamList.size()) {
- throw new TransformerException("XTSE0580 : within xsl:iterate, the number of xsl:param elements are not equal to "
- + "number of xsl:next-iteration's xsl:with-param elements.", xctxt.getSAXLocator());
- }
- else {
- for (int idx = 0; idx < fParamList.size(); idx ++) {
- ParamWithparamData paramData = fParamList.get(idx);
- ParamWithparamData withParamData = fWithparamList.get(idx);
- if (!(paramData.getNameVal()).equals(withParamData.getNameVal())) {
- throw new TransformerException("XTSE3130 : within xsl:iterate, xsl:param and xsl:with-param names at position " +
- (idx + 1) + " are not same.", xctxt.getSAXLocator());
- }
- }
- }
- }
-
- }
-
- /*
- * An object of this class, stores information about, one xsl:param
- * element or one xsl:next-iteration->xsl:with-param element, for a
- * particular xsl:iterate instruction.
- */
- class ParamWithparamData {
-
- public QName nameVal;
-
- public XPath selectVal;
-
- public QName getNameVal() {
- return nameVal;
- }
-
- public void setNameVal(QName nameVal) {
- this.nameVal = nameVal;
- }
-
- public XPath getSelectVal() {
- return selectVal;
- }
-
- public void setSelectVal(XPath selectVal) {
- this.selectVal = selectVal;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
+ }
}
- ParamWithparamData paramWithparamData = (ParamWithparamData)obj;
- return nameVal.equals(paramWithparamData.getNameVal());
}
}
diff --git a/src/org/apache/xalan/templates/ElemIterateBreak.java b/src/org/apache/xalan/templates/ElemIterateBreak.java
index 2135d561..6bb57128 100644
--- a/src/org/apache/xalan/templates/ElemIterateBreak.java
+++ b/src/org/apache/xalan/templates/ElemIterateBreak.java
@@ -154,16 +154,28 @@ public class ElemIterateBreak extends ElemTemplateElement implements ExpressionO
*/
public void execute(TransformerImpl transformer) throws TransformerException
{
- if (this.m_nextSibling != null) {
- XPathContext xpathContext = transformer.getXPathContext();
- throw new TransformerException("XTSE3120 : an xsl:break instruction must not have any other "
- + "stylesheet element as its following sibling.",
- xpathContext.getSAXLocator());
- }
- else {
- transformXslBreakInstruction(transformer);
- XslTransformErrorLocatorHelper.isXslIterateBreakEvaluated = Boolean.TRUE;
- }
+ XPathContext xpathContext = transformer.getXPathContext();
+
+ boolean isXslBreakDescendantOfXslIterate = false;
+
+ if (isXslBreakDescendantOfXslIterate(this)) {
+ isXslBreakDescendantOfXslIterate = true;
+ }
+ else {
+ throw new TransformerException("XTSE3120 : an xsl:break instruction doesn't have "
+ + "xsl:iterate instruction as ancestor.",
+ xpathContext.getSAXLocator());
+ }
+
+ if (isXslBreakDescendantOfXslIterate && isXslInstructionInTailPositionOfSequenceConstructor(this)) {
+ transformXslBreakInstruction(transformer);
+ XslTransformErrorLocatorHelper.isXslIterateBreakEvaluated = Boolean.TRUE;
+ }
+ else {
+ throw new TransformerException("XTSE3120 : an xsl:break instruction is not in a "
+ + "tail position within the sequence constructor of currently "
+ + "active xsl:iterate instruction.", xpathContext.getSAXLocator());
+ }
}
/**
@@ -203,5 +215,28 @@ public class ElemIterateBreak extends ElemTemplateElement implements ExpressionO
}
}
}
+
+ /*
+ * Determine whether, an xsl:break instruction has xsl:iterate instruction
+ * as ancestor.
+ */
+ private boolean isXslBreakDescendantOfXslIterate(ElemIterateBreak xslNextInstr) {
+
+ boolean isXslBreakDescendantOfXslIterate = false;
+
+ ElemTemplateElement xslParentElement = xslNextInstr.m_parentNode;
+
+ while (!isXslBreakDescendantOfXslIterate && (xslParentElement != null)) {
+ if (xslParentElement instanceof ElemIterate) {
+ isXslBreakDescendantOfXslIterate = true;
+ break;
+ }
+ else {
+ xslParentElement = xslParentElement.m_parentNode;
+ }
+ }
+
+ return isXslBreakDescendantOfXslIterate;
+ }
}
diff --git a/src/org/apache/xalan/templates/ElemIterateNextIteration.java b/src/org/apache/xalan/templates/ElemIterateNextIteration.java
index f8c9ed8c..2e708605 100644
--- a/src/org/apache/xalan/templates/ElemIterateNextIteration.java
+++ b/src/org/apache/xalan/templates/ElemIterateNextIteration.java
@@ -17,12 +17,21 @@
*/
package org.apache.xalan.templates;
+import java.util.ArrayList;
+import java.util.List;
+
import javax.xml.transform.TransformerException;
import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.utils.QName;
import org.apache.xpath.Expression;
import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.VariableStack;
import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
/**
* XSLT 3.0 xsl:next-iteration element.
@@ -45,6 +54,11 @@ public class ElemIterateNextIteration extends ElemTemplateElement implements Exp
{
private static final long serialVersionUID = -582877657433106548L;
+
+ // revisit.
+ // can we have better way to maintain xsl:next-iteration->xsl:with-param* state, instead of having this with
+ // 'public static' visibility.
+ public static List<XslIterateParamWithparamData> fWithparamList = new ArrayList<XslIterateParamWithparamData>();
/**
* Construct an element representing xsl:next-iteration.
@@ -148,7 +162,28 @@ public class ElemIterateNextIteration extends ElemTemplateElement implements Exp
*/
public void execute(TransformerImpl transformer) throws TransformerException
{
- elemIterateNextIterationProcessing(transformer);
+
+ XPathContext xpathContext = transformer.getXPathContext();
+
+ boolean isXslNextIterationDescendantOfXslIterate = false;
+
+ if (isXslNextIterationDescendantOfXslIterate(this)) {
+ isXslNextIterationDescendantOfXslIterate = true;
+ }
+ else {
+ throw new TransformerException("XTSE3120 : an xsl:next-iteration instruction doesn't "
+ + "have xsl:iterate instruction as ancestor.",
+ xpathContext.getSAXLocator());
+ }
+
+ if (isXslNextIterationDescendantOfXslIterate && isXslInstructionInTailPositionOfSequenceConstructor(this)) {
+ elemIterateNextIterationProcessing(transformer);
+ }
+ else {
+ throw new TransformerException("XTSE3120 : an xsl:next-iteration instruction is not in a "
+ + "tail position within the sequence constructor of currently "
+ + "active xsl:iterate instruction.", xpathContext.getSAXLocator());
+ }
}
/**
@@ -160,7 +195,85 @@ public class ElemIterateNextIteration extends ElemTemplateElement implements Exp
*/
public void elemIterateNextIterationProcessing(TransformerImpl transformer) throws
TransformerException {
- // no op
+
+ XPathContext xctxt = transformer.getXPathContext();
+
+ int contextNode = xctxt.getContextNode();
+
+ // clear the, xsl:next-iteration->xsl:with-param* list storage before
+ // evaluating this xsl:next-iteration element.
+ fWithparamList.clear();
+
+ NodeList nextIterationChildNodes = this.getChildNodes();
+
+ for (int idx = 0; idx < nextIterationChildNodes.getLength(); idx++) {
+ Node nextIterChild = nextIterationChildNodes.item(idx);
+ if (nextIterChild instanceof ElemWithParam) {
+ ElemWithParam withParamElem = (ElemWithParam)nextIterChild;
+ QName withParamNameVal = withParamElem.getName();
+ XPath withParamSelectXPath = withParamElem.getSelect();
+ XslIterateParamWithparamData paramWithparamDataObj = new XslIterateParamWithparamData();
+ paramWithparamDataObj.setNameVal(withParamNameVal);
+ paramWithparamDataObj.setSelectVal(withParamSelectXPath);
+ if (fWithparamList.contains(paramWithparamDataObj)) {
+ throw new TransformerException("XTSE0670 : duplicate xsl:with-param parameter name '" + withParamNameVal +
+ "'", xctxt.getSAXLocator());
+ }
+ else {
+ fWithparamList.add(paramWithparamDataObj);
+ }
+ }
+ }
+
+ if ((ElemIterate.fParamList).size() != fWithparamList.size()) {
+ throw new TransformerException("XTSE0580 : within xsl:iterate, the number of xsl:param elements are not equal to "
+ + "number of xsl:next-iteration's xsl:with-param elements.", xctxt.getSAXLocator());
+ }
+ else {
+ for (int idx = 0; idx < (ElemIterate.fParamList).size(); idx ++) {
+ XslIterateParamWithparamData paramData = (ElemIterate.fParamList).get(idx);
+ XslIterateParamWithparamData withParamData = fWithparamList.get(idx);
+ if (!(paramData.getNameVal()).equals(withParamData.getNameVal())) {
+ throw new TransformerException("XTSE3130 : within xsl:iterate, xsl:param and xsl:with-param names at position " +
+ (idx + 1) + " are not same.", xctxt.getSAXLocator());
+ }
+ }
+ }
+
+ VariableStack varStack = xctxt.getVarStack();
+ for (int idx = 0; idx < fWithparamList.size(); idx++) {
+ XslIterateParamWithparamData withParamData = fWithparamList.get(idx);
+ XPath withParamSelectVal = withParamData.getSelectVal();
+ XObject evalResult = withParamSelectVal.execute(xctxt, contextNode, this);
+ // update value of current xsl:next-iteration's current xsl:param
+ // 'parameter'. when xsl:iterate's new iteration is entered, this
+ // parameter shall have this new value.
+ varStack.setLocalVariable(idx, evalResult);
+ }
+ }
+
+ /*
+ * Determine whether, an xsl:next-iteration instruction has xsl:iterate instruction
+ * as ancestor.
+ */
+ private boolean isXslNextIterationDescendantOfXslIterate(ElemIterateNextIteration
+ xslNextInstr) {
+
+ boolean isXslNextIterationDescendantOfXslIterate = false;
+
+ ElemTemplateElement xslParentElement = xslNextInstr.m_parentNode;
+
+ while (xslParentElement != null) {
+ if (xslParentElement instanceof ElemIterate) {
+ isXslNextIterationDescendantOfXslIterate = true;
+ break;
+ }
+ else {
+ xslParentElement = xslParentElement.m_parentNode;
+ }
+ }
+
+ return isXslNextIterationDescendantOfXslIterate;
}
}
diff --git a/src/org/apache/xalan/templates/ElemTemplateElement.java b/src/org/apache/xalan/templates/ElemTemplateElement.java
index 9282501b..fcb7f3b6 100644
--- a/src/org/apache/xalan/templates/ElemTemplateElement.java
+++ b/src/org/apache/xalan/templates/ElemTemplateElement.java
@@ -1680,5 +1680,61 @@ public class ElemTemplateElement extends UnImplNode
public void setGroupNodesDtmHandles(List<Integer> groupNodesDtmHandles) {
this.fGroupNodesDtmHandles = groupNodesDtmHandles;
}
+
+ /*
+ * Method to determine whether, an XSLT instruction is in a sequence constructor's tail position.
+ *
+ * The XSLT 3.0 spec, provides following definition to determine, whether an XSLT instruction
+ * is in the tail position within an XSLT sequence constructor,
+ *
+ * An instruction J is in a tail position within a sequence constructor SC if it satisfies
+ * one of the following conditions:
+ 1) J is the last instruction in SC, ignoring any xsl:fallback instructions.
+ 2) J is in a tail position within the sequence constructor that forms the body of an xsl:if instruction that
+ is itself in a tail position within SC.
+ 3) J is in a tail position within the sequence constructor that forms the body of an xsl:when or xsl:otherwise
+ branch of an xsl:choose instruction that is itself in a tail position within SC.
+ 4) J is in a tail position within the sequence constructor that forms the body of an xsl:try instruction that
+ is itself in a tail position within SC (that is, it is immediately followed by an xsl:catch element, ignoring
+ any xsl:fallback elements).
+ 5) J is in a tail position within the sequence constructor that forms the body of an xsl:catch element within
+ an xsl:try instruction that is itself in a tail position within SC.
+
+ Notes : Presently, we check only points 1), 2) and 3) as mentioned within previous definition above (excluding
+ checking the constraints, specified for xsl:fallback instruction).
+
+ @param xslInstr the XSLT instruction for which we need to find, whether its in a tail position of an XSLT
+ sequence constructor.
+
+ */
+ protected boolean isXslInstructionInTailPositionOfSequenceConstructor(ElemTemplateElement xslInstr) {
+
+ boolean isXslInstructionInTailPositionOfSequenceConstructor = true;
+
+ ElemTemplateElement elemTemplateElementNextSubling = xslInstr.m_nextSibling;
+
+ if (elemTemplateElementNextSubling == null) {
+ ElemTemplateElement xslInstrParentElement = xslInstr.m_parentNode;
+
+ if (xslInstrParentElement instanceof ElemIf) {
+ isXslInstructionInTailPositionOfSequenceConstructor = isXslInstructionInTailPositionOfSequenceConstructor(
+ xslInstrParentElement);
+ }
+ else if ((xslInstrParentElement instanceof ElemWhen) || (xslInstrParentElement
+ instanceof ElemOtherwise)) {
+ xslInstrParentElement = xslInstrParentElement.m_parentNode;
+ isXslInstructionInTailPositionOfSequenceConstructor = isXslInstructionInTailPositionOfSequenceConstructor(
+ xslInstrParentElement);
+ }
+ }
+ else {
+ if (!((elemTemplateElementNextSubling instanceof ElemIterateNextIteration) &&
+ (elemTemplateElementNextSubling.m_nextSibling == null))) {
+ isXslInstructionInTailPositionOfSequenceConstructor = false;
+ }
+ }
+
+ return isXslInstructionInTailPositionOfSequenceConstructor;
+ }
}
diff --git a/src/org/apache/xalan/templates/XslIterateParamWithparamData.java b/src/org/apache/xalan/templates/XslIterateParamWithparamData.java
new file mode 100644
index 00000000..57460d3f
--- /dev/null
+++ b/src/org/apache/xalan/templates/XslIterateParamWithparamData.java
@@ -0,0 +1,69 @@
+/*
+ * 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 org.apache.xml.utils.QName;
+import org.apache.xpath.XPath;
+
+/**
+ * A class to support evaluation of xsl:iterate instruction.
+
+ @author Mukul Gandhi <mu...@apache.org>
+ *
+ * @xsl.usage advanced
+ */
+/*
+ * An object of this class, stores information about one xsl:param
+ * element or one xsl:next-iteration->xsl:with-param element, for a
+ * particular xsl:iterate instruction.
+ */
+public class XslIterateParamWithparamData {
+
+ public QName nameVal;
+
+ public XPath selectVal;
+
+ public QName getNameVal() {
+ return nameVal;
+ }
+
+ public void setNameVal(QName nameVal) {
+ this.nameVal = nameVal;
+ }
+
+ public XPath getSelectVal() {
+ return selectVal;
+ }
+
+ public void setSelectVal(XPath selectVal) {
+ this.selectVal = selectVal;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ XslIterateParamWithparamData paramWithparamData =
+ (XslIterateParamWithparamData)obj;
+ return nameVal.equals(paramWithparamData.getNameVal());
+ }
+
+}
diff --git a/tests/org/apache/xalan/xslt3/XslIterateTests.java b/tests/org/apache/xalan/xslt3/XslIterateTests.java
index 48230885..131a2742 100644
--- a/tests/org/apache/xalan/xslt3/XslIterateTests.java
+++ b/tests/org/apache/xalan/xslt3/XslIterateTests.java
@@ -170,5 +170,25 @@ public class XslIterateTests extends XslTransformTestsUtil {
runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
}
+
+ @Test
+ public void xslIterateTest13() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1_c.xml";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test11.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test11.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
+
+ @Test
+ public void xslIterateTest14() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1_d.xml";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test12.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test12.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
}
diff --git a/tests/xsl_iterate/gold/test11.out b/tests/xsl_iterate/gold/test11.out
new file mode 100644
index 00000000..16aa07e6
--- /dev/null
+++ b/tests/xsl_iterate/gold/test11.out
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?><account>
+ <balance value="12.00" date="2008-09-01"/>
+ <balance value="20.00" date="2008-09-01"/>
+ <balance value="18.00" date="2008-09-02"/>
+ <balance value="23.00" date="2008-09-02"/>
+</account>
diff --git a/tests/xsl_iterate/gold/test12.out b/tests/xsl_iterate/gold/test12.out
new file mode 100644
index 00000000..c6406da2
--- /dev/null
+++ b/tests/xsl_iterate/gold/test12.out
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+ <total value="12.00" sequence="1"/>
+ <total value="10.00" sequence="1"/>
+ <total value="13.00" sequence="1"/>
+</result>
diff --git a/tests/xsl_iterate/iterate-003.xsl b/tests/xsl_iterate/iterate-003.xsl
index 83c9e36a..656c8bd3 100644
--- a/tests/xsl_iterate/iterate-003.xsl
+++ b/tests/xsl_iterate/iterate-003.xsl
@@ -29,7 +29,7 @@
</xsl:otherwise>
</xsl:choose>
<xsl:next-iteration>
- <xsl:with-param name="basketCost" select="$basketCost + PRICE"/>
+ <xsl:with-param name="basketCost" select="$basketCost + PRICE"/>
</xsl:next-iteration>
</xsl:iterate>
</out>
diff --git a/tests/xsl_iterate/iterate-003.xsl b/tests/xsl_iterate/test11.xsl
similarity index 50%
copy from tests/xsl_iterate/iterate-003.xsl
copy to tests/xsl_iterate/test11.xsl
index 83c9e36a..6e182794 100644
--- a/tests/xsl_iterate/iterate-003.xsl
+++ b/tests/xsl_iterate/test11.xsl
@@ -1,38 +1,29 @@
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0">
- <!-- use with iterate001.xml -->
+ <!-- Author: mukulg@apache.org -->
- <!-- An XSLT stylesheet test case, that uses xsl:iterate
- along with xsl:param and xsl:next-iteration. This
- XSLT stylesheet uses xsl:break instruction to exit
- from xsl:iterate loop early.
+ <!-- use with test1_c.xml -->
+
+ <!-- This XSLT stylesheet, uses xsl:iterate to compute cumulative
+ totals.
- This XSLT stylesheet has been borrowed from W3C XSLT
- 3.0 test suite and makes few changes to XSLT stylesheet
- syntax. -->
-
- <xsl:output method="xml" indent="yes"/>
+ This XSLT stylesheet is borrowed from XSLT 3.0 spec, with
+ slight modifications. -->
+
+ <xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
- <out>
- <xsl:iterate select="//ITEM">
- <xsl:param name="basketCost" select="0"/>
- <xsl:choose>
- <xsl:when test="$basketCost > 12.00">
- <xsl:break/>
- </xsl:when>
- <xsl:otherwise>
- <item cost="{format-number($basketCost, '00.00')}">
- <xsl:copy-of select="TITLE"/>
- </item>
- </xsl:otherwise>
- </xsl:choose>
- <xsl:next-iteration>
- <xsl:with-param name="basketCost" select="$basketCost + PRICE"/>
- </xsl:next-iteration>
- </xsl:iterate>
- </out>
+ <account>
+ <xsl:iterate select="transactions/transaction">
+ <xsl:param name="balance" select="0.00"/>
+ <xsl:variable name="newBalance" select="$balance + @value"/>
+ <balance date="{@date}" value="{format-number($newBalance, '0.00')}"/>
+ <xsl:next-iteration>
+ <xsl:with-param name="balance" select="$newBalance"/>
+ </xsl:next-iteration>
+ </xsl:iterate>
+ </account>
</xsl:template>
<!--
diff --git a/tests/xsl_iterate/test12.xsl b/tests/xsl_iterate/test12.xsl
new file mode 100644
index 00000000..331602b4
--- /dev/null
+++ b/tests/xsl_iterate/test12.xsl
@@ -0,0 +1,62 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- use with test1_d.xml -->
+
+ <!-- An XSLT stylesheet test case, that uses xsl:iterate and xsl:next-iteration.
+ The xsl:next-iteration instruction is specified in a tail position (as per
+ definition of, when an XSLT instruction is in a sequence constructor's tail
+ position) of sequence constructor that is a child of xsl:iterate.
+
+ Using xsl:iterate, this stylesheet reads a sequence of 'a' elements, and breaks
+ from xsl:iterate when value of 'sequence' attribute changes for the first time.
+ This stylesheet also, maintains and outputs cumulative total of attribute
+ 'value'. -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template match="/elem">
+ <result>
+ <xsl:iterate select="a">
+ <xsl:param name="total" select="0.00"/>
+ <xsl:param name="prevSequence" select="0"/>
+ <xsl:variable name="newTotal" select="$total + @value"/>
+ <xsl:variable name="thisSequence" select="@sequence"/>
+ <xsl:choose>
+ <xsl:when test="(position() = 1) or ($thisSequence = $prevSequence)">
+ <total sequence="{$thisSequence}"
+ value="{format-number($newTotal, '0.00')}"/>
+ <xsl:next-iteration>
+ <xsl:with-param name="total" select="$newTotal"/>
+ <xsl:with-param name="prevSequence" select="$thisSequence"/>
+ </xsl:next-iteration>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:break/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:iterate>
+ </result>
+ </xsl:template>
+
+ <!--
+ * 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.
+ -->
+
+</xsl:stylesheet>
diff --git a/tests/xsl_iterate/test1_c.xml b/tests/xsl_iterate/test1_c.xml
new file mode 100644
index 00000000..4a69a814
--- /dev/null
+++ b/tests/xsl_iterate/test1_c.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<transactions>
+ <transaction date="2008-09-01" value="12.00"/>
+ <transaction date="2008-09-01" value="8.00"/>
+ <transaction date="2008-09-02" value="-2.00"/>
+ <transaction date="2008-09-02" value="5.00"/>
+</transactions>
\ No newline at end of file
diff --git a/tests/xsl_iterate/test1_d.xml b/tests/xsl_iterate/test1_d.xml
new file mode 100644
index 00000000..80a2ae4d
--- /dev/null
+++ b/tests/xsl_iterate/test1_d.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<elem>
+ <a sequence="1" value="12.00"/>
+ <a sequence="1" value="-2.00"/>
+ <a sequence="1" value="3.00"/>
+ <a sequence="2" value="4.00"/>
+ <a sequence="2" value="5.00"/>
+</elem>
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xalan.apache.org
For additional commands, e-mail: commits-help@xalan.apache.org