All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.expr.UserFunctionCall Maven / Gradle / Ivy

Go to download

Provides a basic XSLT 2.0 and XQuery 1.0 processor (W3C Recommendations, January 2007). Command line interfaces and implementations of several Java APIs (DOM, XPath, s9api) are also included.

The newest version!
package net.sf.saxon.expr;
import net.sf.saxon.event.SequenceReceiver;
import net.sf.saxon.evpull.EmptyEventIterator;
import net.sf.saxon.evpull.EventIterator;
import net.sf.saxon.instruct.InstructionDetails;
import net.sf.saxon.instruct.UserFunction;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.om.ValueRepresentation;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trace.InstructionInfo;
import net.sf.saxon.trace.InstructionInfoProvider;
import net.sf.saxon.trace.Location;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.AnyItemType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.TypeHierarchy;
import net.sf.saxon.value.*;


/**
 * This class represents a call to a user-defined function in the stylesheet or query.
*/

public class UserFunctionCall extends FunctionCall implements InstructionInfoProvider {

    private SequenceType staticType;
    private UserFunction function;
    private boolean tailCall = false;
        // indicates only that this is a tail call, not necessarily a recursive tail call
    private boolean confirmed = false;
        // A functionCall is confirmed if the function being called has been located. Generally in this
        // case the value of 'function' will be non-null; but in XSLT it is possible to look-ahead to confirm
        // a function binding before the relevant function is actually compiled.
    private int[] argumentEvaluationModes = null;

    /**
     * Create a function call to a user-written function in a query or stylesheet
     */

    public UserFunctionCall() {}

    /**
     * Set the static type
     * @param type the static type
     */

    public void setStaticType(SequenceType type) {
        staticType = type;
    }

    /**
     * Create the reference to the function to be called
     * @param compiledFunction the function being called
     */

    public void setFunction(UserFunction compiledFunction) {
        function = compiledFunction;
        confirmed = true;
    }

    /**
     * Check the function call against the declared function signature
     * @param compiledFunction the function being called
     * @param visitor an expression visitor
     */

    public void checkFunctionCall(UserFunction compiledFunction,
                            ExpressionVisitor visitor) throws XPathException {
        int n = compiledFunction.getNumberOfArguments();
        for (int i=0; i 1) {
                    m = ExpressionTool.MAKE_MEMO_CLOSURE;
                }
                argumentEvaluationModes[i] = m;
            }
        }
    }


    /**
     * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
     * by an expression in a source tree.
     * 

*

The default implementation of this method assumes that an expression does no navigation other than * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the * same context as the containing expression. The method must be overridden for any expression * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression, * and RootExpression (because they perform navigation), and for the doc(), document(), and collection() * functions because they create a new navigation root. Implementations also exist for PathExpression and * FilterExpression because they have subexpressions that are evaluated in a different context from the * calling expression.

* * @param pathMap the PathMap to which the expression should be added * @param pathMapNodeSet * @return the pathMapNode representing the focus established by this expression, in the case where this * expression is the first operand of a path expression or filter expression. For an expression that does * navigation, it represents the end of the arc in the path map that describes the navigation route. For other * expressions, it is the same as the input pathMapNode. */ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) { return addExternalFunctionCallToPathMap(pathMap, pathMapNodeSet); } /** * Mark tail-recursive calls on stylesheet functions. This marks the function call as tailRecursive if * if is a call to the containing function, and in this case it also returns "true" to the caller to indicate * that a tail call was found. */ public int markTailFunctionCalls(StructuredQName qName, int arity) { tailCall = true; return (getFunctionName().equals(qName) && arity == getNumberOfArguments() ? 2 : 1); } // TODO: attempt to establish whether the function is capable of creating new nodes. This // enables non-creative functions to be moved out of loops. The problem is how to achieve this // without looping in the case of recursive functions. A simple solution might be to go only // one level deep: if the body of a function is known (without analysing function calls) to be // non-creative, then all calls on that function can be marked as non-creative. Note also that // a function is creative if one of its arguments is creative and the result of the function // depends on the identity of that argument. public int getImplementationMethod() { if (Cardinality.allowsMany(getCardinality())) { return ITERATE_METHOD | PROCESS_METHOD; } else { return EVALUATE_METHOD; } } /** * Call the function, returning the value as an item. This method will be used * only when the cardinality is zero or one. If the function is tail recursive, * it returns an Object representing the arguments to the next (recursive) call */ public Item evaluateItem(XPathContext c) throws XPathException { ValueRepresentation val = callFunction(c); return Value.asItem(val); } /** * Call the function, returning an iterator over the results. (But if the function is * tail recursive, it returns an iterator over the arguments of the recursive call) */ public SequenceIterator iterate(XPathContext c) throws XPathException { ValueRepresentation result = callFunction(c); return Value.getIterator(result); } /** * This is the method that actually does the function call * @param c the dynamic context * @return the result of the function * @throws XPathException if dynamic errors occur */ private ValueRepresentation callFunction(XPathContext c) throws XPathException { ValueRepresentation[] actualArgs = evaluateArguments(c); if (tailCall) { ((XPathContextMajor)c).requestTailCall(function, actualArgs); return EmptySequence.getInstance(); } XPathContextMajor c2 = c.newCleanContext(); c2.setOrigin(this); c2.setTemporaryOutputState(true); try { return function.call(actualArgs, c2); } catch (StackOverflowError err) { throw new XPathException("Too many nested function calls. May be due to infinite recursion.", this); } catch (NullPointerException err) { if (function == null) { throw new NullPointerException("Unbound function call " + function.getFunctionName().getDisplayName()); } else { throw err; } } } /** * Process the function call in push mode * @param context the XPath dynamic context * @throws XPathException */ public void process(XPathContext context) throws XPathException { ValueRepresentation[] actualArgs = evaluateArguments(context); if (tailCall) { ((XPathContextMajor)context).requestTailCall(function, actualArgs); } else { SequenceReceiver out = context.getReceiver(); XPathContextMajor c2 = context.newCleanContext(); c2.setReceiver(out); c2.setOrigin(this); function.process(actualArgs, c2); } } /** * Process the function call in pull mode * @param context the XPath dynamic context * @throws XPathException */ public EventIterator iterateEvents(XPathContext context) throws XPathException { ValueRepresentation[] actualArgs = evaluateArguments(context); if (tailCall) { ((XPathContextMajor)context).requestTailCall(function, actualArgs); return EmptyEventIterator.getInstance(); } else { SequenceReceiver out = context.getReceiver(); XPathContextMajor c2 = context.newCleanContext(); c2.setReceiver(out); c2.setOrigin(this); return function.iterateEvents(actualArgs, c2); } } private ValueRepresentation[] evaluateArguments(XPathContext c) throws XPathException { int numArgs = argument.length; ValueRepresentation[] actualArgs = new ValueRepresentation[numArgs]; if (argumentEvaluationModes == null) { // should have been done at compile time computeArgumentEvaluationModes(); } for (int i=0; i 1 && actualArgs[i] instanceof Closure && !(actualArgs[i] instanceof MemoClosure)) { actualArgs[i] = ((Closure)actualArgs[i]).reduce(); } } return actualArgs; } /** * Call the function dynamically. For this to be possible, the static arguments of the function call * must have been set up as SuppliedParameterReference objects. The actual arguments are placed on the * callee's stack, and the type conversion takes place "in situ". * @param suppliedArguments the values to be used for the arguments of the function * @param context the dynamic evaluation context * @return the result of evaluating the function */ public ValueRepresentation dynamicCall(ValueRepresentation[] suppliedArguments, XPathContext context) throws XPathException { ValueRepresentation[] convertedArgs = new ValueRepresentation[suppliedArguments.length]; XPathContextMajor c2 = context.newCleanContext(); c2.setOrigin(this); c2.setCaller(context); c2.openStackFrame(suppliedArguments.length); for (int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy