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

net.sf.saxon.functions.IntegratedFunctionCall Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2023 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.functions;

import net.sf.saxon.Configuration;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.parser.*;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.*;
import net.sf.saxon.pattern.AnyNodeTest;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.SaxonErrorCode;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.Affinity;
import net.sf.saxon.type.AnyItemType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.TypeHierarchy;
import net.sf.saxon.value.Cardinality;
import net.sf.saxon.value.SequenceType;

import java.util.function.Supplier;

/**
 * Expression representing a call to a user-written extension
 * function implemented as a subtype of {@link ExtensionFunctionCall}
 */
public class IntegratedFunctionCall extends FunctionCall implements Callable {

    private final StructuredQName name;
    private final ExtensionFunctionCall function;
    private SequenceType resultType = SequenceType.ANY_SEQUENCE;
    private int state = 0;

    public IntegratedFunctionCall(StructuredQName name, ExtensionFunctionCall function) {
        this.name = name;
        this.function = function;
    }

    /**
     * Set the result type of the function
     * @param resultType the result type
     */

    public void setResultType(SequenceType resultType) {
        this.resultType = resultType;
    }

    /**
     * Get the qualified of the function being called
     *
     * @return the qualified name
     */
    @Override
    public StructuredQName getFunctionName() {
        return name;
    }

    /**
     * Get the target function to be called
     *
     * @param context the dynamic evaluation context
     * @return the target function, or null if unknown
     */
    @Override
    public FunctionItem getTargetFunction(XPathContext context) {
        return null;
    }

    /**
     * Get the ExtensionFunctionCall object supplied by the application
     *
     * @return the ExtensionFunctionCall object
     */

    public ExtensionFunctionCall getFunction() {
        return function;
    }


    /**
     * Method supplied by each class of function to check arguments during parsing, when all
     * the argument expressions have been read. This implementation of the method checks the arguments
     * of the supplied function call against the argument types declared as part of the extension function
     * definition, and generates code to do the conversion if necessary.
     *
     * @param visitor the expression visitor
     * @throws net.sf.saxon.trans.XPathException
     *          if the arguments are statically determined to be incompatible
     *          with the declared argument types of the function.
     */

    @Override
    protected void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
        ExtensionFunctionDefinition definition = function.getDefinition();
        checkArgumentCount(definition.getMinimumNumberOfArguments(), definition.getMaximumNumberOfArguments());
        final int args = getArity();
        SequenceType[] declaredArgumentTypes = definition.getArgumentTypes();
        if (declaredArgumentTypes == null || (args != 0 && declaredArgumentTypes.length == 0)) {
            throw new XPathException("Integrated function " + getDisplayName() +
                    " failed to declare its argument types");
        }
        SequenceType[] actualArgumentTypes = new SequenceType[args];
        TypeChecker tc = visitor.getConfiguration().getTypeChecker(false);
        for (int i = 0; i < args; i++) {
            final int pos = i;
            Supplier role =
                    () -> new RoleDiagnostic(RoleDiagnostic.FUNCTION, getFunctionName().getDisplayName(), pos);
            setArg(i, tc.staticTypeCheck(
                    getArg(i),
                    i < declaredArgumentTypes.length ?
                            declaredArgumentTypes[i] :
                            declaredArgumentTypes[declaredArgumentTypes.length - 1],
                    role,
                    visitor));

            actualArgumentTypes[i] = SequenceType.makeSequenceType(
                    getArg(i).getItemType(),
                    getArg(i).getCardinality());
        }
        resultType = definition.getResultType(actualArgumentTypes);
        if (state == 0) {
            function.supplyStaticContext(visitor.getStaticContext(), 0, getArguments());
        }
        state++;
    }

    /**
     * Type-check the expression.
     */

    /*@NotNull*/
    @Override
    public Expression typeCheck(ExpressionVisitor visitor, ContextItemStaticInfo contextInfo) throws XPathException {
        Expression exp = super.typeCheck(visitor, contextInfo);
        if (exp instanceof IntegratedFunctionCall) {
            Expression exp2 = ((IntegratedFunctionCall) exp).function.rewrite(visitor.getStaticContext(), getArguments());
            if (exp2 == null) {
                return exp;
            } else {
                ExpressionTool.copyLocationInfo(this, exp2);
                return exp2.simplify().typeCheck(visitor, contextInfo).optimize(visitor, contextInfo);
            }
        }
        return exp;
    }

    /**
     * Pre-evaluate a function at compile time. This function does not allow
     * pre-evaluation, so the method returns null.
     *
     * @param visitor an expression visitor
     * @return for this class: always null
     */

    @Override
    public Expression preEvaluate(ExpressionVisitor visitor) {
        return this;
    }

    /**
     * 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) */ /*@NotNull*/ @Override public ItemType getItemType() { return resultType.getPrimaryType(); } /** * 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 resultType.getCardinality(); } /** * Determine the intrinsic dependencies of an expression, that is, those which are not derived * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency * on the context position, while (position()+1) does not. The default implementation * of the method returns 0, indicating "no dependencies". * * @return a set of bit-significant flags identifying the "intrinsic" * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty */ @Override public int getIntrinsicDependencies() { ExtensionFunctionDefinition definition = function.getDefinition(); return definition.dependsOnFocus() ? StaticProperty.DEPENDS_ON_FOCUS : 0; } /** * 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() { ExtensionFunctionDefinition definition = function.getDefinition(); return definition.hasSideEffects() ? StaticProperty.HAS_SIDE_EFFECTS : StaticProperty.NO_NODES_NEWLY_CREATED; } /** * Copy an expression. This makes a deep copy. * * @return the copy of the original expression * @param rebindings Variables that need to be re-bound */ /*@NotNull*/ @Override public Expression copy(RebindingMap rebindings) { IntegratedFunctionCall copy = new IntegratedFunctionCall(getFunctionName(), function); Expression[] args = new Expression[getArity()]; for (int i = 0; i < args.length; i++) { args[i] = getArg(i).copy(rebindings); } copy.setArguments(args); copy.resultType = resultType; copy.state = state; ExpressionTool.copyLocationInfo(this, copy); return copy; } /** * Diagnostic print of expression structure. The abstract expression tree * is written to the supplied output destination. */ @Override public void export(ExpressionPresenter out) throws XPathException { out.startElement("ifCall", this); out.emitAttribute("name", getFunctionName()); out.emitAttribute("type", resultType.toAlphaCode()); for (Operand o : operands()) { o.getChildExpression().export(out); } out.endElement(); } /** * Return an Iterator to iterate over the values of a sequence. The value of every * expression can be regarded as a sequence, so this method is supported for all * expressions. This default implementation handles iteration for expressions that * return singleton values: for non-singleton expressions, the subclass must * provide its own implementation. * * @param context supplies the context for evaluation * @return a SequenceIterator that can be used to iterate over the result * of the expression * @throws net.sf.saxon.trans.XPathException * if any dynamic error occurs evaluating the * expression */ /*@NotNull*/ @Override public SequenceIterator iterate(final XPathContext context) throws XPathException { ExtensionFunctionDefinition definition = function.getDefinition(); Sequence[] argValues = new Sequence[getArity()]; for (int i = 0; i < argValues.length; i++) { argValues[i] = SequenceTool.toLazySequence(getArg(i).iterate(context)); } final Supplier role = () -> new RoleDiagnostic(RoleDiagnostic.FUNCTION_RESULT, getFunctionName().getDisplayName(), 0); final Configuration config = context.getConfiguration(); final TypeHierarchy th = config.getTypeHierarchy(); SequenceIterator result; try { result = function.call(context, argValues).iterate(); } catch (XPathException e) { e.maybeSetLocation(getLocation()); throw e; } if (!definition.trustResultType()) { int card = resultType.getCardinality(); if (card != StaticProperty.ALLOWS_ZERO_OR_MORE) { result = new CardinalityCheckingIterator(result, card, role, getLocation()); } final ItemType type = resultType.getPrimaryType(); if (type != AnyItemType.getInstance()) { result = new ItemMappingIterator(result, ItemMapper.of(item -> { if (!type.matches(item, th)) { String msg = role.get().composeErrorMessage(type, item, th); XPathException err = new XPathException(msg, "XPTY0004"); err.setLocation(getLocation()); throw err; } return item; }), true); } if (th.relationship(type, AnyNodeTest.getInstance()) != Affinity.DISJOINT) { result = new ItemMappingIterator( result, new ConfigurationCheckingFunction(context.getConfiguration()), true); } } return result; } @Override public Item evaluateItem(final XPathContext context) throws XPathException { ExtensionFunctionDefinition definition = function.getDefinition(); Sequence[] argValues = new Sequence[getArity()]; for (int i = 0; i < argValues.length; i++) { argValues[i] = SequenceTool.toLazySequence(getArg(i).iterate(context)); } final RoleDiagnostic role = new RoleDiagnostic(RoleDiagnostic.FUNCTION_RESULT, getFunctionName().getDisplayName(), 0); final Configuration config = context.getConfiguration(); final TypeHierarchy th = config.getTypeHierarchy(); Item result; try { result = function.call(context, argValues).head(); } catch (XPathException e) { e.maybeSetLocation(getLocation()); throw e; } if (!definition.trustResultType()) { final ItemType type = resultType.getPrimaryType(); if (!type.matches(result, th) || (result == null && !Cardinality.allowsZero(resultType.getCardinality()))) { String msg = role.composeErrorMessage(type, result, th); XPathException err = new XPathException(msg, "XPTY0004"); err.setLocation(getLocation()); throw err; } if (result instanceof NodeInfo && !config.isCompatible(((NodeInfo) result).getConfiguration())) { throw new XPathException( "Node returned by extension function was created with an incompatible Configuration", SaxonErrorCode.SXXP0004); } } return result; } /** * Get the effective boolean value of the expression. This returns false if the value * is the empty sequence, a zero-length string, a number equal to zero, or the boolean * false. Otherwise it returns true. * * @param context The context in which the expression is to be evaluated * @return the effective boolean value * @throws net.sf.saxon.trans.XPathException * if any dynamic error occurs evaluating the * expression */ @Override public boolean effectiveBooleanValue(XPathContext context) throws XPathException { Sequence[] argValues = new Sequence[getArity()]; for (int i = 0; i < argValues.length; i++) { argValues[i] = SequenceTool.toLazySequence(getArg(i).iterate(context)); } try { return function.effectiveBooleanValue(context, argValues); } catch (XPathException e) { e.maybeSetLocation(getLocation()); throw e; } } @Override public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { return function.call(context, arguments); } /** * This class checks that NodeInfo objects returned by an extension function were created * under the right Configuration */ public static class ConfigurationCheckingFunction implements ItemMappingFunction { private final Configuration config; public ConfigurationCheckingFunction(Configuration config) { this.config = config; } /** * Map one item to another item. * * @param item The input item to be mapped. * @return either the output item, or null. */ @Override public Item mapItem(Item item) throws XPathException { if (item instanceof NodeInfo && !config.isCompatible(((NodeInfo) item).getConfiguration())) { throw new XPathException( "Node returned by extension function was created with an incompatible Configuration", SaxonErrorCode.SXXP0004); } return item; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy