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

net.sf.saxon.value.Closure 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.value;

import net.sf.saxon.expr.*;
import net.sf.saxon.expr.elab.LearningEvaluator;
import net.sf.saxon.expr.elab.PullEvaluator;
import net.sf.saxon.expr.instruct.SlotManager;
import net.sf.saxon.om.*;
import net.sf.saxon.trace.Instrumentation;
import net.sf.saxon.trans.UncheckedXPathException;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.iter.ManualIterator;

/**
 * A Closure represents a value that has not yet been evaluated: the value is represented
 * by an expression, together with saved values of all the context variables that the
 * expression depends on.
 * 

This Closure is designed for use when the value is only read once. If the value * is read more than once, a new iterator over the underlying expression is obtained * each time: this may (for example in the case of a filter expression) involve * significant re-calculation.

*

The expression may depend on local variables and on the context item; these values * are held in the saved XPathContext object that is kept as part of the Closure, and they * will always be read from that object. The expression may also depend on global variables; * these are unchanging, so they can be read from the Bindery in the normal way. Expressions * that depend on other contextual information, for example the values of position(), last(), * current(), current-group(), should not be evaluated using this mechanism: they should * always be evaluated eagerly. This means that the Closure does not need to keep a copy * of these context variables.

*/ public abstract class Closure implements Sequence, ContextOriginator { protected PullEvaluator inputEvaluator; protected XPathContextMajor savedXPathContext; protected int depth = 0; protected LearningEvaluator learningEvaluator; protected int serialNumber; // The base iterator is used to copy items on demand from the underlying value // to the reservoir. It only ever has one instance (for each Closure) and each // item is read only once. protected SequenceIterator inputIterator; public Closure() { } // /** // * Construct a Closure by supplying the expression and the set of context variables. // * // * @param expression the expression to be lazily evaluated // * @param context the dynamic context of the expression including for example the variables // * on which it depends // * @param ref the number of references to the value being lazily evaluated; this affects // * the kind of Closure that is created // * @return the Closure, a virtual value that can later be materialized when its content is required // * @throws net.sf.saxon.trans.XPathException // * if a dynamic error occurs // */ // // /*@NotNull*/ // public static Sequence make( // Expression expression, XPathContext context, int ref) throws XPathException { // return context.getConfiguration().makeClosure(expression, ref, context); // } public void saveContext(/*@NotNull*/ Expression expression, /*@NotNull*/ XPathContext context) throws XPathException { // Make a copy of all local variables. If the value of any local variable is a closure // whose depth exceeds a certain threshold, we evaluate the closure eagerly to avoid // creating deeply nested lists of Closures, which consume memory unnecessarily // We only copy the local variables if the expression has dependencies on local variables. // What's more, we only copy those variables that the expression actually depends on. Instrumentation.count("Closure.saveContext()"); if ((expression.getDependencies() & StaticProperty.DEPENDS_ON_LOCAL_VARIABLES) != 0) { StackFrame localStackFrame = context.getStackFrame(); Sequence[] local = localStackFrame.getStackFrameValues(); int[] slotsUsed = expression.getSlotsUsed(); // computed on first call if (local != null) { final SlotManager stackFrameMap = localStackFrame.getStackFrameMap(); final Sequence[] savedStackFrame = new Sequence[stackFrameMap.getNumberOfVariables()]; for (int i : slotsUsed) { if (local[i] instanceof Closure) { int cdepth = ((Closure) local[i]).depth; if (cdepth >= 10) { local[i] = SequenceTool.toGroundedValue(local[i].iterate()); } else if (cdepth + 1 > depth) { depth = cdepth + 1; } } savedStackFrame[i] = local[i]; } savedXPathContext.setStackFrame(stackFrameMap, savedStackFrame); } } // Make a copy of the context item FocusIterator currentIterator = context.getCurrentIterator(); if (currentIterator != null) { Item contextItem = currentIterator.current(); ManualIterator single = new ManualIterator(contextItem); savedXPathContext.setCurrentIterator(single); // we don't save position() and last() because we have no way // of restoring them. So the caller must ensure that a Closure is not // created if the expression depends on position() or last() } } public void setLearningEvaluator(LearningEvaluator learningEvaluator, int serialNumber) { this.learningEvaluator = learningEvaluator; this.serialNumber = serialNumber; } /** * Get the first item in the sequence. * * @return the first item in the sequence if there is one, or null if the sequence * is empty * @throws net.sf.saxon.trans.XPathException * in the situation where the sequence is evaluated lazily, and * evaluation of the first item causes a dynamic error. */ @Override public Item head() throws XPathException { try { return iterate().next(); } catch (UncheckedXPathException e) { throw e.getXPathException(); } } /*@Nullable*/ public XPathContextMajor getSavedXPathContext() { return savedXPathContext; } public void setInputEvaluator(PullEvaluator inputEvaluator) { this.inputEvaluator = inputEvaluator; } public void setSavedXPathContext(XPathContextMajor savedXPathContext) { this.savedXPathContext = savedXPathContext; } /** * Evaluate the expression in a given context to return an iterator over a sequence */ /*@NotNull*/ @Override public abstract SequenceIterator iterate(); // { // // if (inputIterator == null) { // try { // inputIterator = inputEvaluator.iterate(savedXPathContext); // return inputIterator; // } catch (XPathException e) { // throw new UncheckedXPathException(e); // } // } else { // // In an ideal world this shouldn't happen: if the value is needed more than once, we should // // have chosen a MemoClosure. // // // Changed Feb 2015 to throw an exception. Processing the expression more than once is not only // // inefficient, it is wrong in the case where node-identity affects the outcome. See for example // // test case fn-fold-left-018 // // //return inputIterator.getAnother(); // throw new IllegalStateException("A Closure can only be read once"); // } // } /** * Reduce a value to its simplest form. If the value is a closure or some other form of deferred value * such as a FunctionCallPackage, then it is reduced to a SequenceExtent. If it is a SequenceExtent containing * a single item, then it is reduced to that item. One consequence that is exploited by class FilterExpression * is that if the value is a singleton numeric value, then the result will be an instance of NumericValue * @return the simplified value * @throws XPathException if an error occurs doing the lazy evaluation */ public GroundedValue reduce() throws XPathException { return SequenceTool.toGroundedValue(iterate()); } @Override public Sequence makeRepeatable() throws XPathException { return materialize(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy