/** * Check number of arguments. <BR> * A convenience routine for use in subclasses. * * @param min the minimum number of arguments allowed * @param max the maximum number of arguments allowed * @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) throws XPathException { int numArgs = getArity(); String msg = null; if (min == max && numArgs != min) { msg = "Function " + getDisplayName() + " must have " + pluralArguments(min); } else if (numArgs < min) { msg = "Function " + getDisplayName() + " must have at least " + pluralArguments(min); } else if (numArgs > max) { msg = "Function " + getDisplayName() + " must have no more than " + pluralArguments(max); } if (msg != null) { XPathException err = new XPathException(msg, "XPST0017"); err.setIsStaticError(true); err.setLocation(getLocation()); throw err; } return numArgs; }
/** * Determine whether two expressions are equivalent */ public boolean equals(Object o) { if (!(o instanceof FunctionCall)) { return false; } FunctionCall f = (FunctionCall)o; if (!getFunctionName().equals(f.getFunctionName())) { return false; } if (getNumberOfArguments() != f.getNumberOfArguments()) { return false; } for (int i=0; i<getNumberOfArguments(); i++) { if (!argument[i].equals(f.argument[i])) { return false; } } return true; }
/** * Type-check the expression. This also calls preEvaluate() to evaluate the function * if all the arguments are constant; functions that do not require this behavior * can override the preEvaluate method. */ public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { boolean fixed = true; for (int i=0; i<argument.length; i++) { Expression exp = visitor.typeCheck(argument[i], contextItemType); if (exp != argument[i]) { adoptChildExpression(exp); argument[i] = exp; } if (!(argument[i] instanceof Literal)) { fixed = false; } } checkArguments(visitor); if (fixed) { try { return preEvaluate(visitor); } catch (NoDynamicContextException err) { // Early evaluation failed, typically because the implicit timezone is not yet known. // Try again later at run-time. return this; } } else { return this; } }
/** * Type-check the expression. This also calls preEvaluate() to evaluate the function * if all the arguments are constant; functions that do not require this behavior * can override the preEvaluate method. */ /*@NotNull*/ public Expression typeCheck(ExpressionVisitor visitor, ContextItemStaticInfo contextInfo) throws XPathException { typeCheckChildren(visitor, contextInfo); checkArguments(visitor); return preEvaluateIfConstant(visitor); }
/** * Set the expression to be used as the Nth argument * @param n the required argument, zero-based * @param child the expression to be used in the relevant position * @throws java.lang.IllegalArgumentException if the value of n is out of range */ public void setArg(int n, Expression child) { getOperanda().setOperand(n, child); adoptChildExpression(child); } /**
/** * Simplify the arguments of the function. * Called from the simplify() method of each function. * * @param env the static context * @return the result of simplifying the arguments of the expression * @throws net.sf.saxon.trans.XPathException if an error occurs */ protected final Expression simplifyArguments(StaticContext env) throws XPathException { for (int i = 0; i < getArguments().length; i++) { Expression exp = getArg(i).simplify(); if (exp != getArg(i)) { adoptChildExpression(exp); setArg(i, exp); } } return this; }
/** * Get the qualified of the function being called * @return the qualified name */ public final StructuredQName getFunctionName() { StructuredQName n = super.getFunctionName(); if (n == null) { return function.getFunctionName(); } else { return n; } }
/** * Determine whether two expressions are equivalent */ public boolean equals(Object o) { if (!(o instanceof FunctionCall)) { return false; } if (getFunctionName() == null) { return this == o; } FunctionCall f = (FunctionCall) o; if (!getFunctionName().equals(f.getFunctionName())) { return false; } if (getArity() != f.getArity()) { return false; } for (int i = 0; i < getArity(); i++) { if (!getArg(i).isEqual(f.getArg(i))) { return false; } } return true; }
/** * Check number of arguments. <BR> * 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; }
public AbstractExpression exprFor (FunctionCall funcall) { if (funcall.getFunctionName().equals(itemAtQName)) { return new Subsequence(exprFor (funcall.getArguments()[0]), exprFor(funcall.getArguments()[1]), LiteralExpression.ONE); Expression arg = funcall.getArguments()[0]; if ((arg.getSpecialProperties() & StaticProperty.REVERSE_DOCUMENT_ORDER) != 0 || (! Cardinality.allowsMany(arg.getCardinality()))) { if (funcall.getNumberOfArguments() == 2) { return new Subsequence (exprFor(funcall.getArguments()[0]), exprFor(funcall.getArguments()[1])); } else { if (funcall.getNumberOfArguments() != 3) { throw new LuxException ("call to subsequence has " + funcall.getNumberOfArguments() + " arguments?"); return new Subsequence (exprFor(funcall.getArguments()[0]), exprFor(funcall.getArguments()[1]), exprFor(funcall.getArguments()[2])); Expression[] args = funcall.getArguments(); AbstractExpression[] aargs = new AbstractExpression[args.length]; for (int i = 0; i < args.length; i++) { aargs[i] = exprFor (args[i]); Entry entry = StandardFunction.getFunction(funcall.getFunctionName().getDisplayName(), aargs.length); ValueType returnType = entry != null ? valueTypeForItemType (entry.itemType) : ValueType.VALUE; QName fnQName = qnameFor (funcall.getFunctionName()); if (functionEqualsBuiltin(funcall, "root") || fnQName.equals(FunCall.LUX_SEARCH)) return new FunCall (qnameFor (funcall.getFunctionName()), returnType, aargs);
/** * Diagnostic print of expression structure. The abstract expression tree * is written to the supplied output destination. */ public void export(ExpressionPresenter out) throws XPathException { out.startElement("functionCall", this); if (getFunctionName() == null) { throw new AssertionError("Exporting call to anonymous function"); } else { out.emitAttribute("name", getFunctionName().getDisplayName()); } for (Operand o : operands()) { o.getChildExpression().export(out); } out.endElement(); }
/** * Type-check the expression. This also calls preEvaluate() to evaluate the function * if all the arguments are constant; functions that do not require this behavior * can override the preEvaluate method. * * @param visitor the expression visitor * @param contextInfo information about the type of the context item */ @Override public Expression typeCheck(ExpressionVisitor visitor, ContextItemStaticInfo contextInfo) throws XPathException { checkFunctionCall(target, visitor); return super.typeCheck(visitor, contextInfo); }
/** * 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(); }
public Expression optimize(ExpressionVisitor visitor, ContextItemStaticInfo contextItemType) throws XPathException { //function.setOnFirstCall(new DeferredAction(visitor, contextItemType)); Expression e = super.optimize(visitor, contextItemType); if (e == this && function != null) { Expression e2 = visitor.obtainOptimizer().tryInlineFunctionCall( this, visitor, contextItemType); if (e2 != this) { return e2.optimize(visitor, contextItemType); } return e2; } return e; }
/** * Check the function call against the declared function signature, applying the * function conversion rules to each argument as necessary * * @param target the function being called * @param visitor an expression visitor * @throws XPathException if there is a type error */ public void checkFunctionCall(Function target, ExpressionVisitor visitor) throws XPathException { TypeChecker tc = visitor.getConfiguration().getTypeChecker(visitor.getStaticContext().isInBackwardsCompatibleMode()); SequenceType[] argTypes = target.getFunctionItemType().getArgumentTypes(); int n = target.getArity(); for (int i = 0; i < n; i++) { String name = getFunctionName() == null ? "" : getFunctionName().getDisplayName(); RoleDiagnostic role = new RoleDiagnostic(RoleDiagnostic.FUNCTION, name, i); setArg(i, tc.staticTypeCheck( getArg(i), argTypes[i], role, visitor)); } }
/** * Optimize an expression whose effective boolean value is required * @param exp the expression whose EBV is to be evaluated * @param visitor an expression visitor * @param contextItemType the type of the context item for this expression * @return an expression that returns the EBV of exp, or null if no optimization was possible * @throws XPathException if static errors are found */ public static Expression rewriteEffectiveBooleanValue( Expression exp, ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy(); if (exp instanceof ValueComparison) { ValueComparison vc = (ValueComparison)exp; if (vc.getResultWhenEmpty() == null) { vc.setResultWhenEmpty(BooleanValue.FALSE); } return exp; } else if (th.isSubType(exp.getItemType(th), BuiltInAtomicType.BOOLEAN) && exp.getCardinality() == StaticProperty.EXACTLY_ONE) { return exp; } else if (exp.getItemType(th) instanceof NodeTest) { // rewrite boolean(x) => exists(x) FunctionCall exists = SystemFunction.makeSystemFunction("exists", new Expression[]{exp}); exists.setLocationId(exp.getLocationId()); return exists.optimize(visitor, contextItemType); } else { return null; } }
/** * Perform optimisation of an expression and its subexpressions. * <p>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.</p> * * @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) */ /*@NotNull*/ public Expression optimize(ExpressionVisitor visitor, ContextItemStaticInfo contextItemType) throws XPathException { optimizeChildren(visitor, contextItemType); boolean fixed = true; for (Operand o : operands()) { if (!(o.getChildExpression() instanceof Literal)) { fixed = false; break; } } return fixed ? preEvaluate(visitor) : this; }
/** * Pre-evaluate a function at compile time. Functions that do not allow * pre-evaluation, or that need access to context information, can override this method. * * @param visitor an expression visitor * @return the result of the early evaluation, or the original expression, or potentially * a simplified expression * @throws net.sf.saxon.trans.XPathException if evaluation fails */ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException { if ((getIntrinsicDependencies() & ~StaticProperty.DEPENDS_ON_STATIC_CONTEXT) != 0) { return this; } try { Literal lit = Literal.makeLiteral(iterate(visitor.getStaticContext().makeEarlyEvaluationContext()).materialize(), this); Optimizer.trace(visitor.getConfiguration(), "Pre-evaluated function call " + toShortString(), lit); return lit; } catch (NoDynamicContextException e) { // early evaluation failed, usually because implicit timezone required return this; } catch (UnsupportedOperationException e) { //e.printStackTrace(); if (e.getCause() instanceof NoDynamicContextException) { return this; } else { throw e; } } }
/** * 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<args.length; a++) { adoptChildExpression(args[a]); } }
/** * 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<argument.length; a++) { argument[a].explain(out); } out.endElement(); }