net.sf.saxon.expr.FunctionCall Maven / Gradle / Ivy
Show all versions of saxon9 Show documentation
package net.sf.saxon.expr;
import net.sf.saxon.om.FastStringBuffer;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.NoDynamicContextException;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.value.SequenceExtent;
import net.sf.saxon.value.Value;
import java.util.Arrays;
import java.util.Iterator;
/**
* Abstract superclass for calls to system-defined and user-defined functions
*/
public abstract class FunctionCall extends Expression {
/**
* The name of the function
*/
private StructuredQName name;
/**
* The array of expressions representing the actual parameters
* to the function call
*/
protected Expression[] argument;
/**
* Set the name of the function being called
* @param name the name of the function
*/
public final void setFunctionName(StructuredQName name) {
this.name = name;
}
/**
* Get the qualified of the function being called
* @return the qualified name
*/
public StructuredQName getFunctionName() {
return name;
}
/**
* Determine the number of actual arguments supplied in the function call
* @return the arity (the number of arguments)
*/
public final int getNumberOfArguments() {
return argument.length;
}
/**
* Method called by the expression parser when all arguments have been supplied
* @param args the expressions contained in the argument list of the function call
*/
public void setArguments(Expression[] args) {
argument = args;
for (int a=0; a
* This method is called after all references to functions and variables have been resolved
* to the declaration of the function or variable, and after all type checking has been done.
*
* @param visitor an expression visitor
* @param contextItemType the static type of "." at the point where this expression is invoked.
* The parameter is set to null if it is known statically that the context item will be undefined.
* If the type of the context item is not known statically, the argument is set to
* {@link net.sf.saxon.type.Type#ITEM_TYPE}
* @return the original expression, rewritten if appropriate to optimize execution
* @throws XPathException if an error is discovered during this phase
* (typically a type error)
*/
public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
boolean fixed = true;
for (int i=0; i
* A convenience routine for use in subclasses.
* @param min the minimum number of arguments allowed
* @param max the maximum number of arguments allowed
* @param visitor an expression visitor
* @return the actual number of arguments
* @throws net.sf.saxon.trans.XPathException if the number of arguments is out of range
*/
protected int checkArgumentCount(int min, int max, ExpressionVisitor visitor) throws XPathException {
int numArgs = argument.length;
if (min==max && numArgs != min) {
throw new XPathException("Function " + getDisplayName() + " must have "
+ min + pluralArguments(min),
this);
}
if (numArgs < min) {
throw new XPathException("Function " + getDisplayName() + " must have at least "
+ min + pluralArguments(min),
this);
}
if (numArgs > max) {
throw new XPathException("Function " + getDisplayName() + " must have no more than "
+ max + pluralArguments(max),
this);
}
return numArgs;
}
/**
* Utility routine used in constructing error messages: get the word "argument" or "arguments"
* @param num the number of arguments
* @return the singular or plural word
*/
private static String pluralArguments(int num) {
if (num==1) return " argument";
return " arguments";
}
/**
* Get the immediate subexpressions of this expression
*/
public Iterator iterateSubExpressions() {
try {
return Arrays.asList(argument).iterator();
} catch (NullPointerException err) {
// typically caused by doing CopyLocationInfo after creating the function
// but before creating its arguments
throw err;
}
}
/**
* Replace one subexpression by a replacement subexpression
* @param original the original subexpression
* @param replacement the replacement subexpression
* @return true if the original subexpression is found
*/
public boolean replaceSubExpression(Expression original, Expression replacement) {
boolean found = false;
for (int i=0; i
* 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 pathMapNodes the node in the PathMap representing the focus at the point where this expression
* is called. Set to null if this expression appears at the top level, in which case the expression, if it
* is registered in the path map at all, must create a new path map root.
* @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 addExternalFunctionCallToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodes) {
// Except in the case of system functions, we have no idea where a function call might
// navigate, so we assume the worst, and register that the path has unknown dependencies
PathMap.PathMapNodeSet result = new PathMap.PathMapNodeSet();
for (Iterator iter = iterateSubExpressions(); iter.hasNext(); ) {
Expression child = (Expression)iter.next();
result.addNodeSet(child.addToPathMap(pathMap, pathMapNodes));
}
result.setHasUnknownDependencies();
return result;
// AxisExpression axis = new AxisExpression(Axis.ANCESTOR_OR_SELF, AnyNodeTest.getInstance());
// axis.setContainer(getContainer());
// PathMap.PathMapNodeSet target = axis.addToPathMap(pathMap, pathMapNodes);
// axis = new AxisExpression(Axis.ANCESTOR_OR_SELF, AnyNodeTest.getInstance());
// axis.setContainer(getContainer());
// return axis.addToPathMap(pathMap, target);
}
/**
* Get the name of the function for display in messages
* @return the name of the function as a lexical QName
*/
public final String getDisplayName() {
return getFunctionName().getDisplayName();
}
/**
* The toString() method for an expression attempts to give a representation of the expression
* in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
* In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
*/
public String toString() {
FastStringBuffer buff = new FastStringBuffer(120);
buff.append(getDisplayName());
Iterator iter = iterateSubExpressions();
boolean first = true;
while (iter.hasNext()) {
buff.append(first ? "(" : ", ");
buff.append(iter.next().toString());
first = false;
}
buff.append(")");
return buff.toString();
}
/**
* Diagnostic print of expression structure. The abstract expression tree
* is written to the supplied output destination.
*/
public void explain(ExpressionPresenter out) {
out.startElement("functionCall");
out.emitAttribute("name", getDisplayName());
for (int a=0; a