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

net.sf.saxon.functions.hof.CoercedFunction Maven / Gradle / Ivy

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

import net.sf.saxon.expr.FunctionCall;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.expr.parser.ContextItemStaticInfo;
import net.sf.saxon.expr.parser.ExpressionVisitor;
import net.sf.saxon.expr.parser.Loc;
import net.sf.saxon.expr.parser.RoleDiagnostic;
import net.sf.saxon.functions.AbstractFunction;
import net.sf.saxon.om.Function;
import net.sf.saxon.om.GroundedValue;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.query.AnnotationList;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.FunctionItemType;
import net.sf.saxon.type.SpecificFunctionType;
import net.sf.saxon.type.TypeHierarchy;
import net.sf.saxon.value.SequenceType;

/**
 * A function item obtained by coercing a supplied function; this adds a wrapper to perform dynamic
 * type checking of the arguments in any call, and type checking of the result.
 */
public class CoercedFunction extends AbstractFunction {

    private Function targetFunction;
    private final SpecificFunctionType requiredType;

    /**
     * Create a CoercedFunction as a wrapper around a target function
     *
     * @param targetFunction the function to be wrapped by a type-checking layer
     * @param requiredType the type of the coerced function, that is the type required
     *                     by the context in which the target function is being used
     * @throws XPathException if the arity of the supplied function does not match the arity of the required type
     */

    public CoercedFunction(Function targetFunction, SpecificFunctionType requiredType) throws XPathException {
        if (targetFunction.getArity() != requiredType.getArity()) {
            throw new XPathException(
                    wrongArityMessage(targetFunction, requiredType.getArity()), "XPTY0004");
        }
        this.targetFunction = targetFunction;
        this.requiredType = requiredType;
    }

    /**
     * Create a CoercedFunction whose target function is not yet known (happens during package re-loading)
     * @param requiredType the type of the coerced function, that is the type required
     *                     by the context in which the target function is being used
     */

    public CoercedFunction(SpecificFunctionType requiredType) {
        this.requiredType = requiredType;
    }

    /**
     * Set the target function
     * @param targetFunction the function to be wrapped by a type-checking layer
     * @throws XPathException if the arity of the supplied function does not match the arity of the required type
     */

    public void setTargetFunction(Function targetFunction) throws XPathException {
        if (targetFunction.getArity() != requiredType.getArity()) {
            throw new XPathException(
                    wrongArityMessage(targetFunction, requiredType.getArity()), "XPTY0004");
        }
        this.targetFunction = targetFunction;
    }

    /**
     * Type check the function (may modify it by adding code for converting the arguments)
     *
     * @param visitor         the expression visitor, supplies context information
     * @param contextItemType the context item type at the point where the function definition appears
     * @throws XPathException if type checking of the target function fails
     *
     */
    @Override
    public void typeCheck(ExpressionVisitor visitor, ContextItemStaticInfo contextItemType) throws XPathException {
        if (targetFunction instanceof AbstractFunction) {
            ((AbstractFunction) targetFunction).typeCheck(visitor, contextItemType);
        }
    }

    /**
     * Get the item type of the function item
     *
     * @return the function item's type
     */

    @Override
    public FunctionItemType getFunctionItemType() {
        return requiredType;
    }

    /**
     * Get the name of the function, or null if it is anonymous
     *
     * @return null indicating that this is an anonymous inline function
     */

    /*@Nullable*/
    @Override
    public StructuredQName getFunctionName() {
        return targetFunction.getFunctionName();
    }

    /**
     * Get a description of this function for use in error messages. For named functions, the description
     * is the function name (as a lexical QName). For others, it might be, for example, "inline function",
     * or "partially-applied ends-with function".
     *
     * @return a description of the function for use in error messages
     */
    @Override
    public String getDescription() {
        return "coerced " + targetFunction.getDescription();
    }

    /**
     * Get the arity of the function
     *
     * @return the number of arguments in the function signature
     */

    @Override
    public int getArity() {
        return targetFunction.getArity();
    }

    /**
     * Get the function annotations. These are the same as the annotations of the base function
     * @return the annotations of the base function
     */
    @Override
    public AnnotationList getAnnotations() {
        return targetFunction.getAnnotations();
    }

    /**
     * Invoke the function
     *
     * @param context the XPath dynamic evaluation context
     * @param args    the actual arguments to be supplied
     * @return the result of invoking the function
     * @throws net.sf.saxon.trans.XPathException if execution of the function fails
     *
     */

    @Override
    public Sequence call(XPathContext context, Sequence[] args) throws XPathException {
        SpecificFunctionType req = requiredType;
        SequenceType[] argTypes = targetFunction.getFunctionItemType().getArgumentTypes();
        TypeHierarchy th = context.getConfiguration().getTypeHierarchy();
        Sequence[] targetArgs = new Sequence[args.length];
        for (int i = 0; i < args.length; i++) {
            GroundedValue gVal = args[i].materialize();
            if (argTypes[i].matches(gVal, th)) {
                targetArgs[i] = gVal;
            } else {
                RoleDiagnostic role = new RoleDiagnostic(RoleDiagnostic.FUNCTION, targetFunction.getDescription(), i);
                targetArgs[i] = th.applyFunctionConversionRules(gVal, argTypes[i], role, Loc.NONE);
            }
        }
        GroundedValue rawResult = targetFunction.call(context, targetArgs).materialize();
        if (req.getResultType().matches(rawResult, th)) {
            return rawResult;
        } else {
            RoleDiagnostic role = new RoleDiagnostic(RoleDiagnostic.FUNCTION_RESULT, targetFunction.getDescription(), 0);
            return th.applyFunctionConversionRules(rawResult, req.getResultType(), role, Loc.NONE);
        }
    }

    /**
     * Factory method to create a CoercedFunction with a given type, for a given targetFunction.
     * It is assumed that we have already established that coercion is needed.
     * Called from Bytecode compiler.
     * @param suppliedFunction the function to be coerced
     * @param requiredType the target type for the coercion
     * @param role diagnostic information about the role of the expression being coerced
     * @return the function after coercion
     * @throws XPathException if the function cannot be coerced to the required type (for example,
     * because it has the wrong arity)
     */

    public static CoercedFunction coerce(Function suppliedFunction, SpecificFunctionType requiredType,
                                         RoleDiagnostic role)
            throws XPathException {
        // DO NOT DELETE THIS METHOD: IT IS CALLED FROM BYTECODE
        int arity = requiredType.getArity();
        if (suppliedFunction.getArity() != arity) {
            String msg = role.composeErrorMessage(
                    requiredType, suppliedFunction, null);
            msg += ". " + wrongArityMessage(suppliedFunction, arity);
            throw new XPathException(msg, "XPTY0004");
        }
        return new CoercedFunction(suppliedFunction, requiredType);
    }

    private static String wrongArityMessage(Function supplied, int expected) {
        return "The supplied function (" + supplied.getDescription() + ") has " + FunctionCall.pluralArguments(supplied.getArity()) +
                " - expected " + expected;
    }

    /**
     * Export information about this function item to the SEF file
     *
     * @param out the SEF output destination
     */
    @Override
    public void export(ExpressionPresenter out) throws XPathException {
        out.startElement("coercedFn");
        out.emitAttribute("type", requiredType.toExportString());
        new FunctionLiteral(targetFunction).export(out);
        out.endElement();
    }

}

// Copyright (c) 2009-2022 Saxonica Limited




© 2015 - 2024 Weber Informatics LLC | Privacy Policy