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

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

package net.sf.saxon.functions;

import net.sf.saxon.expr.Expression;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.expr.instruct.UserFunction;
import net.sf.saxon.expr.parser.ContextItemStaticInfo;
import net.sf.saxon.expr.parser.ExplicitLocation;
import net.sf.saxon.expr.parser.ExpressionVisitor;
import net.sf.saxon.expr.parser.RoleDiagnostic;
import net.sf.saxon.ma.arrays.ArrayBlock;
import net.sf.saxon.ma.arrays.ArrayGet;
import net.sf.saxon.ma.arrays.ArrayItem;
import net.sf.saxon.ma.arrays.ArrayItemType;
import net.sf.saxon.ma.map.MapGet;
import net.sf.saxon.ma.map.MapType;
import net.sf.saxon.om.Function;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.SequenceTool;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.SymbolicName;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.*;
import net.sf.saxon.value.SequenceType;

import java.util.Properties;

/**
 * This class implements the function fn:apply(), which is a standard function in XQuery 3.1.
 * The fn:apply function is also used internally to implement dynamic function calls.
 */

public class ApplyFn extends SystemFunction  {

    // This flag is nonNull when the fn:apply() call actually implements a dynamic function call.
    // It only affects the error code/message if the call fails
    private String dynamicFunctionCall;

    public ApplyFn() {
    }

    /**
     * Say that this call to fn:apply was originally written as a dynamic function call
     * @param fnExpr string representation of the expression used as the dynamic call
     */

    public void setDynamicFunctionCall(String fnExpr) {
        dynamicFunctionCall = fnExpr;
    }

    /**
     * Ask whether this call to fn:apply was originally written as a dynamic function call
     *
     * @return true if it was originally a dynamic function call
     */

    public boolean isDynamicFunctionCall() {
        return dynamicFunctionCall != null;
    }

    /**
     * Get the return type, given knowledge of the actual arguments
     *
     * @param args the actual arguments supplied
     * @return the best available item type that the function will return
     */
    @Override
    public ItemType getResultItemType(Expression[] args) {
        // Item type of the result is the same as that of the supplied function
        ItemType fnType = args[0].getItemType();
        if (fnType instanceof AnyFunctionType) {
            return AnyItemType.getInstance();
        } else if (fnType instanceof FunctionItemType) {
            return ((FunctionItemType) fnType).getResultType().getPrimaryType();
        } else {
            return AnyItemType.getInstance();
        }
    }

    @Override
    public Expression makeOptimizedFunctionCall(ExpressionVisitor visitor, ContextItemStaticInfo contextInfo, Expression... arguments) throws XPathException {
        Expression target = arguments[0];
        if (target.getItemType() instanceof MapType) {
            // Convert $map($key) to map:get($map, $key)
            // This improves streamability analysis - see accumulator-053
            IntegratedFunctionLibrary lib = visitor.getConfiguration().getIntegratedFunctionLibrary();
            SymbolicName name = new SymbolicName(StandardNames.XSL_FUNCTION, MapGet.name, 2);
            Expression[] newArgs = new Expression[2];
            newArgs[0] = arguments[0]; // the map item
            newArgs[1] = (((ArrayBlock) arguments[1]).getOperanda().getOperand(0).getChildExpression());
            Expression mapGet = lib.bind(name, newArgs, visitor.getStaticContext());
            return mapGet.typeCheck(visitor, contextInfo);
        } else if (target.getItemType() instanceof ArrayItemType) {
            // Convert $array($key) to array:get($array, $key)
            IntegratedFunctionLibrary lib = visitor.getConfiguration().getIntegratedFunctionLibrary();
            SymbolicName name = new SymbolicName(StandardNames.XSL_FUNCTION, ArrayGet.name, 2);
            Expression[] newArgs = new Expression[2];
            newArgs[0] = arguments[0]; // the map item
            newArgs[1] = (((ArrayBlock) arguments[1]).getOperanda().getOperand(0).getChildExpression());
            Expression arrayGet = lib.bind(name, newArgs, visitor.getStaticContext());
            return arrayGet.typeCheck(visitor, contextInfo);
        }
        return null;
    }

    /**
     * Evaluate the expression
     *
     * @param context   the dynamic evaluation context
     * @param arguments the values of the arguments. The first argument is the function item
     *                  to be called; the second argument is an array containing
     *                  the arguments to be passed to that function, before conversion.
     * @return the result of the evaluation, in the form of a SequenceIterator
     * @throws net.sf.saxon.trans.XPathException
     *          if a dynamic error occurs during the evaluation of the expression
     */
    public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
        final Function function = (Function) arguments[0].head();
        ArrayItem args = (ArrayItem)arguments[1].head();

        if (function.getArity() != args.size()) {
            String errorCode = isDynamicFunctionCall() ? "XPTY0004" : "FOAP0001";
            XPathException err = new XPathException(
                "Number of arguments required = " + function.getArity() +
                    "; number supplied = " + args.size(), errorCode);
            err.setIsTypeError(isDynamicFunctionCall());
            err.setXPathContext(context);
            throw err;
        }
        TypeHierarchy th = context.getConfiguration().getTypeHierarchy();
        FunctionItemType fit = function.getFunctionItemType();
        Sequence[] argArray = new Sequence[args.size()];
        if (fit == AnyFunctionType.ANY_FUNCTION) {
            for (int i = 0; i < argArray.length; i++) {
                argArray[i] = args.get(i);
            }
        } else {
            for (int i = 0; i < argArray.length; i++) {
                SequenceType expected = fit.getArgumentTypes()[i];
                RoleDiagnostic role;
                if (isDynamicFunctionCall()) {
                    role = new RoleDiagnostic(RoleDiagnostic.FUNCTION, dynamicFunctionCall, i);
                } else {
                    role = new RoleDiagnostic(RoleDiagnostic.FUNCTION, "fn:apply", i + 1);
                }
                Sequence converted = th.applyFunctionConversionRules(
                    args.get(i), expected, role, ExplicitLocation.UNKNOWN_LOCATION);
                argArray[i] = SequenceTool.makeRepeatable(converted);
            }
        }
        XPathContext c2 = context;
        if (function instanceof UserFunction) {
            c2 = ((UserFunction)function).makeNewContext(context);
        }
        Sequence rawResult = function.call(c2, argArray);
        if (function.isTrustedResultType()) {
            // trust system functions to return a result of the correct type
            return rawResult;
        } else {
            // Check the result of the function
            RoleDiagnostic resultRole = new RoleDiagnostic(RoleDiagnostic.FUNCTION_RESULT, "fn:apply", -1);
            return th.applyFunctionConversionRules(
                rawResult, fit.getResultType(), resultRole, ExplicitLocation.UNKNOWN_LOCATION);
        }
    }

    @Override
    public void exportAttributes(ExpressionPresenter out) {
        out.emitAttribute("dyn", dynamicFunctionCall);
    }

    /**
     * Import any attributes found in the export file, that is, any attributes output using
     * the exportAttributes method
     *
     * @param attributes the attributes, as a properties object
     * @throws net.sf.saxon.trans.XPathException
     */
    @Override
    public void importAttributes(Properties attributes) throws XPathException {
        dynamicFunctionCall = attributes.getProperty("dyn");
    }
}


// Copyright (c) 2015 Saxonica Limited.




© 2015 - 2025 Weber Informatics LLC | Privacy Policy