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

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

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.expr;

import net.sf.saxon.Configuration;
import net.sf.saxon.expr.oper.OperandArray;
import net.sf.saxon.expr.parser.*;
import net.sf.saxon.functions.*;
import net.sf.saxon.functions.Error;
import net.sf.saxon.om.LazySequence;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.SequenceTool;
import net.sf.saxon.pattern.NodeSetPattern;
import net.sf.saxon.pattern.Pattern;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.BuiltInAtomicType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.TypeHierarchy;
import net.sf.saxon.value.IntegerValue;

import java.util.Map;

/**
 * A call to a system-defined function (specifically, a function implemented as an instance
 * of {@link net.sf.saxon.functions.SystemFunction})
 */
public class SystemFunctionCall extends StaticFunctionCall implements Negatable {

    public SystemFunctionCall(SystemFunction target, Expression[] arguments) {
        super(target, arguments);
    }

    /**
     * Set the retained static context
     *
     * @param rsc the static context to be retained
     */
    @Override
    public void setRetainedStaticContext(RetainedStaticContext rsc) {
        super.setRetainedStaticContext(rsc);
        getTargetFunction().setRetainedStaticContext(rsc);
    }

    /**
     * Pre-evaluate a function at compile time. Functions that do not allow
     * pre-evaluation, or that need access to context information, can prevent early
     * evaluation by setting the LATE bit in the function properties.
     *
     * @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
     */
    @Override
    public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
        SystemFunction target = getTargetFunction();
        if ((target.getDetails().properties & StandardFunction.LATE) == 0) {
            return super.preEvaluate(visitor);
        } else {
            // Early evaluation of this function is suppressed
            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.
     *
     * @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 {
        typeCheckChildren(visitor, contextInfo);
        checkFunctionCall(getTargetFunction(), visitor);
        // Give the function an opportunity to use the type information now available
        getTargetFunction().supplyTypeInformation(visitor, contextInfo, getArguments());
        if ((getTargetFunction().getDetails().properties & StandardFunction.LATE) == 0) {
            return preEvaluateIfConstant(visitor);
        }
        return this;
    }

    @Override
    public SystemFunction getTargetFunction() {
        return (SystemFunction)super.getTargetFunction();
    }

    /**
     * Compute the dependencies of an expression, as the union of the
     * dependencies of its subexpressions. (This is overridden for path expressions
     * and filter expressions, where the dependencies of a subexpression are not all
     * propogated). This method should be called only once, to compute the dependencies;
     * after that, getDependencies should be used.
     *
     * @return the depencies, as a bit-mask
     */
    @Override
    public int getIntrinsicDependencies() {
        int properties = getTargetFunction().getDetails().properties;
        if ((properties & StandardFunction.FOCUS) != 0) {
            int dep = 0;
            if ((properties & StandardFunction.CITEM) != 0) {
                dep |= StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
            }
            if ((properties & StandardFunction.POSN) != 0) {
                dep |= StaticProperty.DEPENDS_ON_POSITION;
            }
            if ((properties & StandardFunction.LAST) != 0) {
                dep |= StaticProperty.DEPENDS_ON_LAST;
            }
            return dep;
        } else if (isCallOn(RegexGroup.class)) {
            return StaticProperty.DEPENDS_ON_CURRENT_GROUP;
        } else if (isCallOnSystemFunction("current-merge-group")) {
            return StaticProperty.DEPENDS_ON_CURRENT_GROUP;
        } else if (isCallOnSystemFunction("current-merge-key")) {
            return StaticProperty.DEPENDS_ON_CURRENT_GROUP;
        } else {
            return 0;
        }
    }

    /**
     * Compute the static cardinality of this expression
     *
     * @return the computed cardinality, as one of the values {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_ONE},
     * {@link net.sf.saxon.expr.StaticProperty#EXACTLY_ONE}, {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ONE_OR_MORE},
     * {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_MORE}
     */
    @Override
    protected int computeCardinality() {
        return getTargetFunction().getCardinality(getArguments());
    }

    /**
     * Compute the special properties of this expression. These properties are denoted by a bit-significant
     * integer, possible values are in class {@link net.sf.saxon.expr.StaticProperty}. The "special" properties are properties
     * other than cardinality and dependencies, and most of them relate to properties of node sequences, for
     * example whether the nodes are in document order.
     *
     * @return the special properties, as a bit-significant integer
     */
    @Override
    protected int computeSpecialProperties() {
        return getTargetFunction().getSpecialProperties(getArguments());
    }

    public Expression optimize(ExpressionVisitor visitor, ContextItemStaticInfo contextInfo) throws XPathException {
        Expression sf = super.optimize(visitor, contextInfo);
        if (sf == this) {
            // Give the function an opportunity to regenerate the function call, with more information about
            // the types of the arguments than was previously available
            Expression sfo = getTargetFunction().makeOptimizedFunctionCall(visitor, contextInfo, getArguments());
            if (sfo != null) {
                sfo.setParentExpression(getParentExpression());
                ExpressionTool.copyLocationInfo(this, sfo);
                return sfo;
            }
        }
        if (sf instanceof SystemFunctionCall) {
            // If any arguments are known to be empty, pre-evaluate the result
            StandardFunction.Entry details = ((SystemFunctionCall) sf).getTargetFunction().getDetails();
            if ((details.properties & StandardFunction.UO) != 0) {
                // First argument does not need to be in any particular order
                setArg(0, getArg(0).unordered(true, visitor.isOptimizeForStreaming()));
            }
            if (getArity() <= details.resultIfEmpty.length) {
                // the condition eliminates concat, which is a special case.
                for (int i = 0; i < getArity(); i++) {
                    if (Literal.isEmptySequence(getArg(i)) && details.resultIfEmpty[i] != null) {
                        return Literal.makeLiteral(SequenceTool.toGroundedValue(details.resultIfEmpty[i]));
                    }
                }
            }
        }
        return sf;
    }

    @Override
    public boolean isVacuousExpression() {
        return isCallOn(Error.class);
    }

    /**
     * Determine the data type of the expression, if possible. All expression return
     * sequences, in general; this method determines the type of the items within the
     * sequence, assuming that (a) this is known in advance, and (b) it is the same for
     * all items in the sequence.
     * 

*

This method should always return a result, though it may be the best approximation * that is available at the time.

* * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER, * Type.NODE, or Type.ITEM (meaning not known at compile time) */ @Override public ItemType getItemType() { return getTargetFunction().getResultItemType(getArguments()); } /** * Copy an expression. This makes a deep copy. * * @return the copy of the original expression * @param rebindings */ @Override public Expression copy(RebindingMap rebindings) { Expression[] args = new Expression[getArity()]; for (int i = 0; i < args.length; i++) { args[i] = getArg(i).copy(rebindings); } return new SystemFunctionCall(getTargetFunction(), args); } /** * For an expression that returns an integer or a sequence of integers, get * a lower and upper bound on the values of the integers that may be returned, from * static analysis. The default implementation returns null, meaning "unknown" or * "not applicable". Other implementations return an array of two IntegerValue objects, * representing the lower and upper bounds respectively. The values * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH * are used to indicate values limited by the size of a string or the size of a sequence. * * @return the lower and upper bounds of integer values in the result, or null to indicate * unknown or not applicable. */ //@Override public IntegerValue[] getIntegerBounds() { SystemFunction fn = getTargetFunction(); if ((fn.getDetails().properties & StandardFunction.FILTER) != 0) { return getArg(0).getIntegerBounds(); } return fn.getIntegerBounds(); } /** * Check whether this specific instance of the expression is negatable * * @param th the TypeHierarchy (in case it's needed) * @return true if it is */ public boolean isNegatable(TypeHierarchy th) { return isCallOn(NotFn.class) || isCallOn(BooleanFn.class) || isCallOn(Empty.class) || isCallOn(Exists.class); } /** * Create an expression that returns the negation of this expression * * @return the negated expression * @throws UnsupportedOperationException if isNegatable() returns false */ public Expression negate() { SystemFunction fn = getTargetFunction(); if (fn instanceof NotFn) { Expression arg = getArg(0); if (arg.getItemType() == BuiltInAtomicType.BOOLEAN && arg.getCardinality() == StaticProperty.EXACTLY_ONE) { return arg; } else { return SystemFunction.makeCall("boolean", getRetainedStaticContext(), arg); } } else if (fn instanceof BooleanFn) { return SystemFunction.makeCall("not", getRetainedStaticContext(), getArg(0)); } else if (fn instanceof Exists) { return SystemFunction.makeCall("empty", getRetainedStaticContext(), getArg(0)); } else if (fn instanceof Empty) { return SystemFunction.makeCall("exists", getRetainedStaticContext(), getArg(0)); } throw new UnsupportedOperationException(); } /** * Replace this expression by a simpler expression that delivers the results without regard * to order. * * @param retainAllNodes set to true if the result must contain exactly the same nodes as the * original; set to false if the result can eliminate (or introduce) duplicates. * @param forStreaming set to true if the result is to be optimized for streaming * @return an expression that delivers the same nodes in a more convenient order * @throws net.sf.saxon.trans.XPathException if the rewrite fails */ @Override public Expression unordered(boolean retainAllNodes, boolean forStreaming) throws XPathException { SystemFunction fn = getTargetFunction(); if (fn instanceof Reverse) { return getArg(0); } if (fn instanceof TreatFn) { setArg(0, getArg(0).unordered(retainAllNodes, forStreaming)); } return this; } /** * 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 the PathMapNodeSet to which the paths embodied in this expression should be added * @return the pathMapNodeSet representing the points in the source document that are both reachable by this * expression, and that represent possible results of this 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. */ @Override public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) { if (isCallOn(Doc.class) || isCallOn(DocumentFn.class) || isCallOn(CollectionFn.class)) { getArg(0).addToPathMap(pathMap, pathMapNodeSet); return new PathMap.PathMapNodeSet(pathMap.makeNewRoot(this)); } else { return super.addToPathMap(pathMap, pathMapNodeSet); } } /** * Convert this expression to an equivalent XSLT pattern * * @param config the Saxon configuration * @param is30 true if this is XSLT 3.0 * @return the equivalent pattern * @throws net.sf.saxon.trans.XPathException if conversion is not possible */ @Override public Pattern toPattern(Configuration config, boolean is30) throws XPathException { SystemFunction fn = getTargetFunction(); if (fn instanceof Root_1) { if (is30 && (getArg(0) instanceof ContextItemExpression || (getArg(0) instanceof ItemChecker && ((ItemChecker) getArg(0)).getBaseExpression() instanceof ContextItemExpression))) { return new NodeSetPattern(this); } } return super.toPattern(config, is30); } public Sequence[] evaluateArguments(XPathContext context) throws XPathException { OperandArray operanda = getOperanda(); int numArgs = operanda.getNumberOfOperands(); Sequence[] actualArgs = new Sequence[numArgs]; for (int i = 0; i < numArgs; i++) { actualArgs[i] = new LazySequence(operanda.getOperandExpression(i).iterate(context)); } return actualArgs; } /** * Diagnostic print of expression structure. The abstract expression tree * is written to the supplied output destination. * * @param out */ @Override public void export(ExpressionPresenter out) throws XPathException { out.startElement("fn", this); out.emitAttribute("name", getFunctionName().getDisplayName()); getTargetFunction().exportAttributes(out); for (Operand o : operands()) { o.getChildExpression().export(out); } out.endElement(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy