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

net.sf.saxon.ma.arrays.ArrayFunctionSet 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.ma.arrays;

import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.expr.elab.Pingable;
import net.sf.saxon.functions.Fold;
import net.sf.saxon.functions.FoldingFunction;
import net.sf.saxon.functions.SystemFunction;
import net.sf.saxon.functions.registry.BuiltInFunctionSet;
import net.sf.saxon.ma.Parcel;
import net.sf.saxon.ma.zeno.ZenoSequence;
import net.sf.saxon.om.*;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.AnyItemType;
import net.sf.saxon.type.BuiltInAtomicType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.SpecificFunctionType;
import net.sf.saxon.value.*;
import net.sf.saxon.z.IntHashSet;
import net.sf.saxon.z.IntSet;

import java.util.ArrayList;
import java.util.List;

/**
 * Function signatures (and pointers to implementations) of the functions defined in XPath 3.1
 */

public class ArrayFunctionSet extends BuiltInFunctionSet {

    private final static ArrayFunctionSet instance31 = new ArrayFunctionSet(31);
    private final static ArrayFunctionSet instance40 = new ArrayFunctionSet(40);

    private ArrayFunctionSet(int version) {
        init(version);
    }

    public static ArrayFunctionSet getInstance(int version) {
        return version >= 40 ? instance40 : instance31;
    }

    private void init(int version) {


        register("append", 2, e -> e.populate( ArrayAppend::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, AnyItemType.getInstance(), STAR | NAV, null));

        ItemType filterFunctionType = new SpecificFunctionType(
                new SequenceType[]{SequenceType.ANY_SEQUENCE},
                SequenceType.SINGLE_BOOLEAN);

        register("filter", 2, e -> e.populate( ArrayFilter::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, filterFunctionType, ONE | INS, null));

        register("flatten", 1, e -> e.populate( ArrayFlatten::new, AnyItemType.getInstance(), STAR, 0)
                .arg(0, AnyItemType.getInstance(), STAR | ABS, null));

        ItemType foldFunctionType = new SpecificFunctionType(
                new SequenceType[]{SequenceType.ANY_SEQUENCE, SequenceType.ANY_SEQUENCE},
                SequenceType.ANY_SEQUENCE);

        register("fold-left", 3, e -> e.populate( ArrayFoldLeft::new, AnyItemType.getInstance(), STAR, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, AnyItemType.getInstance(), STAR | NAV, null)
                .arg(2, foldFunctionType, ONE | INS, null));

        register("fold-right", 3, e -> e.populate( ArrayFoldRight::new, AnyItemType.getInstance(), STAR, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, AnyItemType.getInstance(), STAR | NAV, null)
                .arg(2, foldFunctionType, ONE | INS, null));


        ItemType forEachFunctionType = new SpecificFunctionType(
                new SequenceType[]{SequenceType.ANY_SEQUENCE},
                SequenceType.ANY_SEQUENCE);

        register("for-each", 2, e -> e.populate( ArrayForEach::new, AnyItemType.getInstance(), STAR, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, forEachFunctionType, ONE | INS, null));

        register("for-each-pair", 3, e -> e.populate( ArrayForEachPair::new, AnyItemType.getInstance(), STAR, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(2, foldFunctionType, ONE | INS, null));

        register("get", 2, e -> e.populate( ArrayGet::new, AnyItemType.getInstance(), STAR, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, BuiltInAtomicType.INTEGER, ONE | ABS, null));

        register("head", 1, e -> e.populate( ArrayHead::new, AnyItemType.getInstance(), STAR, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null));

        register("insert-before", 3, e -> e.populate( ArrayInsertBefore::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, BuiltInAtomicType.INTEGER, STAR | ABS, null)
                .arg(2, AnyItemType.getInstance(), STAR | NAV, null));

        register("join", 1, e -> e.populate( ArrayJoin::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, STAR | INS, null));

        register("put", 3, e -> e.populate( ArrayPut::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, BuiltInAtomicType.INTEGER, STAR | INS, null)
                .arg(2, AnyItemType.getInstance(), STAR | NAV, null));

        register("remove", 2, e -> e.populate( ArrayRemove::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, BuiltInAtomicType.INTEGER, STAR | ABS, null));

        register("reverse", 1, e -> e.populate( ArrayReverse::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null));

        register("size", 1, e -> e.populate( ArraySize::new, BuiltInAtomicType.INTEGER, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null));

        ItemType sortFunctionType = new SpecificFunctionType(
                new SequenceType[]{SequenceType.ANY_SEQUENCE},
                SequenceType.ATOMIC_SEQUENCE);

        register("sort", 1, e -> e.populate( ArraySort::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null));

        register("sort", 2, e -> e.populate( ArraySort::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, BuiltInAtomicType.STRING, OPT | ABS, null));

        register("sort", 3, e -> e.populate( ArraySort::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, BuiltInAtomicType.STRING, OPT | ABS, null)
                .arg(2, sortFunctionType, ONE | INS, null));

        register("subarray", 2, e -> e.populate( ArraySubarray::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, BuiltInAtomicType.INTEGER, ONE | ABS, null));

        register("subarray", 3, e -> e.populate( ArraySubarray::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null)
                .arg(1, BuiltInAtomicType.INTEGER, ONE | ABS, null)
                .arg(2, BuiltInAtomicType.INTEGER, ONE | ABS, null));



        register("tail", 1, e -> e.populate( ArrayTail::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null));

        // TODO: the following functions should be private

        register("_to-sequence", 1, e -> e.populate(ArrayToSequence::new, AnyItemType.getInstance(), STAR, 0)
                .arg(0, ArrayItemType.ANY_ARRAY_TYPE, ONE | INS, null));

        register("_from-sequence", 1, e -> e.populate(ArrayFromSequence::new, ArrayItemType.ANY_ARRAY_TYPE, ONE, 0)
                .arg(0, AnyItemType.getInstance(), STAR | INS, null));


    }

    @Override
    public NamespaceUri getNamespace() {
        return NamespaceUri.ARRAY_FUNCTIONS;
    }

    @Override
    public String getConventionalPrefix() {
        return "array";
    }

    /**
     * Check that a number proposed for use as a subscript is greater than zero and less than
     * the maximum subscript allowed by the implementation (2^31-1), returning the value
     * as a Java int
     *
     * @param subscript the proposed subscript (one-based)
     * @param limit the upper limit allowed (usually the size of the array, sometimes arraysize + 1)
     * @return the proposed subscript as an int, if it is in range (still one-based)
     * @throws XPathException if the subscript is 0, negative, or outside the permitted range
     */
    public static int checkSubscript(IntegerValue subscript, int limit) throws XPathException {
        int index = subscript.asSubscript();
        if (index <= 0) {
            throw new XPathException("Array subscript " + subscript.getUnicodeStringValue() + " is out of range", "FOAY0001");
        }
        if (index > limit) {
            throw new XPathException("Array subscript " + subscript.getUnicodeStringValue() +
                                             " exceeds limit (" + limit + ")", "FOAY0001");
        }
        return index;
    }

    /**
     * Abstract superclass for functions that produce an array, and that decide what kind of array implementation
     * to use based on past experience. Specifically, if the generated array is frequently converted to
     * an ImmutableArrayItem, then the function ends up deciding to generate an ImmutableArrayItem in the
     * first place.
     */

    public static abstract class ArrayGeneratingFunction extends SystemFunction implements Pingable {

        private double numberOfCalls = 0;
        private double numberOfConversions = 0;
        private double totalSize = 0;

        /**
         * Callback function, invoked when a SimpleArrayItem created by this function needs to be converted
         * to an {@code ImmutableArrayItem}
         */

        @Override
        public void ping() {
            numberOfConversions++;
        }

        /**
         * Get the estimated number of members in the array, based on past experience
         * @return the average size of arrays previously created, plus a little margin for expansion
         */

        protected int expectedSize() {
            return numberOfCalls < 10 ? 10 : (int)(totalSize / numberOfCalls * 1.05); // allow a little leeway
        }

        /**
         * Construct an array, given a list of members
         * @param members the members of the array
         * @return the constructed array
         */
        protected ArrayItem makeArray(List members) {
            if (numberOfConversions > Math.max(10, numberOfCalls * 0.5)) {
                // More than half the calls result in the array being converted...
                return new ImmutableArrayItem(members);
            } else {
                numberOfCalls++;
                totalSize += members.size();
                SimpleArrayItem result = new SimpleArrayItem(members);
                result.requestNotification(this);
                return result;
            }
        }
    }


    /**
     * Implementation of the function array:append(array, item()*) => array
     */
    public static class ArrayAppend extends SystemFunction {

        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            return array.append(arguments[1].materialize());
        }

    }

    /**
     * Implementation of the function array:filter(array, function) => array
     */
    public static class ArrayFilter extends ArrayGeneratingFunction {

        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {

            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            FunctionItem fn = (FunctionItem) arguments[1].head();
            List list = new ArrayList<>(expectedSize());
            for (GroundedValue gv : array.members()) {
                if (((BooleanValue) dynamicCall(fn, context, new Sequence[]{gv}).head()).getBooleanValue()) {
                    list.add(gv);
                }
            }
            return makeArray(list);
        }
    }

    /**
     * Implementation of the function array:flatten => item()*
     */
    public static class ArrayFlatten extends SystemFunction {

        private void flatten(Sequence arg, List out) {
            SequenceTool.supply(arg.iterate(), (ItemConsumer) item -> {
                if (item instanceof ArrayItem) {
                    for (GroundedValue member : ((ArrayItem) item).members()) {
                        flatten(member, out);
                    }
                } else {
                    out.add(item);
                }
            });
        }

        @Override
        public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
            List out = new ArrayList<>();
            flatten(arguments[0], out);
            return SequenceExtent.makeSequenceExtent(out);
        }
    }

    /**
     * Implementation of the function array:fold-left(array, item()*, function) => array
     */
    public static class ArrayFoldLeft extends SystemFunction {

        @Override
        public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            int arraySize = array.arrayLength();
            Sequence zero = arguments[1];
            FunctionItem fn = (FunctionItem) arguments[2].head();
            int i;
            for (i=0; i < arraySize; i++) {
                zero = dynamicCall(fn, context, new Sequence[]{zero, array.get(i)});
            }
            return zero;
        }
    }

    /**
     * Implementation of the function array:fold-left(array, item()*, function) => array
     */
    public static class ArrayFoldRight extends SystemFunction {

        @Override
        public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            Sequence zero = arguments[1];
            FunctionItem fn = (FunctionItem) arguments[2].head();
            int i;
            for (i = array.arrayLength() - 1; i >= 0; i--) {
                zero = dynamicCall(fn, context, new Sequence[]{array.get(i), zero});
            }
            return zero;
        }
    }

    /**
     * Implementation of the proposed 4.0 function array:exists(array)
     */
    public static class ArrayExists extends SystemFunction {

        @Override
        public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            int len = array.arrayLength();
            return BooleanValue.get(len > 0);
        }

    }

    /**
     * Implementation of the proposed 4.0 function array:empty(array)
     */
    public static class ArrayEmpty extends SystemFunction {

        @Override
        public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            int len = array.arrayLength();
            return BooleanValue.get(len == 0);
        }

    }

    /**
     * Implementation of the proposed 4.0 function array:foot(array) => item()*
     */
    public static class ArrayFoot extends SystemFunction {

        @Override
        public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            int len = array.arrayLength();
            if (len == 0) {
                throw new XPathException("Argument to array:foot is an empty array", "FOAY0001");
            }
            return array.get(len - 1);
        }

    }

    /**
     * Implementation of the function array:for-each(array, function) => array
     */
    public static class ArrayForEach extends ArrayGeneratingFunction {

        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            FunctionItem fn = (FunctionItem) arguments[1].head();
            List list = new ArrayList<>(expectedSize());
            for (GroundedValue gv : array.members()) {
                list.add(dynamicCall(fn, context, new GroundedValue[]{gv}).materialize());
            }
            return makeArray(list);
        }

    }

    /**
     * Implementation of the function array:for-each-pair(array, array, function) => array
     */
    public static class ArrayForEachPair extends ArrayGeneratingFunction {

        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array1 = (ArrayItem) arguments[0].head();
            assert array1 != null;
            ArrayItem array2 = (ArrayItem) arguments[1].head();
            assert array2 != null;
            FunctionItem fn = (FunctionItem) arguments[2].head();
            List list = new ArrayList<>(expectedSize());
            int i;
            for (i=0; i < array1.arrayLength() && i < array2.arrayLength(); i++) {
                list.add(dynamicCall(fn, context, new Sequence[]{array1.get(i), array2.get(i)}).materialize());
            }
            return makeArray(list);
        }
    }

    /**
     * Implementation of the function array:get(array, xs:integer) => item()*
     */
    public static class ArrayGet extends SystemFunction {

        @Override
        public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            IntegerValue index = (IntegerValue) arguments[1].head();
            return array.get(checkSubscript(index, array.arrayLength()) - 1);
        }

    }

    /**
     * Implementation of the function array:head(array) => item()*
     */
    public static class ArrayHead extends SystemFunction {

        @Override
        public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            if (array.arrayLength() == 0){
                throw new XPathException("Argument to array:head is an empty array","FOAY0001");
            }
            return array.get(0);
        }

    }

    /**
     * Implementation of the function array:insert-before(array, xs:integer, item()*) => array
     */
    public static class ArrayInsertBefore extends SystemFunction {


        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            int index = checkSubscript((IntegerValue) arguments[1].head(), array.arrayLength() + 1) - 1;
            if (index < 0 || index > array.arrayLength()){
                throw new XPathException("Specified position is not in range","FOAY0001");
            }
            Sequence newMember = arguments[2];
            return array.insert(index, newMember.materialize());
        }

    }

    /**
     * Implementation of the function array:join(arrays) => array
     */
    public static class ArrayJoin extends SystemFunction {

        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            SequenceIterator iterator = arguments[0].iterate();
            ArrayItem array = SimpleArrayItem.EMPTY_ARRAY;
            ArrayItem nextArray;
            while ((nextArray = (ArrayItem) iterator.next()) != null) {
                array = array.concat(nextArray);
            }
            return array;
        }

    }



    /**
     * Implementation of the function array:put(arrays, index, newValue) => array
     */
    public static class ArrayPut extends SystemFunction {

        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            int index = checkSubscript((IntegerValue) arguments[1].head(), array.arrayLength()) - 1;
            GroundedValue newVal = arguments[2].materialize();
            return array.put(index, newVal);
        }

    }

    /**
     * Implementation of the function array:remove(array, xs:integer) => array
     */
    public static class ArrayRemove extends SystemFunction {


        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            GroundedValue offsets = arguments[1].materialize();
            if (offsets instanceof IntegerValue) {
                int index = checkSubscript((IntegerValue) offsets, array.arrayLength()) - 1;
                return array.remove(index);
            }
            IntSet positions = new IntHashSet();
            SequenceIterator arg1 = offsets.iterate();
            SequenceTool.supply(arg1, (ItemConsumer) pos -> {
                int index = checkSubscript((IntegerValue) pos, array.arrayLength()) - 1;
                positions.add(index);
            });
            return array.removeSeveral(positions);
        }

    }

    /**
     * Implementation of the function array:reverse(array, xs:integer, xs:integer) => array
     */
    public static class ArrayReverse extends ArrayGeneratingFunction {

        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            List list = new ArrayList<>(array.arrayLength());
            int i;
            for (i=0; i < array.arrayLength(); i++) {
                list.add(array.get(array.arrayLength()-i-1));
            }
            return makeArray(list);
        }

    }

    /**
     * Implementation of the function array:size(array) => integer
     */
    public static class ArraySize extends SystemFunction {

        @Override
        public IntegerValue call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            return new Int64Value(array.arrayLength());
        }

    }

    /**
     * Implementation of the function array:subarray(array, xs:integer, xs:integer) => array
     */
    public static class ArraySubarray extends SystemFunction {


        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            int start = checkSubscript((IntegerValue) arguments[1].head(), array.arrayLength()+1);
            int length;
            if (arguments.length == 3) {
                IntegerValue len = (IntegerValue) arguments[2].head();
                int signum = len.signum();
                if (signum < 0) {
                    throw new XPathException("Specified length of subarray is less than zero", "FOAY0002");
                }
                length = signum == 0 ? 0 : checkSubscript(len, array.arrayLength());
            }
            else {
                length = array.arrayLength() - start + 1;
            }
            if (start < 1) {
                throw new XPathException("Start position is less than one","FOAY0001");
            }
            if (start > array.arrayLength() + 1) {
                throw new XPathException("Start position is out of bounds","FOAY0001");
            }
            if (start + length > array.arrayLength() + 1) {
                throw new XPathException("Specified length of subarray is too great for start position given","FOAY0001");
            }
            return array.subArray(start-1, start+length-1);
        }

    }

    /**
     * Implementation of the function array:tail(array) => item()*
     */
    public static class ArrayTail extends SystemFunction {

        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            assert array != null;
            if (array.arrayLength() < 1){
                throw new XPathException("Argument to array:tail is an empty array","FOAY0001");
            }
            return array.remove(0);
        }
    }

    /**
     * Implementation of the function array:_to-sequence(array) => item()* which
     * is used internally for the implementation of array?*
     */

    public static class ArrayToSequence extends SystemFunction {
        @Override
        public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
            ArrayItem array = (ArrayItem) arguments[0].head();
            return toSequence(array);
        }

        public static Sequence toSequence(ArrayItem array) throws XPathException {
            ZenoSequence results = new ZenoSequence();
            for (GroundedValue seq : array.members()) {
                results = results.appendSequence(seq);
            }
            return results;
        }
    }

    /**
     * Implementation of the function array:_from-sequence(item()*) => array(*) which
     * is used internally for the implementation of array{} and of the saxon:array extension
     */

    public static class ArrayFromSequence extends FoldingFunction implements Pingable {

        private double numberOfCalls = 0;
        private double numberOfConversions = 0;

        /**
         * Callback function, invoked when a SimpleArrayItem created by this function needs to be converted
         * to an {@code ImmutableArrayItem}
         */

        @Override
        public void ping() {
            numberOfConversions++;
        }

        @Override
        public ArrayItem call(XPathContext context, Sequence[] arguments) throws XPathException {
            if (numberOfConversions > Math.max(10, numberOfCalls * 0.5)) {
                return ImmutableArrayItem.from(arguments[0].iterate());
            } else {
                SimpleArrayItem result = SimpleArrayItem.makeSimpleArrayItem(arguments[0].iterate());
                result.requestNotification(this);
                numberOfCalls++;
                return result;
            }
        }

        /**
         * Create the Fold object which is used to perform a streamed evaluation
         *
         * @param context             the dynamic evaluation context
         * @param additionalArguments the values of all arguments other than the first.
         * @return the Fold object used to compute the function
         */
        @Override
        public Fold getFold(XPathContext context, Sequence... additionalArguments) {
            return new Fold() {
                final List members = new ArrayList<>();

                /**
                 * Process one item in the input sequence, returning a new copy of the working data
                 *
                 * @param item the item to be processed from the input sequence
                 */
                @Override
                public void processItem(Item item) {
                    members.add(item);
                }

                /**
                 * Ask whether the computation has completed. A function that can deliver its final
                 * result without reading the whole input should return true; this will be followed
                 * by a call on result() to deliver the final result.
                 *
                 * @return true if the result of the function is now available even though not all
                 * items in the sequence have been processed
                 */
                @Override
                public boolean isFinished() {
                    return false;
                }

                /**
                 * Compute the final result of the function, when all the input has been processed
                 *
                 * @return the result of the function
                 */
                @Override
                public Sequence result() {
                    return new SimpleArrayItem(members);
                }
            };
        }
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy