net.sf.saxon.expr.XPathContextMajor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of saxon-he Show documentation
Show all versions of saxon-he Show documentation
An OSGi bundle for Saxon-HE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 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.expr;
import net.sf.saxon.Configuration;
import net.sf.saxon.Controller;
import net.sf.saxon.expr.instruct.Executable;
import net.sf.saxon.expr.instruct.ParameterSet;
import net.sf.saxon.expr.instruct.SlotManager;
import net.sf.saxon.expr.instruct.UserFunction;
import net.sf.saxon.expr.sort.GroupIterator;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.regex.RegexIterator;
import net.sf.saxon.trace.InstructionInfo;
import net.sf.saxon.trace.Location;
import net.sf.saxon.trans.Mode;
import net.sf.saxon.trans.Rule;
import net.sf.saxon.trans.RuleManager;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.iter.SingletonIterator;
import net.sf.saxon.tree.iter.UnfailingIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* This class represents a "major context" in which an XPath expression is evaluated:
* a "major context" object allows all aspects of the dynamic context to change, whereas
* a "minor context" only allows changes to the focus and the destination for push output.
*/
public class XPathContextMajor extends XPathContextMinor {
private ParameterSet localParameters;
private ParameterSet tunnelParameters;
/*@Nullable*/ private UserFunction tailCallFunction;
private Mode currentMode;
/*@Nullable*/ private Rule currentTemplate;
private GroupIterator currentGroupIterator;
private RegexIterator currentRegexIterator;
boolean isTemporaryDestination = false;
Object origin;
private ThreadManager threadManager = null;
public class ThreadManager {
List threads = new ArrayList();
Exception errFound;
public void addThread(Thread t) {
threads.add(t);
}
public List getChildThreads() {
return threads;
}
public void setException(Exception err) {
errFound = err;
}
public XPathException getException() {
if (errFound instanceof XPathException) {
return (XPathException) errFound;
}
throw new AssertionError(errFound);
}
}
/**
* Add a new thread to the list of child threads
* for this context. Create threadManager if null.
*
* @param t the Thread
*/
public void addChildThread(Thread t) {
if (threadManager == null) {
threadManager = new ThreadManager();
}
synchronized (threadManager.threads) {
threadManager.addThread(t);
}
}
public ThreadManager getThreadManager() {
return threadManager;
}
public void createThreadManager() {
threadManager = new ThreadManager();
}
/**
* Check for running child threads and wait for them
* to complete and join to main thread.
* Throw any exception caught in any of the running threads
*/
public void notifyChildThreads() throws XPathException {
// if (getCaller() != null) {
// getCaller().notifyChildThreads();
// }
if (threadManager != null) {
List childThreads = threadManager.getChildThreads();
while (!childThreads.isEmpty()) {
try {
if (threadManager.errFound != null) {
throw threadManager.getException();
}
childThreads.get(0).join();
synchronized (threadManager.threads) {
childThreads.remove(0);
}
} catch (InterruptedException e) {
throw new AssertionError(e);
} catch (IndexOutOfBoundsException e) {
}
}
if (threadManager.errFound != null) {
XPathException err = threadManager.getException();
threadManager.errFound = null;
throw err;
}
// threadManager = null;
}
}
/**
* Constructor should only be called by the Controller,
* which acts as a XPathContext factory.
*
* @param controller the Controller
*/
public XPathContextMajor(Controller controller) {
this.controller = controller;
stackFrame = StackFrame.EMPTY;
origin = controller;
}
/**
* Private Constructor
*/
private XPathContextMajor() {
}
/**
* Constructor for use in free-standing Java applications.
*
* @param item the item to use as the initial context item. If this is null,
* the comtext item is initially undefined (which will cause a dynamic error
* if it is referenced).
* @param exec the Executable
*/
public XPathContextMajor(Item item, Executable exec) {
controller = new Controller(exec.getConfiguration(), exec);
if (item != null) {
UnfailingIterator iter = SingletonIterator.makeIterator(item);
iter.next();
currentIterator = iter;
last = new LastValue(1);
}
origin = controller;
}
/**
* Constructor for use in free-standing Java applications.
*
* @param item the item to use as the initial context item. If this is null,
* the comtext item is initially undefined (which will cause a dynamic error
* if it is referenced).
* @param config the Saxon Configuration
* @deprecated since 9.0 - use {@link #XPathContextMajor(Item, Executable)}
*/
public XPathContextMajor(Item item, Configuration config) {
// No longer used internally but retained for backwards compatibility (8.8)
Executable exec = new Executable(config);
exec.setHostLanguage(Configuration.JAVA_APPLICATION, true);
controller = new Controller(config, exec);
if (item != null) {
UnfailingIterator iter = SingletonIterator.makeIterator(item);
iter.next();
currentIterator = iter;
last = new LastValue(1);
}
origin = controller;
}
/**
* Construct a new context as a copy of another. The new context is effectively added
* to the top of a stack, and contains a pointer to the previous context. The
*/
public XPathContextMajor newContext() {
XPathContextMajor c = new XPathContextMajor();
c.controller = controller;
c.currentIterator = currentIterator;
c.stackFrame = stackFrame;
c.localParameters = localParameters;
c.tunnelParameters = tunnelParameters;
c.last = last;
c.currentReceiver = currentReceiver;
c.isTemporaryDestination = isTemporaryDestination;
c.currentMode = currentMode;
c.currentTemplate = currentTemplate;
c.currentRegexIterator = currentRegexIterator;
c.currentGroupIterator = currentGroupIterator;
c.caller = this;
c.tailCallFunction = null;
c.threadManager = threadManager;
return c;
}
/**
* Create a new "major" context (one that is capable of holding a stack frame with local variables
*
* @param prev the previous context (the one causing the new context to be created)
* @return the new major context
*/
public static XPathContextMajor newContext(XPathContextMinor prev) {
XPathContextMajor c = new XPathContextMajor();
XPathContext p = prev;
while (!(p instanceof XPathContextMajor)) {
p = p.getCaller();
}
c.controller = p.getController();
c.currentIterator = prev.getCurrentIterator();
c.stackFrame = prev.getStackFrame();
c.localParameters = p.getLocalParameters();
c.tunnelParameters = p.getTunnelParameters();
c.last = prev.last;
c.currentReceiver = prev.currentReceiver;
c.isTemporaryDestination = ((XPathContextMajor) p).isTemporaryDestination;
c.currentMode = p.getCurrentMode();
c.currentTemplate = p.getCurrentTemplateRule();
c.currentRegexIterator = p.getCurrentRegexIterator();
c.currentGroupIterator = p.getCurrentGroupIterator();
c.caller = prev;
c.tailCallFunction = null;
c.threadManager = ((XPathContextMajor) p).threadManager;
return c;
}
/**
* Make a copy of the supplied context for use in a new thread (typically for
* an asynchronous xsl:result-document
* @param prev the context to be copied
* @return the copy of the context
*/
public static XPathContextMajor newThreadContext(XPathContextMinor prev) {
XPathContextMajor c = newContext(prev);
c.stackFrame = prev.stackFrame.copy();
return c;
}
/**
* Get the local parameters for the current template call.
*
* @return the supplied parameters
*/
public ParameterSet getLocalParameters() {
if (localParameters == null) {
localParameters = new ParameterSet();
}
return localParameters;
}
/**
* Set the local parameters for the current template call.
*
* @param localParameters the supplied parameters
*/
public void setLocalParameters(ParameterSet localParameters) {
this.localParameters = localParameters;
}
/**
* Get the tunnel parameters for the current template call.
*
* @return the supplied tunnel parameters
*/
public ParameterSet getTunnelParameters() {
return tunnelParameters;
}
/**
* Set the tunnel parameters for the current template call.
*
* @param tunnelParameters the supplied tunnel parameters
*/
public void setTunnelParameters(ParameterSet tunnelParameters) {
this.tunnelParameters = tunnelParameters;
}
/**
* Set the creating expression (for use in diagnostics). The origin is generally set to "this" by the
* object that creates the new context. It's up to the debugger to determine whether this information
* is useful. The object will either be an {@link Expression}, allowing information
* about the calling instruction to be obtained, or null.
*/
public void setOrigin(InstructionInfo expr) {
origin = expr;
}
/**
* Set the type of creating expression (for use in diagnostics). When a new context is created, either
* this method or {@link XPathContextMajor#setOrigin} should be called.
*
* @param loc The originating location: the argument must be one of the integer constants in class
* {@link net.sf.saxon.trace.Location}
*/
public void setOriginatingConstructType(int loc) {
origin = Integer.valueOf(loc);
}
/**
* Get the type of location from which this context was created.
*/
public int getOriginatingConstructType() {
if (origin == null) {
return -1;
}
if (origin instanceof Expression) {
if (origin instanceof SlashExpression) {
return Location.PATH_EXPRESSION;
}
return ((Expression) origin).getConstructType();
} else if (origin instanceof Integer) {
return ((Integer) origin).intValue();
} else if (origin instanceof InstructionInfo) {
return ((InstructionInfo) origin).getConstructType();
} else {
return -1;
}
}
/**
* Get information about the creating expression or other construct.
*/
public InstructionInfo getOrigin() {
if (origin instanceof InstructionInfo) {
return (InstructionInfo) origin;
} else {
return null;
}
}
/**
* Set the local stack frame. This method is used when creating a Closure to support
* delayed evaluation of expressions. The "stack frame" is actually on the Java heap, which
* means it can survive function returns and the like.
*
* @param map the SlotManager, which holds static details of the allocation of variables to slots
* @param variables the array of "slots" to hold the actual variable values. This array will be
* copied if it is too small to hold all the variables defined in the SlotManager
*/
public void setStackFrame(SlotManager map, Sequence[] variables) {
stackFrame = new StackFrame(map, variables);
if (map != null && variables.length != map.getNumberOfVariables()) {
if (variables.length > map.getNumberOfVariables()) {
throw new IllegalStateException(
"Attempting to set more local variables (" + variables.length +
") than the stackframe can accommodate (" + map.getNumberOfVariables() + ")");
}
stackFrame.slots = new Sequence[map.getNumberOfVariables()];
System.arraycopy(variables, 0, stackFrame.slots, 0, variables.length);
}
}
/**
* Reset the stack frame variable map, while reusing the StackFrame object itself. This
* is done on a tail call to a different function
*
* @param map the SlotManager representing the stack frame contents
* @param numberOfParams the number of parameters required on the new stack frame
*/
public void resetStackFrameMap(SlotManager map, int numberOfParams) {
stackFrame.map = map;
if (stackFrame.slots.length != map.getNumberOfVariables()) {
Sequence[] v2 = new Sequence[map.getNumberOfVariables()];
System.arraycopy(stackFrame.slots, 0, v2, 0, numberOfParams);
stackFrame.slots = v2;
} else {
// not strictly necessary
Arrays.fill(stackFrame.slots, numberOfParams, stackFrame.slots.length, null);
}
}
/**
* Get a all the variables in the stack frame
*
* @return an array holding all the variables, each referenceable by its slot number
*/
public Sequence[] getAllVariableValues() {
return stackFrame.getStackFrameValues();
}
/**
* Overwrite all the variables in the stack frame
*
* @param values an array holding all the variables, each referenceable by its slot number;
* the caller must ensure this is the correct length (and valid in other ways)
*/
public void resetAllVariableValues(Sequence[] values) {
stackFrame.setStackFrameValues(values);
}
/**
* Overwrite all the parameters in the stack frame. (Used from compiled bytecode)
*
* @param values an array holding all the parameters, each referenceable by its slot number;
* the caller must ensure this is the correct length (and valid in other ways)
*/
public void resetParameterValues(Sequence[] values) {
System.arraycopy(values, 0, stackFrame.slots, 0, values.length);
}
/**
* Reset the local stack frame. This method is used when processing a tail-recursive function.
* Instead of the function being called recursively, the parameters are set to new values and the
* function body is evaluated repeatedly
*
* @param fn the user function being called using tail recursion
* @param variables the parameter to be supplied to the user function
*/
public void requestTailCall(UserFunction fn, Sequence[] variables) {
if (variables.length > stackFrame.slots.length) {
Sequence[] v2 = new Sequence[fn.getStackFrameMap().getNumberOfVariables()];
System.arraycopy(variables, 0, v2, 0, variables.length);
stackFrame.slots = v2;
} else {
System.arraycopy(variables, 0, stackFrame.slots, 0, variables.length);
}
tailCallFunction = fn;
}
/**
* Determine whether the body of a function is to be repeated, due to tail-recursive function calls
*
* @return null if no tail call has been requested, or the name of the function to be called otherwise
*/
public UserFunction getTailCallFunction() {
UserFunction fn = tailCallFunction;
tailCallFunction = null;
return fn;
}
/**
* Create a new stack frame for local variables, using the supplied SlotManager to
* define the allocation of slots to individual variables
*
* @param map the SlotManager for the new stack frame
*/
public void openStackFrame(SlotManager map) {
int numberOfSlots = map.getNumberOfVariables();
if (numberOfSlots == 0) {
stackFrame = StackFrame.EMPTY;
} else {
stackFrame = new StackFrame(map, new Sequence[numberOfSlots]);
}
}
/**
* Create a new stack frame large enough to hold a given number of local variables,
* for which no stack frame map is available. This is used in particular when evaluating
* match patterns of template rules.
*
* @param numberOfVariables The number of local variables to be accommodated.
*/
public void openStackFrame(int numberOfVariables) {
stackFrame = new StackFrame(new SlotManager(numberOfVariables),
new Sequence[numberOfVariables]);
}
/**
* Set the current mode.
*
* @param mode the new current mode
*/
public void setCurrentMode(Mode mode) {
this.currentMode = mode;
}
/**
* Get the current mode.
*
* @return the current mode. May return null if the current mode is the default mode.
*/
public Mode getCurrentMode() {
Mode m = currentMode;
if (m == null) {
RuleManager rm = getController().getRuleManager();
if (rm != null) {
return rm.getUnnamedMode();
} else {
return null;
}
} else {
return m;
}
}
/**
* Set the current template. This is used to support xsl:apply-imports. The caller
* is responsible for remembering the previous current template and resetting it
* after use.
*
* @param rule the current template rule, or null to indicate that there is no current template rule
*/
public void setCurrentTemplateRule(/*@Nullable*/ Rule rule) {
this.currentTemplate = rule;
}
/**
* Get the current template. This is used to support xsl:apply-imports
*
* @return the current template
*/
public Rule getCurrentTemplateRule() {
return currentTemplate;
}
/**
* Set the current grouping iterator. This supports the current-group() and
* current-grouping-key() functions in XSLT 2.0
*
* @param iterator the new current GroupIterator
*/
public void setCurrentGroupIterator(GroupIterator iterator) {
this.currentGroupIterator = iterator;
}
/**
* Get the current group iterator. This supports the current-group() and
* current-grouping-key() functions in XSLT 2.0
*
* @return the current grouped collection
*/
public GroupIterator getCurrentGroupIterator() {
return currentGroupIterator;
}
/**
* Set the current regex iterator. This supports the functionality of the regex-group()
* function in XSLT 2.0.
*
* @param currentRegexIterator the current regex iterator
*/
public void setCurrentRegexIterator(RegexIterator currentRegexIterator) {
this.currentRegexIterator = currentRegexIterator;
}
/**
* Get the current regex iterator. This supports the functionality of the regex-group()
* function in XSLT 2.0.
*
* @return the current regular expressions iterator
*/
public RegexIterator getCurrentRegexIterator() {
return currentRegexIterator;
}
/**
* Use local parameter. This is called when a local xsl:param element is processed.
* If a parameter of the relevant name was supplied, it is bound to the xsl:param element.
* Otherwise the method returns false, so the xsl:param default will be evaluated
*
* @param parameterId
* @param slotNumber
* @param isTunnel True if a tunnel parameter is required, else false @return ParameterSet.NOT_SUPPLIED, ParameterSet.SUPPLIED, or ParameterSet.SUPPLIED_AND_CHECKED
*/
public int useLocalParameter(
int parameterId, int slotNumber, boolean isTunnel) throws XPathException {
ParameterSet params = (isTunnel ? getTunnelParameters() : localParameters);
if (params == null) {
return ParameterSet.NOT_SUPPLIED;
}
int index = params.getIndex(parameterId);
if (index < 0) {
return ParameterSet.NOT_SUPPLIED;
}
Sequence val = params.getValue(index);
stackFrame.slots[slotNumber] = val;
boolean checked = params.isTypeChecked(index);
return (checked ? ParameterSet.SUPPLIED_AND_CHECKED : ParameterSet.SUPPLIED);
}
/**
* Set the XSLT output state to "temporary" or "final"
*
* @param temporary set to true to set temporary output state; false to set final output state
*/
public void setTemporaryOutputState(boolean temporary) {
isTemporaryDestination = temporary;
}
/**
* Ask whether the XSLT output state is "temporary" or "final"
*
* @return true to set temporary output state; false to set final output state
*/
public boolean isTemporaryOutputState() {
return isTemporaryDestination;
}
}