vlsi commented on code in PR #13:
URL: https://github.com/apache/xalan-java/pull/13#discussion_r1247444607


##########
src/org/apache/xpath/functions/FuncFilter.java:
##########
@@ -0,0 +1,237 @@
+/*
+ * 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.functions;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+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.objects.InlineFunction;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XObjectFactory;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.w3c.dom.Node;
+
+/**
+ * Execute the filter() function.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+/*
+ * fn:filter is one of XPath 3.1's higher-order function

Review Comment:
   This breaks comment, so the documentation won't be visible in javadoc



##########
src/org/apache/xpath/functions/FuncForEach.java:
##########
@@ -0,0 +1,212 @@
+/*
+ * 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.functions;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+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.objects.InlineFunction;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XObjectFactory;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.w3c.dom.Node;
+
+/**
+ * Execute the for-each() function.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+/*
+ * fn:for-each is one of XPath 3.1's higher-order function
+ * (ref, https://www.w3.org/TR/xpath-functions-31/#higher-order-functions).
+ * 
+ * The XPath function fn:for-each has following signature, and definition,
+ * 
+ * fn:for-each($seq as item()*, $action as function(item()) as item()*) as 
item()*
+ * 
+ * The function fn:for-each applies the function item $action to every item 
from 
+ * the sequence $seq in turn, returning the concatenation of the resulting 
sequences
+ * in order.
+ */
+public class FuncForEach extends Function2Args {
+
+   private static final long serialVersionUID = 2912594883291006421L;
+   
+   private static final String FUNCTION_NAME = "for-each()"; 
+
+  /**
+   * Execute the function. The function must return a valid object.
+   * 
+   * @param xctxt The current execution context.
+   * 
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws 
javax.xml.transform.TransformerException
+  {                      
+      
+        XObject funcEvaluationResult = null;
+        
+        final int contextNode = xctxt.getCurrentNode();
+        
+        Expression arg0 = getArg0();
+        Expression arg1 = getArg1();                
+        
+        DTMIterator arg0DtmIterator = null;
+        
+        XObject arg0XsObject = null;
+                  
+        if (arg0 instanceof LocPathIterator) {
+            arg0DtmIterator = arg0.asIterator(xctxt, contextNode);             
  
+        }
+        else {
+            arg0XsObject = arg0.execute(xctxt, contextNode);
+        }
+        
+        ResultSequence resultSeq = new ResultSequence();
+                    
+        if (arg1 instanceof InlineFunction) {            
+            resultSeq = evaluateFnForEach(xctxt, arg0XsObject, 
arg0DtmIterator, (InlineFunction)arg1); 
+        }
+        else if (arg1 instanceof Variable) {
+            XObject arg1VarValue = arg1.execute(xctxt);
+            if (arg1VarValue instanceof InlineFunction) {
+                resultSeq = evaluateFnForEach(xctxt, arg0XsObject, 
arg0DtmIterator, (InlineFunction)arg1VarValue);   
+            }
+            else {
+                throw new javax.xml.transform.TransformerException("FORG0006 : 
The second argument to function call for-each(), "
+                                                                               
                + "is not a function item.", xctxt.getSAXLocator());    
+            }
+        }
+        else {
+            throw new javax.xml.transform.TransformerException("FORG0006 : The 
second argument to function call for-each(), "
+                                                                               
            + "is not a function item.", xctxt.getSAXLocator());               
+        }            
+        
+        funcEvaluationResult = resultSeq;
+        
+        return funcEvaluationResult;
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct.
+   *
+   * @param argNum The number of arguments that is being passed to the 
function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+     if (argNum != 2) {
+        reportWrongNumberArgs();

Review Comment:
   Having actual number of detected arguments helps users to analyze failures



##########
src/org/apache/xpath/functions/FuncFilter.java:
##########
@@ -0,0 +1,237 @@
+/*
+ * 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.functions;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+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.objects.InlineFunction;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XObjectFactory;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.w3c.dom.Node;
+
+/**
+ * Execute the filter() function.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+/*
+ * fn:filter is one of XPath 3.1's higher-order function
+ * (ref, https://www.w3.org/TR/xpath-functions-31/#higher-order-functions).
+ * 
+ * The XPath function fn:filter has following signature, and definition,
+ * 
+ * fn:filter($seq as item()*, $f as function(item()) as xs:boolean) as item()*
+ * 
+ * The function fn:filter returns those items from the sequence $seq for which 
the 
+ * supplied function $f returns true.
+ * 
+ * Error conditions,
+   As a consequence of the function signature and the function calling rules, 
a type 
+   error occurs if the supplied function $f returns anything other than a 
single 
+   xs:boolean item. There is no conversion to an effective boolean value.
+ */
+public class FuncFilter extends Function2Args {
+
+   private static final long serialVersionUID = 2912594883291006421L;
+   
+   private static final String FUNCTION_NAME = "filter()"; 
+
+  /**
+   * Execute the function. The function must return a valid object.
+   * 
+   * @param xctxt The current execution context.
+   * 
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws 
javax.xml.transform.TransformerException
+  {
+      
+        XObject funcEvaluationResult = null;
+        
+        final int contextNode = xctxt.getCurrentNode();
+        
+        Expression arg0 = getArg0();
+        Expression arg1 = getArg1();                
+        
+        DTMIterator arg0DtmIterator = null;
+        
+        XObject arg0XsObject = null;
+                  
+        if (arg0 instanceof LocPathIterator) {
+            arg0DtmIterator = arg0.asIterator(xctxt, contextNode);             
  
+        }
+        else {
+            arg0XsObject = arg0.execute(xctxt, contextNode);
+        }
+        
+        ResultSequence resultSeq = new ResultSequence();
+                    
+        if (arg1 instanceof InlineFunction) {            
+            resultSeq = evaluateFnFilter(xctxt, arg0XsObject, arg0DtmIterator, 
(InlineFunction)arg1); 
+        }
+        else if (arg1 instanceof Variable) {
+            XObject arg1VarValue = arg1.execute(xctxt);
+            if (arg1VarValue instanceof InlineFunction) {
+                resultSeq = evaluateFnFilter(xctxt, arg0XsObject, 
arg0DtmIterator, (InlineFunction)arg1VarValue);   
+            }
+            else {
+                throw new javax.xml.transform.TransformerException("FORG0006 : 
The second argument to function call filter(), "
+                                                                               
                + "is not a function item.", xctxt.getSAXLocator());    
+            }
+        }
+        else {
+            throw new javax.xml.transform.TransformerException("FORG0006 : The 
second argument to function call filter(), "
+                                                                               
            + "is not a function item.", xctxt.getSAXLocator());               

Review Comment:
   It is bad when different errors have exactly the same text. It would be 
better if the error messages were different, and it would help if they included 
something related to the actual type of the argument



##########
src/org/apache/xpath/compiler/XPathParser.java:
##########
@@ -1561,6 +1582,75 @@ else if (lookahead('(', 1) || (lookahead(':', 1) && 
lookahead('(', 3)))
 
     return matchFound;
   }
+  
+  protected InlineFunction InlineFunctionExpr() throws 
javax.xml.transform.TransformerException {
+      InlineFunction inlineFunction = new InlineFunction();
+      
+      List<String> funcParamNameList = new ArrayList<String>();      
+      String funcBodyXPathExprStr = null;
+      
+      nextToken();
+      
+      consumeExpected('(');
+
+      if (!tokenIs(')')) {
+          while (!tokenIs(')') && m_token != null)
+          {
+              if (tokenIs(','))
+              {
+                  
error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_PARAM, null);
+              }
+    
+              if (m_tokenChar == '$')
+              {
+                  nextToken();
+                  funcParamNameList.add(m_token);
+                  nextToken();
+              }
+    
+              if (!tokenIs(')'))
+              {
+                  consumeExpected(',');
+    
+                  if (tokenIs(')'))
+                  {
+                      
error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_FOLLOWING_PARAM, null);         
           
+                  }
+              }
+          }    
+      }
+      
+      if (funcParamNameList.size() > 1) {
+          error(XPATHErrorResources.ER_INLINE_FUNCTION_PARAM_CARDINALITY, new 
Object[] { 
+                                                                
Integer.valueOf(funcParamNameList.size()) });   
+      }
+      
+      inlineFunction.setFuncParamNameList(funcParamNameList);
+      
+      consumeExpected(')');
+      
+      consumeExpected('{');
+      
+      StringBuffer funcBodyXPathExprStrBuff = new StringBuffer();

Review Comment:
   `StringBuilder`?



##########
tests/org/apache/xalan/xpath3/FnFilterTests.java:
##########
@@ -0,0 +1,80 @@
+/*
+ * 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.XslTransformTestsUtil;
+import org.apache.xalan.xslt3.XSLConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * XPath 3.1 function fn:filter test cases.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+public class FnFilterTests extends XslTransformTestsUtil {        
+    
+    private static final String XSL_TRANSFORM_INPUT_DIRPATH = 
XSLConstants.XSL_TRANSFORM_INPUT_DIRPATH_PREFIX + "fn_filter/";
+    
+    private static final String XSL_TRANSFORM_GOLD_DIRPATH = 
XSLConstants.XSL_TRANSFORM_GOLD_DIRPATH_PREFIX + "fn_filter/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 xslFnFilterTest1() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xsl"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test1.out";        
        
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, 
null);

Review Comment:
   This could be simplified as something like
   
   ```java
   assertValueOf(
       /* xsl= */ "filter(1 to 10, function($a) { true() })",
       /* output= */ "1 2 3 4 5 6 7 8 9 10"
   );
   assertValueOf(
       /* xsl= */ "filter(1 to 10, function($a) { false() })",
       /* output= */ ""
   );
   ```
   
   Creating separate xls files for trivial test cases makes it hard to 
understand what is tested and what is not as there are tons of files where a 
single Java line would be enough.
   
   



##########
src/org/apache/xpath/functions/FuncFilter.java:
##########
@@ -0,0 +1,237 @@
+/*
+ * 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.functions;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+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.objects.InlineFunction;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XObjectFactory;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.w3c.dom.Node;
+
+/**
+ * Execute the filter() function.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+/*
+ * fn:filter is one of XPath 3.1's higher-order function
+ * (ref, https://www.w3.org/TR/xpath-functions-31/#higher-order-functions).
+ * 
+ * The XPath function fn:filter has following signature, and definition,
+ * 
+ * fn:filter($seq as item()*, $f as function(item()) as xs:boolean) as item()*
+ * 
+ * The function fn:filter returns those items from the sequence $seq for which 
the 
+ * supplied function $f returns true.
+ * 
+ * Error conditions,
+   As a consequence of the function signature and the function calling rules, 
a type 
+   error occurs if the supplied function $f returns anything other than a 
single 
+   xs:boolean item. There is no conversion to an effective boolean value.
+ */
+public class FuncFilter extends Function2Args {
+
+   private static final long serialVersionUID = 2912594883291006421L;
+   
+   private static final String FUNCTION_NAME = "filter()"; 
+
+  /**
+   * Execute the function. The function must return a valid object.
+   * 
+   * @param xctxt The current execution context.
+   * 
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws 
javax.xml.transform.TransformerException
+  {
+      
+        XObject funcEvaluationResult = null;
+        
+        final int contextNode = xctxt.getCurrentNode();
+        
+        Expression arg0 = getArg0();
+        Expression arg1 = getArg1();                
+        
+        DTMIterator arg0DtmIterator = null;
+        
+        XObject arg0XsObject = null;
+                  
+        if (arg0 instanceof LocPathIterator) {
+            arg0DtmIterator = arg0.asIterator(xctxt, contextNode);             
  
+        }
+        else {
+            arg0XsObject = arg0.execute(xctxt, contextNode);
+        }
+        
+        ResultSequence resultSeq = new ResultSequence();

Review Comment:
   This initialization is redundant as it will be overwritten below



##########
src/org/apache/xpath/compiler/XPathParser.java:
##########
@@ -1561,6 +1582,75 @@ else if (lookahead('(', 1) || (lookahead(':', 1) && 
lookahead('(', 3)))
 
     return matchFound;
   }
+  
+  protected InlineFunction InlineFunctionExpr() throws 
javax.xml.transform.TransformerException {
+      InlineFunction inlineFunction = new InlineFunction();
+      
+      List<String> funcParamNameList = new ArrayList<String>();      
+      String funcBodyXPathExprStr = null;
+      
+      nextToken();
+      
+      consumeExpected('(');
+
+      if (!tokenIs(')')) {
+          while (!tokenIs(')') && m_token != null)
+          {
+              if (tokenIs(','))
+              {
+                  
error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_PARAM, null);
+              }
+    
+              if (m_tokenChar == '$')
+              {
+                  nextToken();
+                  funcParamNameList.add(m_token);
+                  nextToken();
+              }
+    
+              if (!tokenIs(')'))
+              {
+                  consumeExpected(',');
+    
+                  if (tokenIs(')'))
+                  {
+                      
error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_FOLLOWING_PARAM, null);         
           
+                  }
+              }
+          }    
+      }
+      
+      if (funcParamNameList.size() > 1) {
+          error(XPATHErrorResources.ER_INLINE_FUNCTION_PARAM_CARDINALITY, new 
Object[] { 
+                                                                
Integer.valueOf(funcParamNameList.size()) });   

Review Comment:
   Why limit the number of parameters in the parser?



##########
src/org/apache/xpath/compiler/XPathParser.java:
##########
@@ -1561,6 +1582,75 @@ else if (lookahead('(', 1) || (lookahead(':', 1) && 
lookahead('(', 3)))
 
     return matchFound;
   }
+  
+  protected InlineFunction InlineFunctionExpr() throws 
javax.xml.transform.TransformerException {
+      InlineFunction inlineFunction = new InlineFunction();
+      
+      List<String> funcParamNameList = new ArrayList<String>();      
+      String funcBodyXPathExprStr = null;
+      
+      nextToken();
+      
+      consumeExpected('(');
+
+      if (!tokenIs(')')) {
+          while (!tokenIs(')') && m_token != null)
+          {
+              if (tokenIs(','))
+              {
+                  
error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_PARAM, null);
+              }
+    
+              if (m_tokenChar == '$')
+              {
+                  nextToken();
+                  funcParamNameList.add(m_token);
+                  nextToken();
+              }
+    
+              if (!tokenIs(')'))
+              {
+                  consumeExpected(',');
+    
+                  if (tokenIs(')'))
+                  {
+                      
error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_FOLLOWING_PARAM, null);         
           
+                  }
+              }
+          }    
+      }
+      
+      if (funcParamNameList.size() > 1) {
+          error(XPATHErrorResources.ER_INLINE_FUNCTION_PARAM_CARDINALITY, new 
Object[] { 
+                                                                
Integer.valueOf(funcParamNameList.size()) });   
+      }
+      
+      inlineFunction.setFuncParamNameList(funcParamNameList);
+      
+      consumeExpected(')');
+      
+      consumeExpected('{');
+      
+      StringBuffer funcBodyXPathExprStrBuff = new StringBuffer();
+      
+      if (tokenIs('}')) {
+          consumeExpected('}');    
+      }
+      else {
+         while (!tokenIs('}') && m_token != null)
+         {
+             funcBodyXPathExprStrBuff.append(m_token);
+             nextToken();

Review Comment:
   What if inline functions are nested? Will this work?



##########
src/org/apache/xalan/templates/ElemCopyOf.java:
##########
@@ -192,6 +198,46 @@ else if (t == DTM.ATTRIBUTE_NODE)
           SerializerUtils.outputResultTreeFragment(
             handler, value, transformer.getXPathContext());
           break;
+        case XObject.CLASS_RESULT_SEQUENCE :
+          // added for XSLT 3.0
+          ResultSequence resultSequence = (ResultSequence)value;
+          for (int idx = 0; idx < resultSequence.size(); idx++) {
+             XObject sequenceItem = resultSequence.item(idx);
+             
+             if (sequenceItem.getType() == XObject.CLASS_STRING) {
+                 String str = sequenceItem.str();
+                 handler.characters(str.toCharArray(), 0, str.length());
+                 if (idx < (resultSequence.size() - 1)) {
+                     String strSpace = " ";
+                     handler.characters(strSpace.toCharArray(), 0, 
strSpace.length());

Review Comment:
   This looks like unneccesary `" ".toCharArray()` in a loop. It can probably 
cached in a static final field.
   



##########
src/org/apache/xpath/functions/FuncFilter.java:
##########
@@ -0,0 +1,237 @@
+/*
+ * 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.functions;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+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.objects.InlineFunction;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XObjectFactory;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.w3c.dom.Node;
+
+/**
+ * Execute the filter() function.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+/*
+ * fn:filter is one of XPath 3.1's higher-order function
+ * (ref, https://www.w3.org/TR/xpath-functions-31/#higher-order-functions).
+ * 
+ * The XPath function fn:filter has following signature, and definition,
+ * 
+ * fn:filter($seq as item()*, $f as function(item()) as xs:boolean) as item()*
+ * 
+ * The function fn:filter returns those items from the sequence $seq for which 
the 
+ * supplied function $f returns true.
+ * 
+ * Error conditions,
+   As a consequence of the function signature and the function calling rules, 
a type 
+   error occurs if the supplied function $f returns anything other than a 
single 
+   xs:boolean item. There is no conversion to an effective boolean value.
+ */
+public class FuncFilter extends Function2Args {
+
+   private static final long serialVersionUID = 2912594883291006421L;
+   
+   private static final String FUNCTION_NAME = "filter()"; 
+
+  /**
+   * Execute the function. The function must return a valid object.
+   * 
+   * @param xctxt The current execution context.
+   * 
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws 
javax.xml.transform.TransformerException
+  {
+      
+        XObject funcEvaluationResult = null;
+        
+        final int contextNode = xctxt.getCurrentNode();
+        
+        Expression arg0 = getArg0();
+        Expression arg1 = getArg1();                
+        
+        DTMIterator arg0DtmIterator = null;
+        
+        XObject arg0XsObject = null;
+                  
+        if (arg0 instanceof LocPathIterator) {
+            arg0DtmIterator = arg0.asIterator(xctxt, contextNode);             
  
+        }
+        else {
+            arg0XsObject = arg0.execute(xctxt, contextNode);
+        }
+        
+        ResultSequence resultSeq = new ResultSequence();
+                    
+        if (arg1 instanceof InlineFunction) {            
+            resultSeq = evaluateFnFilter(xctxt, arg0XsObject, arg0DtmIterator, 
(InlineFunction)arg1); 
+        }
+        else if (arg1 instanceof Variable) {
+            XObject arg1VarValue = arg1.execute(xctxt);
+            if (arg1VarValue instanceof InlineFunction) {
+                resultSeq = evaluateFnFilter(xctxt, arg0XsObject, 
arg0DtmIterator, (InlineFunction)arg1VarValue);   
+            }
+            else {
+                throw new javax.xml.transform.TransformerException("FORG0006 : 
The second argument to function call filter(), "
+                                                                               
                + "is not a function item.", xctxt.getSAXLocator());    
+            }
+        }
+        else {
+            throw new javax.xml.transform.TransformerException("FORG0006 : The 
second argument to function call filter(), "
+                                                                               
            + "is not a function item.", xctxt.getSAXLocator());               
+        }            
+        
+        funcEvaluationResult = resultSeq;
+        
+        return funcEvaluationResult;
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct.
+   *
+   * @param argNum The number of arguments that is being passed to the 
function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+     if (argNum != 2) {
+        reportWrongNumberArgs();

Review Comment:
   It should be better to include the actual number of used arguments in the 
error message



##########
src/org/apache/xpath/functions/FuncForEach.java:
##########
@@ -0,0 +1,212 @@
+/*
+ * 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.functions;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+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.objects.InlineFunction;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XObjectFactory;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.w3c.dom.Node;
+
+/**
+ * Execute the for-each() function.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+/*
+ * fn:for-each is one of XPath 3.1's higher-order function
+ * (ref, https://www.w3.org/TR/xpath-functions-31/#higher-order-functions).
+ * 
+ * The XPath function fn:for-each has following signature, and definition,
+ * 
+ * fn:for-each($seq as item()*, $action as function(item()) as item()*) as 
item()*
+ * 
+ * The function fn:for-each applies the function item $action to every item 
from 
+ * the sequence $seq in turn, returning the concatenation of the resulting 
sequences
+ * in order.
+ */
+public class FuncForEach extends Function2Args {
+
+   private static final long serialVersionUID = 2912594883291006421L;
+   
+   private static final String FUNCTION_NAME = "for-each()"; 
+
+  /**
+   * Execute the function. The function must return a valid object.
+   * 
+   * @param xctxt The current execution context.
+   * 
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws 
javax.xml.transform.TransformerException
+  {                      
+      
+        XObject funcEvaluationResult = null;
+        
+        final int contextNode = xctxt.getCurrentNode();
+        
+        Expression arg0 = getArg0();
+        Expression arg1 = getArg1();                
+        
+        DTMIterator arg0DtmIterator = null;
+        
+        XObject arg0XsObject = null;
+                  
+        if (arg0 instanceof LocPathIterator) {
+            arg0DtmIterator = arg0.asIterator(xctxt, contextNode);             
  
+        }
+        else {
+            arg0XsObject = arg0.execute(xctxt, contextNode);
+        }
+        
+        ResultSequence resultSeq = new ResultSequence();
+                    
+        if (arg1 instanceof InlineFunction) {            
+            resultSeq = evaluateFnForEach(xctxt, arg0XsObject, 
arg0DtmIterator, (InlineFunction)arg1); 
+        }
+        else if (arg1 instanceof Variable) {
+            XObject arg1VarValue = arg1.execute(xctxt);
+            if (arg1VarValue instanceof InlineFunction) {
+                resultSeq = evaluateFnForEach(xctxt, arg0XsObject, 
arg0DtmIterator, (InlineFunction)arg1VarValue);   
+            }
+            else {
+                throw new javax.xml.transform.TransformerException("FORG0006 : 
The second argument to function call for-each(), "
+                                                                               
                + "is not a function item.", xctxt.getSAXLocator());    
+            }
+        }
+        else {
+            throw new javax.xml.transform.TransformerException("FORG0006 : The 
second argument to function call for-each(), "
+                                                                               
            + "is not a function item.", xctxt.getSAXLocator());               

Review Comment:
   The errors have the same text yet they are from different branches. It would 
be hard to tell what went wrong



##########
src/org/apache/xpath/objects/InlineFunction.java:
##########
@@ -0,0 +1,77 @@
+/*
+ * 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.objects;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+
+/*
+ * The XalanJ xpath parser, creates and populates this object,
+ * as a representation of XPath 3.1 function item "inline function" 
+ * definition within XPath expressions.
+ * 
+ * The XPath 3.1 spec, defines function item "inline function" XPath 
+ * expressions with following grammar,
+
+   InlineFunctionExpr        ::=  "function" "(" ParamList? ")" ("as" 
SequenceType)? FunctionBody
+
+   ParamList                 ::=   Param ("," Param)*
+
+   Param                     ::=   "$" EQName TypeDeclaration?
+
+   FunctionBody              ::=   EnclosedExpr
+
+   EnclosedExpr              ::=   "{" Expr? "}"

Review Comment:
   This will be ill-formatted in javadoc. I guess it should be wrapped with 
`<pre>` or something like that



##########
src/org/apache/xalan/templates/ElemCopyOf.java:
##########
@@ -32,15 +32,21 @@
 import org.apache.xml.serializer.SerializationHandler;
 import org.apache.xpath.XPath;
 import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.ResultSequence;
 import org.apache.xpath.objects.XObject;
 
-/**
- * Implement xsl:copy-of.
- * <pre>
- * <!ELEMENT xsl:copy-of EMPTY>
- * <!ATTLIST xsl:copy-of select %expr; #REQUIRED>
- * </pre>
- * @see <a href="http://www.w3.org/TR/xslt#copy-of";>copy-of in XSLT 
Specification</a>
+/*
+ * Implementation of XSLT xsl:copy-of instruction.
+ * 
+ * XSLT 3.0 xsl:copy-of instruction definition,
+ *  
+ * <xsl:copy-of
+          select = expression
+          copy-accumulators? = boolean
+          copy-namespaces? = boolean
+          type? = eqname
+          validation? = "strict" | "lax" | "preserve" | "strip" />

Review Comment:
   This won't be properly formatted in javadoc. I guess something like `<pre>` 
is needed



##########
src/org/apache/xpath/objects/InlineFunction.java:
##########
@@ -0,0 +1,77 @@
+/*
+ * 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.objects;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+
+/*
+ * The XalanJ xpath parser, creates and populates this object,
+ * as a representation of XPath 3.1 function item "inline function" 
+ * definition within XPath expressions.
+ * 
+ * The XPath 3.1 spec, defines function item "inline function" XPath 
+ * expressions with following grammar,
+
+   InlineFunctionExpr        ::=  "function" "(" ParamList? ")" ("as" 
SequenceType)? FunctionBody
+
+   ParamList                 ::=   Param ("," Param)*
+
+   Param                     ::=   "$" EQName TypeDeclaration?
+
+   FunctionBody              ::=   EnclosedExpr
+
+   EnclosedExpr              ::=   "{" Expr? "}"
+   
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ *   
+ * @xsl.usage advanced  
+ */
+public class InlineFunction extends XObject {
+
+    private static final long serialVersionUID = 9219253671212483045L;
+    
+    private List<String> funcParamNameList = new ArrayList<String>();
+    
+    private String funcBodyXPathExprStr = null;
+
+    public List<String> getFuncParamNameList() {
+        return funcParamNameList;
+    }
+
+    public void setFuncParamNameList(List<String> funcParamNameList) {
+        this.funcParamNameList = funcParamNameList;
+    }

Review Comment:
   Should parameter names use `QName` instead of `String`?



##########
tests/org/apache/xalan/xpath3/FnFilterTests.java:
##########
@@ -0,0 +1,80 @@
+/*
+ * 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.XslTransformTestsUtil;
+import org.apache.xalan.xslt3.XSLConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * XPath 3.1 function fn:filter test cases.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+public class FnFilterTests extends XslTransformTestsUtil {        
+    
+    private static final String XSL_TRANSFORM_INPUT_DIRPATH = 
XSLConstants.XSL_TRANSFORM_INPUT_DIRPATH_PREFIX + "fn_filter/";
+    
+    private static final String XSL_TRANSFORM_GOLD_DIRPATH = 
XSLConstants.XSL_TRANSFORM_GOLD_DIRPATH_PREFIX + "fn_filter/gold/";
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+        // no op
+    }
+
+    @AfterClass
+    public static void tearDownAfterClass() throws Exception {        
+        xmlDocumentBuilderFactory = null;
+        xmlDocumentBuilder = null;
+        xslTransformerFactory = null;
+    }

Review Comment:
   Why adding no-op methods? It would be better to remove them
   
   JUnit does not use class after execution, so nulling out the fields adds no 
value.



##########
tests/fn_foreach/test8.xsl:
##########
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+                version="3.0">
+                
+   <!-- Author: muk...@apache.org -->
+   
+   <!-- Test for the XPath 3.1 fn:for-each() function -->
+   
+   <!-- This XSLT stylesheet tests, passing an XPath function
+        item as a template parameter argument. -->                  

Review Comment:
   It would be better if you could use `function_as_template_parameter` instead 
of `test8` in test names.



##########
src/org/apache/xpath/functions/FuncFilter.java:
##########
@@ -0,0 +1,237 @@
+/*
+ * 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.functions;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+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.objects.InlineFunction;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XObjectFactory;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.w3c.dom.Node;
+
+/**
+ * Execute the filter() function.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+/*
+ * fn:filter is one of XPath 3.1's higher-order function
+ * (ref, https://www.w3.org/TR/xpath-functions-31/#higher-order-functions).
+ * 
+ * The XPath function fn:filter has following signature, and definition,
+ * 
+ * fn:filter($seq as item()*, $f as function(item()) as xs:boolean) as item()*
+ * 
+ * The function fn:filter returns those items from the sequence $seq for which 
the 
+ * supplied function $f returns true.
+ * 
+ * Error conditions,
+   As a consequence of the function signature and the function calling rules, 
a type 
+   error occurs if the supplied function $f returns anything other than a 
single 
+   xs:boolean item. There is no conversion to an effective boolean value.
+ */
+public class FuncFilter extends Function2Args {
+
+   private static final long serialVersionUID = 2912594883291006421L;
+   
+   private static final String FUNCTION_NAME = "filter()"; 
+
+  /**
+   * Execute the function. The function must return a valid object.
+   * 
+   * @param xctxt The current execution context.
+   * 
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws 
javax.xml.transform.TransformerException
+  {
+      
+        XObject funcEvaluationResult = null;
+        
+        final int contextNode = xctxt.getCurrentNode();
+        
+        Expression arg0 = getArg0();
+        Expression arg1 = getArg1();                
+        
+        DTMIterator arg0DtmIterator = null;
+        
+        XObject arg0XsObject = null;
+                  
+        if (arg0 instanceof LocPathIterator) {
+            arg0DtmIterator = arg0.asIterator(xctxt, contextNode);             
  
+        }
+        else {
+            arg0XsObject = arg0.execute(xctxt, contextNode);
+        }
+        
+        ResultSequence resultSeq = new ResultSequence();
+                    
+        if (arg1 instanceof InlineFunction) {            
+            resultSeq = evaluateFnFilter(xctxt, arg0XsObject, arg0DtmIterator, 
(InlineFunction)arg1); 
+        }
+        else if (arg1 instanceof Variable) {
+            XObject arg1VarValue = arg1.execute(xctxt);
+            if (arg1VarValue instanceof InlineFunction) {
+                resultSeq = evaluateFnFilter(xctxt, arg0XsObject, 
arg0DtmIterator, (InlineFunction)arg1VarValue);   
+            }
+            else {
+                throw new javax.xml.transform.TransformerException("FORG0006 : 
The second argument to function call filter(), "
+                                                                               
                + "is not a function item.", xctxt.getSAXLocator());    
+            }
+        }
+        else {
+            throw new javax.xml.transform.TransformerException("FORG0006 : The 
second argument to function call filter(), "
+                                                                               
            + "is not a function item.", xctxt.getSAXLocator());               
+        }            
+        
+        funcEvaluationResult = resultSeq;
+        
+        return funcEvaluationResult;
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct.
+   *
+   * @param argNum The number of arguments that is being passed to the 
function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+     if (argNum != 2) {
+        reportWrongNumberArgs();
+     }
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createXPATHMessage(
+                                              XPATHErrorResources.ER_TWO, 
null)); //"2"
+  }
+  
+  /*
+   * Evaluate the function call fn:filter.
+   */
+  private ResultSequence evaluateFnFilter(XPathContext xctxt, XObject 
arg0XsObject, 
+                                               DTMIterator arg0DtmIterator, 
InlineFunction arg1) 
+                                                                               
     throws TransformerException {
+
+        ResultSequence resultSeq = new ResultSequence(); 
+        
+        List<String> funcParamNameList = arg1.getFuncParamNameList();
+        String funcBodyXPathExprStr = arg1.getFuncBodyXPathExprStr();
+        
+        if (funcBodyXPathExprStr == null || "".equals(funcBodyXPathExprStr)) {
+           return resultSeq;
+        }
+        
+        QName varQname = null;
+        
+        if (funcParamNameList.size() == 1) {
+           varQname = new QName(funcParamNameList.get(0));
+        }
+        
+        SourceLocator srcLocator = xctxt.getSAXLocator();
+        
+        XPath xpathInlineFn = new XPath(funcBodyXPathExprStr, srcLocator, 
null, XPath.SELECT, null);
+        
+        if (arg0XsObject instanceof ResultSequence) {
+           XPathContext xpathContextNew = new XPathContext(false);
+           Map<QName, XObject> inlineFunctionVarMap = 
xpathContextNew.getInlineFunctionVarMap();
+        
+           ResultSequence inpResultSeq = (ResultSequence)arg0XsObject;
+           for (int idx = 0; idx < inpResultSeq.size(); idx++) {
+               XObject inpSeqItem = inpResultSeq.item(idx);
+               if (varQname != null) {
+                  inlineFunctionVarMap.put(varQname, inpSeqItem);
+               }
+        
+               XObject resultObj = xpathInlineFn.execute(xpathContextNew, 
DTM.NULL, null);
+               if (resultObj instanceof XBoolean) {
+                   if (((XBoolean)resultObj).bool()) {
+                      resultSeq.add(inpSeqItem);
+                   }
+               }
+               else {
+                   throw new 
javax.xml.transform.TransformerException("XPTY0004 : The item type of the 
result of calling "
+                                                                               
               + "function filter() is not xs:boolean.", 
xctxt.getSAXLocator()); 
+               }
+           }
+        
+           inlineFunctionVarMap.clear();
+        }
+        else if (arg0DtmIterator != null) {                  
+           Map<QName, XObject> inlineFunctionVarMap = 
xctxt.getInlineFunctionVarMap();
+        
+           int dtmNodeHandle;
+           
+           while (DTM.NULL != (dtmNodeHandle = arg0DtmIterator.nextNode())) {
+               DTM dtm = xctxt.getDTM(dtmNodeHandle);
+               Node node = dtm.getNode(dtmNodeHandle);
+               XObject inpSeqItem = XObjectFactory.create(node, xctxt);        
       
+               if (varQname != null) {
+                  inlineFunctionVarMap.put(varQname, inpSeqItem);
+               }
+        
+               xctxt.pushCurrentNode(dtmNodeHandle);
+               
+               XObject resultObj = xpathInlineFn.execute(xctxt, dtmNodeHandle, 
null);
+               if (resultObj instanceof XBoolean) {
+                   if (((XBoolean)resultObj).bool()) {                       
+                       resultSeq.add(new XNodeSet(dtmNodeHandle, xctxt));
+                   }
+               }
+               else {
+                   throw new 
javax.xml.transform.TransformerException("XPTY0004 : The item type of the 
result of calling "
+                                                                               
               + "function filter() is not xs:boolean.", 
xctxt.getSAXLocator());  

Review Comment:
   It would help if the error message could refer to the type (and value) of 
the actual resut



##########
src/org/apache/xpath/functions/FuncFilter.java:
##########
@@ -0,0 +1,237 @@
+/*
+ * 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.functions;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+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.objects.InlineFunction;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XObjectFactory;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.w3c.dom.Node;
+
+/**
+ * Execute the filter() function.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+/*
+ * fn:filter is one of XPath 3.1's higher-order function
+ * (ref, https://www.w3.org/TR/xpath-functions-31/#higher-order-functions).
+ * 
+ * The XPath function fn:filter has following signature, and definition,
+ * 
+ * fn:filter($seq as item()*, $f as function(item()) as xs:boolean) as item()*
+ * 
+ * The function fn:filter returns those items from the sequence $seq for which 
the 
+ * supplied function $f returns true.
+ * 
+ * Error conditions,
+   As a consequence of the function signature and the function calling rules, 
a type 
+   error occurs if the supplied function $f returns anything other than a 
single 
+   xs:boolean item. There is no conversion to an effective boolean value.
+ */
+public class FuncFilter extends Function2Args {
+
+   private static final long serialVersionUID = 2912594883291006421L;
+   
+   private static final String FUNCTION_NAME = "filter()"; 
+
+  /**
+   * Execute the function. The function must return a valid object.
+   * 
+   * @param xctxt The current execution context.
+   * 
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws 
javax.xml.transform.TransformerException
+  {
+      
+        XObject funcEvaluationResult = null;
+        
+        final int contextNode = xctxt.getCurrentNode();
+        
+        Expression arg0 = getArg0();
+        Expression arg1 = getArg1();                
+        
+        DTMIterator arg0DtmIterator = null;
+        
+        XObject arg0XsObject = null;
+                  
+        if (arg0 instanceof LocPathIterator) {
+            arg0DtmIterator = arg0.asIterator(xctxt, contextNode);             
  
+        }
+        else {
+            arg0XsObject = arg0.execute(xctxt, contextNode);
+        }
+        
+        ResultSequence resultSeq = new ResultSequence();
+                    
+        if (arg1 instanceof InlineFunction) {            
+            resultSeq = evaluateFnFilter(xctxt, arg0XsObject, arg0DtmIterator, 
(InlineFunction)arg1); 
+        }
+        else if (arg1 instanceof Variable) {
+            XObject arg1VarValue = arg1.execute(xctxt);
+            if (arg1VarValue instanceof InlineFunction) {
+                resultSeq = evaluateFnFilter(xctxt, arg0XsObject, 
arg0DtmIterator, (InlineFunction)arg1VarValue);   
+            }
+            else {
+                throw new javax.xml.transform.TransformerException("FORG0006 : 
The second argument to function call filter(), "
+                                                                               
                + "is not a function item.", xctxt.getSAXLocator());    
+            }
+        }
+        else {
+            throw new javax.xml.transform.TransformerException("FORG0006 : The 
second argument to function call filter(), "
+                                                                               
            + "is not a function item.", xctxt.getSAXLocator());               
+        }            
+        
+        funcEvaluationResult = resultSeq;
+        
+        return funcEvaluationResult;
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct.
+   *
+   * @param argNum The number of arguments that is being passed to the 
function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+     if (argNum != 2) {
+        reportWrongNumberArgs();
+     }
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createXPATHMessage(
+                                              XPATHErrorResources.ER_TWO, 
null)); //"2"
+  }
+  
+  /*
+   * Evaluate the function call fn:filter.
+   */
+  private ResultSequence evaluateFnFilter(XPathContext xctxt, XObject 
arg0XsObject, 
+                                               DTMIterator arg0DtmIterator, 
InlineFunction arg1) 
+                                                                               
     throws TransformerException {
+
+        ResultSequence resultSeq = new ResultSequence(); 
+        
+        List<String> funcParamNameList = arg1.getFuncParamNameList();
+        String funcBodyXPathExprStr = arg1.getFuncBodyXPathExprStr();
+        
+        if (funcBodyXPathExprStr == null || "".equals(funcBodyXPathExprStr)) {
+           return resultSeq;
+        }
+        
+        QName varQname = null;
+        
+        if (funcParamNameList.size() == 1) {
+           varQname = new QName(funcParamNameList.get(0));
+        }
+        
+        SourceLocator srcLocator = xctxt.getSAXLocator();
+        
+        XPath xpathInlineFn = new XPath(funcBodyXPathExprStr, srcLocator, 
null, XPath.SELECT, null);
+        
+        if (arg0XsObject instanceof ResultSequence) {
+           XPathContext xpathContextNew = new XPathContext(false);
+           Map<QName, XObject> inlineFunctionVarMap = 
xpathContextNew.getInlineFunctionVarMap();
+        
+           ResultSequence inpResultSeq = (ResultSequence)arg0XsObject;
+           for (int idx = 0; idx < inpResultSeq.size(); idx++) {
+               XObject inpSeqItem = inpResultSeq.item(idx);
+               if (varQname != null) {
+                  inlineFunctionVarMap.put(varQname, inpSeqItem);
+               }
+        
+               XObject resultObj = xpathInlineFn.execute(xpathContextNew, 
DTM.NULL, null);
+               if (resultObj instanceof XBoolean) {
+                   if (((XBoolean)resultObj).bool()) {
+                      resultSeq.add(inpSeqItem);
+                   }
+               }
+               else {
+                   throw new 
javax.xml.transform.TransformerException("XPTY0004 : The item type of the 
result of calling "
+                                                                               
               + "function filter() is not xs:boolean.", 
xctxt.getSAXLocator()); 
+               }
+           }
+        
+           inlineFunctionVarMap.clear();
+        }
+        else if (arg0DtmIterator != null) {                  
+           Map<QName, XObject> inlineFunctionVarMap = 
xctxt.getInlineFunctionVarMap();
+        
+           int dtmNodeHandle;
+           
+           while (DTM.NULL != (dtmNodeHandle = arg0DtmIterator.nextNode())) {
+               DTM dtm = xctxt.getDTM(dtmNodeHandle);
+               Node node = dtm.getNode(dtmNodeHandle);
+               XObject inpSeqItem = XObjectFactory.create(node, xctxt);        
       
+               if (varQname != null) {
+                  inlineFunctionVarMap.put(varQname, inpSeqItem);
+               }
+        
+               xctxt.pushCurrentNode(dtmNodeHandle);

Review Comment:
   I see there's `pushCurrentNode`, however, I do not see a corresponding 
`pop`. Does it look like a memory leak?
   I guess there should be try-catch with `popCurrentNode`.



##########
src/org/apache/xpath/functions/FuncForEach.java:
##########
@@ -0,0 +1,212 @@
+/*
+ * 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.functions;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+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.objects.InlineFunction;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XObjectFactory;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.w3c.dom.Node;
+
+/**
+ * Execute the for-each() function.
+ * 
+ * @author Mukul Gandhi <muk...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+/*
+ * fn:for-each is one of XPath 3.1's higher-order function
+ * (ref, https://www.w3.org/TR/xpath-functions-31/#higher-order-functions).
+ * 
+ * The XPath function fn:for-each has following signature, and definition,
+ * 
+ * fn:for-each($seq as item()*, $action as function(item()) as item()*) as 
item()*
+ * 
+ * The function fn:for-each applies the function item $action to every item 
from 
+ * the sequence $seq in turn, returning the concatenation of the resulting 
sequences
+ * in order.
+ */
+public class FuncForEach extends Function2Args {
+
+   private static final long serialVersionUID = 2912594883291006421L;
+   
+   private static final String FUNCTION_NAME = "for-each()"; 
+
+  /**
+   * Execute the function. The function must return a valid object.
+   * 
+   * @param xctxt The current execution context.
+   * 
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws 
javax.xml.transform.TransformerException
+  {                      
+      
+        XObject funcEvaluationResult = null;
+        
+        final int contextNode = xctxt.getCurrentNode();
+        
+        Expression arg0 = getArg0();
+        Expression arg1 = getArg1();                
+        
+        DTMIterator arg0DtmIterator = null;
+        
+        XObject arg0XsObject = null;
+                  
+        if (arg0 instanceof LocPathIterator) {
+            arg0DtmIterator = arg0.asIterator(xctxt, contextNode);             
  
+        }
+        else {
+            arg0XsObject = arg0.execute(xctxt, contextNode);
+        }
+        
+        ResultSequence resultSeq = new ResultSequence();

Review Comment:
   This initialization is redundant as the variable will be overwritten in the 
subsequent lines anyway



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@xalan.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@xalan.apache.org
For additional commands, e-mail: dev-h...@xalan.apache.org

Reply via email to