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 2024/08/09 16:49:59 UTC
(xalan-java) branch xalan-j_xslt3.0 updated: committing implementation of xpath path expression having function call suffix, with few conformance tests
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 b5342ad5f committing implementation of xpath path expression having function call suffix, with few conformance tests
b5342ad5f is described below
commit b5342ad5fb1e4aec2946db23ce68ea42c7dd1021
Author: Mukul Gandhi <ga...@gmail.com>
AuthorDate: Fri Aug 9 22:19:29 2024 +0530
committing implementation of xpath path expression having function call suffix, with few conformance tests
---
src/org/apache/xalan/templates/ElemForEach.java | 27 ++++
src/org/apache/xalan/templates/ElemIterate.java | 42 +++++-
src/org/apache/xalan/templates/ElemValueOf.java | 24 +++-
src/org/apache/xalan/templates/ElemVariable.java | 28 +++-
src/org/apache/xpath/XPath.java | 43 +++++-
src/org/apache/xpath/axes/WalkingIterator.java | 17 +++
src/org/apache/xpath/compiler/XPathParser.java | 153 ++++++++++++++++++---
.../xpath/composite/XPathExprFunctionSuffix.java | 55 ++++++++
.../xpath3/XPathExprFunctionCallSuffixTests.java | 91 ++++++++++++
tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java | 4 +-
tests/xpath_function_call_suffix/gold/test1.out | 4 +
tests/xpath_function_call_suffix/gold/test2.out | 1 +
tests/xpath_function_call_suffix/gold/test3.out | 7 +
tests/xpath_function_call_suffix/gold/test4.out | 7 +
tests/xpath_function_call_suffix/test1.xml | 8 ++
tests/xpath_function_call_suffix/test1.xsl | 44 ++++++
tests/xpath_function_call_suffix/test2.xsl | 40 ++++++
tests/xpath_function_call_suffix/test3.xsl | 43 ++++++
tests/xpath_function_call_suffix/test4.xsl | 43 ++++++
19 files changed, 647 insertions(+), 34 deletions(-)
diff --git a/src/org/apache/xalan/templates/ElemForEach.java b/src/org/apache/xalan/templates/ElemForEach.java
index b05a7cc69..d7327d17e 100644
--- a/src/org/apache/xalan/templates/ElemForEach.java
+++ b/src/org/apache/xalan/templates/ElemForEach.java
@@ -41,6 +41,7 @@ import org.apache.xpath.ExpressionOwner;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathContext;
import org.apache.xpath.axes.LocPathIterator;
+import org.apache.xpath.axes.WalkingIterator;
import org.apache.xpath.composite.XPathForExpr;
import org.apache.xpath.composite.XPathSequenceConstructor;
import org.apache.xpath.functions.Function;
@@ -448,6 +449,13 @@ public class ElemForEach extends ElemTemplateElement implements ExpressionOwner
else if (m_selectExpression instanceof LocPathIterator) {
LocPathIterator locPathIterator = (LocPathIterator)m_selectExpression;
+ Function func = null;
+
+ if (locPathIterator instanceof WalkingIterator) {
+ WalkingIterator walkingIter = (WalkingIterator)locPathIterator;
+ func = walkingIter.getFuncExpr();
+ }
+
boolean isProcessAsNodeset = true;
DTMIterator dtmIter = null;
try {
@@ -457,6 +465,25 @@ public class ElemForEach extends ElemTemplateElement implements ExpressionOwner
isProcessAsNodeset = false;
}
+ if (dtmIter != null) {
+ ResultSequence rSeq = null;
+ if (func != null) {
+ rSeq = new ResultSequence();
+ int nextNode;
+ while ((nextNode = dtmIter.nextNode()) != DTM.NULL)
+ {
+ XNodeSet singletonXPathNode = new XNodeSet(nextNode, xctxt);
+ xctxt.setXPath3ContextItem(singletonXPathNode);
+ XObject funcEvalResult = func.execute(xctxt);
+ rSeq.add(funcEvalResult);
+ }
+
+ processSequenceOrArray(transformer, xctxt, rSeq);
+
+ return;
+ }
+ }
+
if (!isProcessAsNodeset) {
String xpathPatternStr = m_xpath.getPatternString();
diff --git a/src/org/apache/xalan/templates/ElemIterate.java b/src/org/apache/xalan/templates/ElemIterate.java
index 211a5fe07..87ea932d7 100644
--- a/src/org/apache/xalan/templates/ElemIterate.java
+++ b/src/org/apache/xalan/templates/ElemIterate.java
@@ -32,12 +32,14 @@ import org.apache.xpath.Expression;
import org.apache.xpath.ExpressionOwner;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathContext;
+import org.apache.xpath.axes.WalkingIterator;
+import org.apache.xpath.functions.Function;
import org.apache.xpath.objects.ResultSequence;
import org.apache.xpath.objects.XNodeSet;
import org.apache.xpath.objects.XObject;
import org.apache.xpath.objects.XPathArray;
-/*
+/**
* Implementation of the XSLT 3.0 xsl:iterate instruction.
*
* Ref : https://www.w3.org/TR/xslt-30/#element-iterate
@@ -180,7 +182,43 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
verifyXslElemIterateChildElementsSequence(xctxt);
- XObject evalResult = m_selectExpression.execute(xctxt);
+ XObject evalResult = null;
+
+ Function func = null;
+
+ if (m_selectExpression instanceof WalkingIterator) {
+ WalkingIterator walkingIter = (WalkingIterator)m_selectExpression;
+ func = walkingIter.getFuncExpr();
+
+ DTMIterator dtmIter = null;
+ try {
+ dtmIter = walkingIter.asIterator(xctxt, sourceNode);
+ }
+ catch (ClassCastException ex) {
+ // no op
+ }
+
+ if (dtmIter != null) {
+ ResultSequence rSeq = null;
+ if (func != null) {
+ rSeq = new ResultSequence();
+ int nextNode;
+ while ((nextNode = dtmIter.nextNode()) != DTM.NULL)
+ {
+ XNodeSet singletonXPathNode = new XNodeSet(nextNode, xctxt);
+ xctxt.setXPath3ContextItem(singletonXPathNode);
+ XObject funcEvalResult = func.execute(xctxt);
+ rSeq.add(funcEvalResult);
+ }
+
+ evalResult = rSeq;
+ }
+ }
+ }
+
+ if (evalResult == null) {
+ evalResult = m_selectExpression.execute(xctxt);
+ }
List<XObject> itemsToBeProcessed = null;
diff --git a/src/org/apache/xalan/templates/ElemValueOf.java b/src/org/apache/xalan/templates/ElemValueOf.java
index 20f45cee5..d45fdccb6 100644
--- a/src/org/apache/xalan/templates/ElemValueOf.java
+++ b/src/org/apache/xalan/templates/ElemValueOf.java
@@ -33,12 +33,13 @@ import org.apache.xpath.Expression;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathContext;
import org.apache.xpath.axes.LocPathIterator;
+import org.apache.xpath.axes.WalkingIterator;
import org.apache.xpath.composite.XPathIfExpr;
import org.apache.xpath.composite.XPathLetExpr;
-import org.apache.xpath.functions.XSLConstructorStylesheetOrExtensionFunction;
import org.apache.xpath.functions.Function;
-import org.apache.xpath.functions.XSLFunctionService;
import org.apache.xpath.functions.XPathDynamicFunctionCall;
+import org.apache.xpath.functions.XSLConstructorStylesheetOrExtensionFunction;
+import org.apache.xpath.functions.XSLFunctionService;
import org.apache.xpath.objects.ResultSequence;
import org.apache.xpath.objects.XNodeSet;
import org.apache.xpath.objects.XNumber;
@@ -460,6 +461,13 @@ public class ElemValueOf extends ElemTemplateElement {
else if (expr instanceof LocPathIterator) {
LocPathIterator locPathIterator = (LocPathIterator)expr;
+ Function func = null;
+
+ if (locPathIterator instanceof WalkingIterator) {
+ WalkingIterator walkingIter = (WalkingIterator)locPathIterator;
+ func = walkingIter.getFuncExpr();
+ }
+
DTMIterator dtmIter = null;
try {
dtmIter = locPathIterator.asIterator(xctxt, current);
@@ -474,8 +482,16 @@ public class ElemValueOf extends ElemTemplateElement {
while ((nextNode = dtmIter.nextNode()) != DTM.NULL)
{
XNodeSet singletonXPathNode = new XNodeSet(nextNode, xctxt);
- String nodeStrVal = singletonXPathNode.str();
- strBuff.append(nodeStrVal + " ");
+ String resultStr = "";
+ if (func != null) {
+ xctxt.setXPath3ContextItem(singletonXPathNode);
+ XObject funcEvalResult = func.execute(xctxt);
+ resultStr = XslTransformEvaluationHelper.getStrVal(funcEvalResult);
+ }
+ else {
+ resultStr = singletonXPathNode.str();
+ }
+ strBuff.append(resultStr + " ");
}
String nodeSetStrValue = strBuff.toString();
diff --git a/src/org/apache/xalan/templates/ElemVariable.java b/src/org/apache/xalan/templates/ElemVariable.java
index 6d057102c..11393093e 100644
--- a/src/org/apache/xalan/templates/ElemVariable.java
+++ b/src/org/apache/xalan/templates/ElemVariable.java
@@ -28,6 +28,7 @@ import org.apache.xalan.res.XSLTErrorResources;
import org.apache.xalan.transformer.TransformerImpl;
import org.apache.xalan.xslt.util.XslTransformEvaluationHelper;
import org.apache.xalan.xslt.util.XslTransformSharedDatastore;
+import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xml.utils.PrefixResolver;
import org.apache.xml.utils.QName;
@@ -36,6 +37,7 @@ 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.axes.WalkingIterator;
import org.apache.xpath.composite.SequenceTypeData;
import org.apache.xpath.composite.SequenceTypeSupport;
import org.apache.xpath.functions.XSLConstructorStylesheetOrExtensionFunction;
@@ -484,6 +486,13 @@ public class ElemVariable extends ElemTemplateElement
LocPathIterator locPathIterator = (LocPathIterator)selectExpression;
+ Function func = null;
+
+ if (locPathIterator instanceof WalkingIterator) {
+ WalkingIterator walkingIter = (WalkingIterator)locPathIterator;
+ func = walkingIter.getFuncExpr();
+ }
+
DTMIterator dtmIter = null;
try {
dtmIter = locPathIterator.asIterator(xctxt, contextNode);
@@ -492,8 +501,23 @@ public class ElemVariable extends ElemTemplateElement
// no op
}
- if (dtmIter != null) {
- var = new XNodeSet(dtmIter);
+ if (dtmIter != null) {
+ ResultSequence rSeq = null;
+ if (func != null) {
+ rSeq = new ResultSequence();
+ int nextNode;
+ while ((nextNode = dtmIter.nextNode()) != DTM.NULL)
+ {
+ XNodeSet singletonXPathNode = new XNodeSet(nextNode, xctxt);
+ xctxt.setXPath3ContextItem(singletonXPathNode);
+ XObject funcEvalResult = func.execute(xctxt);
+ rSeq.add(funcEvalResult);
+ }
+ var = rSeq;
+ }
+ else {
+ var = new XNodeSet(dtmIter);
+ }
if (m_asAttr != null) {
var = SequenceTypeSupport.castXdmValueToAnotherType(var, m_asAttr, null, xctxt);
diff --git a/src/org/apache/xpath/XPath.java b/src/org/apache/xpath/XPath.java
index e3ff7aa70..8c132ca78 100644
--- a/src/org/apache/xpath/XPath.java
+++ b/src/org/apache/xpath/XPath.java
@@ -34,9 +34,11 @@ import org.apache.xml.dtm.DTMIterator;
import org.apache.xml.utils.PrefixResolver;
import org.apache.xml.utils.SAXSourceLocator;
import org.apache.xpath.axes.LocPathIterator;
+import org.apache.xpath.axes.WalkingIterator;
import org.apache.xpath.compiler.Compiler;
import org.apache.xpath.compiler.FunctionTable;
import org.apache.xpath.compiler.XPathParser;
+import org.apache.xpath.composite.XPathExprFunctionSuffix;
import org.apache.xpath.functions.Function;
import org.apache.xpath.objects.ResultSequence;
import org.apache.xpath.objects.XNumber;
@@ -249,22 +251,51 @@ public class XPath implements Serializable, ExpressionOwner
m_patternString = exprString;
XPathParser parser = new XPathParser(errorListener, locator);
- Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
+ Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
+
+ Expression expr = null;
if (SELECT == type) {
parser.initXPath(compiler, exprString, prefixResolver, false);
m_arrowop_remaining_xpath_expr_str = parser.getArrowOpRemainingXPathExprStr();
+
+ XPathExprFunctionSuffix pathExprFunctionSuffix = parser.getPathExprFunctionSuffix();
+ if (pathExprFunctionSuffix != null) {
+ parser = new XPathParser(errorListener, locator);
+ compiler = new Compiler(errorListener, locator, m_funcTable);
+ String xpathOneExprStr = pathExprFunctionSuffix.getXPathOneStr();
+ parser.initXPath(compiler, xpathOneExprStr, prefixResolver, false);
+ Expression expr1 = compiler.compile(0);
+
+ parser = new XPathParser(errorListener, locator);
+ compiler = new Compiler(errorListener, locator, m_funcTable);
+ String xpathTwoExprStr = pathExprFunctionSuffix.getXPathTwoStr();
+ parser.initXPath(compiler, xpathTwoExprStr, prefixResolver, false);
+ Expression expr2 = compiler.compile(0);
+
+ parser.setPathExprFunctionSuffix(null);
+
+ expr = expr1;
+
+ if ((expr instanceof WalkingIterator) && (expr2 instanceof Function)) {
+ WalkingIterator walkingIter = (WalkingIterator)expr;
+ walkingIter.setFuncExpr((Function)expr2);
+ }
+ }
}
else if (MATCH == type)
- parser.initMatchPattern(compiler, exprString, prefixResolver);
+ parser.initMatchPattern(compiler, exprString, prefixResolver);
else
throw new RuntimeException(XSLMessages.createXPATHMessage(
XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE,
- new Object[]{Integer.toString(type)}));
-
- Expression expr = compiler.compile(0);
+ new Object[]{Integer.toString(type)}));
+
+ if (expr == null) {
+ expr = compiler.compile(0);
+ }
+
if (expr instanceof ArrowOp) {
- ((ArrowOp)expr).setArrowOpRemainingXPathExprStr(m_arrowop_remaining_xpath_expr_str);
+ ((ArrowOp)expr).setArrowOpRemainingXPathExprStr(m_arrowop_remaining_xpath_expr_str);
}
this.setExpression(expr);
diff --git a/src/org/apache/xpath/axes/WalkingIterator.java b/src/org/apache/xpath/axes/WalkingIterator.java
index 10765f349..caebd75cc 100644
--- a/src/org/apache/xpath/axes/WalkingIterator.java
+++ b/src/org/apache/xpath/axes/WalkingIterator.java
@@ -28,6 +28,7 @@ import org.apache.xpath.VariableStack;
import org.apache.xpath.XPathVisitor;
import org.apache.xpath.compiler.Compiler;
import org.apache.xpath.compiler.OpMap;
+import org.apache.xpath.functions.Function;
/**
* Location path iterator that uses Walkers.
@@ -35,6 +36,14 @@ import org.apache.xpath.compiler.OpMap;
public class WalkingIterator extends LocPathIterator implements ExpressionOwner
{
static final long serialVersionUID = 9110225941815665906L;
+
+ /**
+ * If there's a function call suffix at an end of XPath expression
+ * similar to /a/b/funcCall(..), we keep a compiled Function
+ * object for funcCall(..) within this class field.
+ */
+ private Function m_func_expr = null;
+
/**
* Create a WalkingIterator iterator, including creation
* of step walkers from the opcode list, and call back
@@ -357,4 +366,12 @@ public class WalkingIterator extends LocPathIterator implements ExpressionOwner
return true;
}
+ public void setFuncExpr(Function expr) {
+ m_func_expr = expr;
+ }
+
+ public Function getFuncExpr() {
+ return m_func_expr;
+ }
+
}
diff --git a/src/org/apache/xpath/compiler/XPathParser.java b/src/org/apache/xpath/compiler/XPathParser.java
index 195a1f258..b7745844b 100644
--- a/src/org/apache/xpath/compiler/XPathParser.java
+++ b/src/org/apache/xpath/compiler/XPathParser.java
@@ -50,6 +50,7 @@ import org.apache.xml.utils.PrefixResolver;
import org.apache.xpath.XPathProcessorException;
import org.apache.xpath.composite.ForQuantifiedExprVarBinding;
import org.apache.xpath.composite.LetExprVarBinding;
+import org.apache.xpath.composite.XPathExprFunctionSuffix;
import org.apache.xpath.composite.SequenceTypeArrayTest;
import org.apache.xpath.composite.SequenceTypeData;
import org.apache.xpath.composite.SequenceTypeFunctionTest;
@@ -252,7 +253,9 @@ public class XPathParser
private String m_arrowOpRemainingXPathExprStr = null;
- private boolean isFunctionArgumentParse;
+ private XPathExprFunctionSuffix m_pathExprFunctionSuffix = null;
+
+ private boolean m_isFunctionArgumentParse;
/**
* The parser constructor.
@@ -606,7 +609,7 @@ public class XPathParser
if (m_queueMark < m_ops.getTokenQueueSize())
{
- m_token = (String) m_ops.m_tokenQueue.elementAt(m_queueMark++);
+ m_token = (String) m_ops.m_tokenQueue.elementAt(m_queueMark++);
m_tokenChar = m_token.charAt(0);
}
else
@@ -956,7 +959,7 @@ public class XPathParser
OccurrenceIndicator.ONE_OR_MANY);
nextToken();
}
- else if ((m_token != null) && !isXPathInlineFunctionParse && !isFunctionArgumentParse) {
+ else if ((m_token != null) && !isXPathInlineFunctionParse && !m_isFunctionArgumentParse) {
throw new javax.xml.transform.TransformerException("XPST0051 A sequence type occurence indicator '" + m_token + "', is not recognized.");
}
}
@@ -3425,7 +3428,7 @@ public class XPathParser
protected void Argument() throws javax.xml.transform.TransformerException
{
- isFunctionArgumentParse = true;
+ m_isFunctionArgumentParse = true;
int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
@@ -3622,7 +3625,7 @@ public class XPathParser
m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- isFunctionArgumentParse = false;
+ m_isFunctionArgumentParse = false;
}
@@ -4026,7 +4029,9 @@ public class XPathParser
{
int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
- int axesType;
+ int axesType = 0;
+
+ boolean funcMatchFound = false;
// The next blocks guarantee that a FROM_XXX will be added.
if (lookahead("::", 1))
@@ -4041,23 +4046,98 @@ public class XPathParser
axesType = OpCodes.FROM_ATTRIBUTES;
appendOp(2, axesType);
- nextToken();
+ nextToken();
}
- else
+ else if (!isXPathPatternExcludeTrailingNodeFunctions() && (lookahead('(', 1) || (lookahead(':', 1) && lookahead('(', 3))))
{
- axesType = OpCodes.FROM_CHILDREN;
-
- appendOp(2, axesType);
+ TokenQueueScanPosition prevTokQueueScanPosition = new TokenQueueScanPosition(
+ m_queueMark, m_tokenChar, m_token);
+ funcMatchFound = FunctionCall();
+ if (funcMatchFound && (m_token != null)) {
+ restoreTokenQueueScanPosition(prevTokQueueScanPosition);
+
+ axesType = OpCodes.FROM_CHILDREN;
+
+ appendOp(2, axesType);
+ }
+ else if (funcMatchFound) {
+ m_queueMark = 0;
+ m_tokenChar = 0;
+ m_token = null;
+
+ nextToken();
+
+ int scanOneEndPos = 0;
+ int scanTwoStartPos = 0;
+ if (prevTokQueueScanPosition.getQueueMark() > 2) {
+ scanOneEndPos = (prevTokQueueScanPosition.getQueueMark() - 2);
+ scanTwoStartPos = prevTokQueueScanPosition.getQueueMark();
+ }
+ else if (prevTokQueueScanPosition.getQueueMark() > 1) {
+ scanOneEndPos = (prevTokQueueScanPosition.getQueueMark() - 1);
+ scanTwoStartPos = prevTokQueueScanPosition.getQueueMark();
+ }
+
+ String xpathOneStr = "";
+ String xpathTwoStr = "";
+ xpathOneStr = m_token;
+ while (m_queueMark != scanOneEndPos) {
+ nextToken();
+ xpathOneStr += m_token;
+ }
+
+ for (int idx = scanOneEndPos; idx < scanTwoStartPos; idx++) {
+ nextToken();
+ }
+
+ while (m_token != null) {
+ xpathTwoStr += m_token;
+ // The following is copy of the code of nextToken()
+ // function, with minor modification for this need.
+ if (m_queueMark < m_ops.getTokenQueueSize())
+ {
+ Object tokenObj = m_ops.m_tokenQueue.elementAt(m_queueMark++);
+ if (tokenObj instanceof XString) {
+ m_token = "'" + ((XString)tokenObj).str() + "'";
+ }
+ else {
+ m_token = (String)tokenObj;
+ }
+ m_tokenChar = m_token.charAt(0);
+ }
+ else
+ {
+ m_token = null;
+ m_tokenChar = 0;
+ }
+ }
+
+ m_pathExprFunctionSuffix = new XPathExprFunctionSuffix();
+ m_pathExprFunctionSuffix.setXPathOneStr(xpathOneStr);
+ m_pathExprFunctionSuffix.setXPathTwoStr(xpathTwoStr);
+
+ m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+ m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+
+ }
}
+ else
+ {
+ axesType = OpCodes.FROM_CHILDREN;
- // Make room for telling how long the step is without the predicate
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- NodeTest(axesType);
+ appendOp(2, axesType);
+ }
- // Tell how long the step is without the predicate
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+ if (!funcMatchFound) {
+ // Make room for telling how long the step is without the predicate
+ m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+ NodeTest(axesType);
+
+ // Tell how long the step is without the predicate
+ m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
+ m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+ }
}
/**
@@ -5158,7 +5238,7 @@ public class XPathParser
XSTypeDefinition xsTypeDefinition = xsModel.getTypeDefinition(typeName, typeNamespace);
if (xsTypeDefinition != null) {
- if (!isFunctionArgumentParse && tokenIs(')')) {
+ if (!m_isFunctionArgumentParse && tokenIs(')')) {
consumeExpected(')');
}
xpathSequenceTypeExpr.setXsSequenceTypeDefinition(xsTypeDefinition);
@@ -5641,5 +5721,40 @@ public class XPathParser
m_tokenChar = tokQueueScanPosition.getTokenChar();
m_token = tokQueueScanPosition.getToken();
}
+
+ public XPathExprFunctionSuffix getPathExprFunctionSuffix() {
+ return m_pathExprFunctionSuffix;
+ }
+
+ public void setPathExprFunctionSuffix(XPathExprFunctionSuffix pathExprFunctionSuffix) {
+ this.m_pathExprFunctionSuffix = pathExprFunctionSuffix;
+ }
+
+ /**
+ * This method checks, whether there's a function call suffix at an
+ * end of XPath expression similar to /a/b/funcCall(..).
+ */
+ private boolean isXPathPatternExcludeTrailingNodeFunctions() {
+ boolean isExclude = false;
+
+ String currXPathPattern = m_ops.m_currentPattern;
+ if (currXPathPattern.endsWith("node()") || currXPathPattern.endsWith("text()") || currXPathPattern.endsWith("comment()")) {
+ isExclude = true;
+ }
+ else {
+ String[] spltResult = currXPathPattern.split("node\\(\\)[\\s]*[\\|][\\s]*");
+ if (spltResult.length > 1) {
+ isExclude = true;
+ }
+ if (!isExclude) {
+ spltResult = currXPathPattern.split("comment\\(\\)\\[.*\\],");
+ if (spltResult.length > 1) {
+ isExclude = true;
+ }
+ }
+ }
+
+ return isExclude;
+ }
}
diff --git a/src/org/apache/xpath/composite/XPathExprFunctionSuffix.java b/src/org/apache/xpath/composite/XPathExprFunctionSuffix.java
new file mode 100644
index 000000000..b93c2fc36
--- /dev/null
+++ b/src/org/apache/xpath/composite/XPathExprFunctionSuffix.java
@@ -0,0 +1,55 @@
+/*
+ * 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.xpath.composite;
+
+/**
+ * An object of this class, stores few run-time information
+ * for evaluation of XPath path expressions having function
+ * call suffix.
+ *
+ * For e.g, an XPath expression /a/b/funcCall(.) is split into
+ * two XPath strings /a/b and funcCall(.). The function call
+ * funcCall(.) is invoked on each node of the nodeset resulting
+ * from evaluation of XPath expression /a/b.
+ *
+ * @author Mukul Gandhi <mu...@apache.org>
+ *
+ * @xsl.usage advanced
+ */
+public class XPathExprFunctionSuffix {
+
+ private String m_xpathOneStr = null;
+
+ private String m_xpathTwoStr = null;
+
+ public String getXPathOneStr() {
+ return m_xpathOneStr;
+ }
+
+ public void setXPathOneStr(String xpathOneStr) {
+ this.m_xpathOneStr = xpathOneStr;
+ }
+
+ public String getXPathTwoStr() {
+ return m_xpathTwoStr;
+ }
+
+ public void setXPathTwoStr(String xpathTwoStr) {
+ this.m_xpathTwoStr = xpathTwoStr;
+ }
+
+}
diff --git a/tests/org/apache/xalan/xpath3/XPathExprFunctionCallSuffixTests.java b/tests/org/apache/xalan/xpath3/XPathExprFunctionCallSuffixTests.java
new file mode 100644
index 000000000..fb226a281
--- /dev/null
+++ b/tests/org/apache/xalan/xpath3/XPathExprFunctionCallSuffixTests.java
@@ -0,0 +1,91 @@
+/*
+ * 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.xpath3;
+
+import org.apache.xalan.util.XSLConstants;
+import org.apache.xalan.util.XSLTransformTestsUtil;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * XPath 3.1 test cases to test, XPath path expressions
+ * having function call suffix.
+ *
+ * @author Mukul Gandhi <mu...@apache.org>
+ *
+ * @xsl.usage advanced
+ */
+public class XPathExprFunctionCallSuffixTests extends XSLTransformTestsUtil {
+
+ private static final String XSL_TRANSFORM_INPUT_DIRPATH = XSLConstants.XSL_TRANSFORM_INPUT_DIRPATH_PREFIX + "xpath_function_call_suffix/";
+
+ private static final String XSL_TRANSFORM_GOLD_DIRPATH = XSLConstants.XSL_TRANSFORM_GOLD_DIRPATH_PREFIX + "xpath_function_call_suffix/gold/";
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ // no op
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ xmlDocumentBuilderFactory = null;
+ xmlDocumentBuilder = null;
+ xslTransformerFactory = null;
+ }
+
+ @Test
+ public void xpathExprFunctionCallSuffixTest1() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xml";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test1.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
+
+ @Test
+ public void xpathExprFunctionCallSuffixTest2() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xml";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test2.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test2.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
+
+ @Test
+ public void xpathExprFunctionCallSuffixTest3() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xml";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test3.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test3.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
+
+ @Test
+ public void xpathExprFunctionCallSuffixTest4() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xml";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test4.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test4.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
+
+}
diff --git a/tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java b/tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java
index 8c44b9ef5..9f8351b84 100644
--- a/tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java
+++ b/tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java
@@ -46,6 +46,7 @@ import org.apache.xalan.xpath3.ValueComparisonTests;
import org.apache.xalan.xpath3.XPathArrayTests;
import org.apache.xalan.xpath3.XPathArrowOpTests;
import org.apache.xalan.xpath3.XPathCastTests;
+import org.apache.xalan.xpath3.XPathExprFunctionCallSuffixTests;
import org.apache.xalan.xpath3.XPathGeneralComparisonOpTests;
import org.apache.xalan.xpath3.XPathMapTests;
import org.apache.xalan.xpath3.XPathNamedFunctionReferenceTests;
@@ -84,7 +85,8 @@ import org.junit.runners.Suite.SuiteClasses;
JiraIssuesTests.class, XPathNamedFunctionReferenceTests.class, FnApplyTests.class,
XPathGeneralComparisonOpTests.class, FnMapFindTests.class, XPathNumericArithmeticTests.class,
XslTunnelParameterTests.class, FuncBooleanValuesTests.class, XslElementValidationTests.class,
- XslLiteralResultElementValidationTests.class, XslAttributeValidationTests.class })
+ XslLiteralResultElementValidationTests.class, XslAttributeValidationTests.class,
+ XPathExprFunctionCallSuffixTests.class })
public class Xsl3TestSuite1 {
}
diff --git a/tests/xpath_function_call_suffix/gold/test1.out b/tests/xpath_function_call_suffix/gold/test1.out
new file mode 100644
index 000000000..bac2334e7
--- /dev/null
+++ b/tests/xpath_function_call_suffix/gold/test1.out
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+ <one>2 4 10 20 30</one>
+ <two>hello 2 hello -4 hello 10 hello -20 hello 30</two>
+</result>
diff --git a/tests/xpath_function_call_suffix/gold/test2.out b/tests/xpath_function_call_suffix/gold/test2.out
new file mode 100644
index 000000000..e5ea57ec0
--- /dev/null
+++ b/tests/xpath_function_call_suffix/gold/test2.out
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><result>2 4 10 20 30</result>
diff --git a/tests/xpath_function_call_suffix/gold/test3.out b/tests/xpath_function_call_suffix/gold/test3.out
new file mode 100644
index 000000000..a96be1149
--- /dev/null
+++ b/tests/xpath_function_call_suffix/gold/test3.out
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+ <item>2</item>
+ <item>4</item>
+ <item>10</item>
+ <item>20</item>
+ <item>30</item>
+</result>
diff --git a/tests/xpath_function_call_suffix/gold/test4.out b/tests/xpath_function_call_suffix/gold/test4.out
new file mode 100644
index 000000000..a96be1149
--- /dev/null
+++ b/tests/xpath_function_call_suffix/gold/test4.out
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+ <item>2</item>
+ <item>4</item>
+ <item>10</item>
+ <item>20</item>
+ <item>30</item>
+</result>
diff --git a/tests/xpath_function_call_suffix/test1.xml b/tests/xpath_function_call_suffix/test1.xml
new file mode 100644
index 000000000..3c8f7c3ac
--- /dev/null
+++ b/tests/xpath_function_call_suffix/test1.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<info>
+ <a val="2">a1</a>
+ <a val="-4">a2</a>
+ <a val="10">a3</a>
+ <a val="-20">a4</a>
+ <a val="30">a5</a>
+</info>
\ No newline at end of file
diff --git a/tests/xpath_function_call_suffix/test1.xsl b/tests/xpath_function_call_suffix/test1.xsl
new file mode 100644
index 000000000..54121cc64
--- /dev/null
+++ b/tests/xpath_function_call_suffix/test1.xsl
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- use with test1.xml -->
+
+ <!-- An XSLT test case to test, XPath path expression
+ having a function call suffix.
+ -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template match="/info">
+ <result>
+ <one>
+ <xsl:value-of select=".//@val/abs(.)"/>
+ </one>
+ <two>
+ <xsl:value-of select=".//@val/concat('hello', ' ', .)"/>
+ </two>
+ </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/xpath_function_call_suffix/test2.xsl b/tests/xpath_function_call_suffix/test2.xsl
new file mode 100644
index 000000000..028be9aae
--- /dev/null
+++ b/tests/xpath_function_call_suffix/test2.xsl
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- use with test1.xml -->
+
+ <!-- An XSLT test case to test, XPath path expression
+ having a function call suffix.
+ -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template match="/info">
+ <result>
+ <xsl:variable name="seq1" select=".//@val/abs(.)"/>
+ <xsl:value-of select="$seq1"/>
+ </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/xpath_function_call_suffix/test3.xsl b/tests/xpath_function_call_suffix/test3.xsl
new file mode 100644
index 000000000..af1016809
--- /dev/null
+++ b/tests/xpath_function_call_suffix/test3.xsl
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- use with test1.xml -->
+
+ <!-- An XSLT test case to test, XPath path expression
+ having a function call suffix.
+ -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template match="/info">
+ <result>
+ <xsl:for-each select=".//@val/abs(.)">
+ <item>
+ <xsl:value-of select="."/>
+ </item>
+ </xsl:for-each>
+ </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/xpath_function_call_suffix/test4.xsl b/tests/xpath_function_call_suffix/test4.xsl
new file mode 100644
index 000000000..ed3ad2448
--- /dev/null
+++ b/tests/xpath_function_call_suffix/test4.xsl
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- use with test1.xml -->
+
+ <!-- An XSLT test case to test, XPath path expression
+ having a function call suffix.
+ -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template match="/info">
+ <result>
+ <xsl:iterate select=".//@val/abs(.)">
+ <item>
+ <xsl:value-of select="."/>
+ </item>
+ </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>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xalan.apache.org
For additional commands, e-mail: commits-help@xalan.apache.org