package client.net.sf.saxon.ce.expr;
import client.net.sf.saxon.ce.Configuration;
import client.net.sf.saxon.ce.Controller;
import client.net.sf.saxon.ce.event.*;
import client.net.sf.saxon.ce.expr.instruct.LocalParam;
import client.net.sf.saxon.ce.expr.instruct.ParameterSet;
import client.net.sf.saxon.ce.expr.sort.GroupIterator;
import client.net.sf.saxon.ce.om.*;
import client.net.sf.saxon.ce.regex.RegexIterator;
import client.net.sf.saxon.ce.trans.Mode;
import client.net.sf.saxon.ce.trans.Rule;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.value.DateTimeValue;
/**
* This class represents a minor change in the dynamic 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 XPathContextMinor implements XPathContext {
Controller controller;
SequenceIterator currentIterator;
LastValue last = null;
SequenceReceiver currentReceiver;
boolean isTemporaryDestination = false;
XPathContext caller = null;
protected StackFrame stackFrame;
Object origin = null;
XPathException currentException;
/**
* Private Constructor
*/
protected XPathContextMinor() {
}
/**
* 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
*/
public XPathContextMajor newContext() {
return XPathContextMajor.newContext(this);
}
/**
* 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
*/
public XPathContextMinor newMinorContext() {
XPathContextMinor c = new XPathContextMinor();
c.controller = controller;
c.caller = this;
c.currentIterator = currentIterator;
c.currentReceiver = currentReceiver;
c.last = last;
c.isTemporaryDestination = isTemporaryDestination;
c.stackFrame = stackFrame;
c.currentException = currentException;
return c;
}
/**
* Set the calling XPathContext
*/
public void setCaller(XPathContext caller) {
this.caller = caller;
}
/**
* Construct a new context without copying (used for the context in a function call)
*/
public XPathContextMajor newCleanContext() {
XPathContextMajor c = new XPathContextMajor(getController());
c.setCaller(this);
return c;
}
/**
* Get the local parameters for the current template call.
* @return the supplied parameters
*/
public ParameterSet getLocalParameters() {
return getCaller().getLocalParameters();
}
/**
* Get the tunnel parameters for the current template call.
* @return the supplied tunnel parameters
*/
public ParameterSet getTunnelParameters() {
return getCaller().getTunnelParameters();
}
/**
* Get the Controller. May return null when running outside XSLT or XQuery
*/
public final Controller getController() {
return controller;
}
/**
* Get the Configuration
*/
public final Configuration getConfiguration() {
return controller.getConfiguration();
}
/**
* Get the Name Pool
*/
public final NamePool getNamePool() {
return controller.getNamePool();
}
/**
* Get the calling XPathContext (the next one down the stack). This will be null if unknown, or
* if the bottom of the stack has been reached.
*/
public final XPathContext getCaller() {
return caller;
}
/**
* Set a new sequence iterator.
*/
public void setCurrentIterator(SequenceIterator iter) {
currentIterator = iter;
last = new LastValue(-1);
}
/**
* Get the current iterator.
* This encapsulates the context item, context position, and context size.
* @return the current iterator, or null if there is no current iterator
* (which means the context item, position, and size are undefined).
*/
public final SequenceIterator getCurrentIterator() {
return currentIterator;
}
/**
* Get the context position (the position of the context item)
* @return the context position (starting at one)
* @throws XPathException if the context position is undefined
*/
public final int getContextPosition() throws XPathException {
if (currentIterator==null) {
XPathException e = new XPathException("The context position is currently undefined");
e.setXPathContext(this);
e.setErrorCode("FONC0001");
throw e;
}
return currentIterator.position();
}
/**
* Get the context item
* @return the context item, or null if the context item is undefined
*/
public final Item getContextItem() {
if (currentIterator==null) {
return null;
}
return currentIterator.current();
}
/**
* Get the context size (the position of the last item in the current node list)
* @return the context size
* @throws XPathException if the context position is undefined
*/
public final int getLast() throws XPathException {
if (currentIterator == null) {
XPathException e = new XPathException("The context size is currently undefined");
e.setXPathContext(this);
e.setErrorCode("FONC0001");
throw e;
}
if (last.value >= 0) {
return last.value;
}
if ((currentIterator.getProperties() & SequenceIterator.LAST_POSITION_FINDER) == 0) {
SequenceIterator another = currentIterator.getAnother();
int count = 0;
while (another.next() != null) {
count++;
}
return (last.value = count);
} else {
return (last.value = ((LastPositionFinder)currentIterator).getLastPosition());
}
}
/**
* Determine whether the context position is the same as the context size
* that is, whether position()=last()
*/
public final boolean isAtLast() throws XPathException {
return getContextPosition() == getLast();
}
/**
* Get a reference to the local stack frame for variables. Note that it's
* the caller's job to make a local copy of this. This is used for creating
* a Closure containing a retained copy of the variables for delayed evaluation.
* @return array of variables.
*/
public StackFrame getStackFrame() {
return stackFrame;
}
/**
* Get the value of a local variable, identified by its slot number
*/
public ValueRepresentation evaluateLocalVariable(int slotnumber) {
return stackFrame.slots[slotnumber];
}
/**
* Set the value of a local variable, identified by its slot number
*/
public void setLocalVariable(int slotnumber, ValueRepresentation value) {
try {
stackFrame.slots[slotnumber] = value;
} catch (ArrayIndexOutOfBoundsException e) {
throw new AssertionError("Internal error: invalid slot number for local variable " +
(slotnumber == -999 ? "(No slot allocated)" : "(" + slotnumber + ")"));
}
}
/**
* Set a new output destination, supplying the output format details. <BR>
* This affects all further output until resetOutputDestination() is called. Note that
* it is the caller's responsibility to close the Writer after use.
*
* @exception XPathException if any dynamic error occurs; and
* specifically, if an attempt is made to switch to a final output
* destination while writing a temporary tree or sequence @param isFinal true if the destination is a final result tree
* (either the principal output or a secondary result tree); false if
*/
public void changeOutputDestination(Receiver receiver,
boolean isFinal
)
throws XPathException {
if (isFinal && isTemporaryDestination) {
XPathException err = new XPathException("Cannot switch to a final result destination while writing a temporary tree");
err.setErrorCode("XTDE1480");
throw err;
}
if (!isFinal) {
isTemporaryDestination = true;
}
PipelineConfiguration pipe = receiver.getPipelineConfiguration();
ComplexContentOutputter out = new ComplexContentOutputter();
out.setPipelineConfiguration(pipe);
// add a filter to remove duplicate namespaces
NamespaceReducer ne = new NamespaceReducer();
ne.setUnderlyingReceiver(receiver);
ne.setPipelineConfiguration(pipe);
out.setReceiver(receiver);
currentReceiver = out;
}
/**
* Set the output destination to write to a sequence. <BR>
* This affects all further output until resetOutputDestination() is called.
*
* @param out The SequenceReceiver to be used
*/
public void setTemporaryReceiver(SequenceReceiver out) {
isTemporaryDestination = true;
currentReceiver = out;
}
/**
* Change the Receiver to which output is written
*/
public void setReceiver(SequenceReceiver receiver) {
currentReceiver = receiver;
}
/**
* Get the Receiver to which output is currently being written.
*
* @return the current Receiver
*/
public final SequenceReceiver getReceiver() {
return currentReceiver;
}
/**
* 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 qName The fingerprint of the parameter name
* @param binding The XSLParam element to bind its value to
* @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(StructuredQName qName,
LocalParam binding,
boolean isTunnel) throws XPathException {
return getCaller().useLocalParameter(qName, binding, isTunnel);
}
/**
* Get the current mode.
* @return the current mode
*/
public Mode getCurrentMode() {
return getCaller().getCurrentMode();
}
/**
* Get the current template. This is used to support xsl:apply-imports
*
* @return the current template
*/
public Rule getCurrentTemplateRule() {
return getCaller().getCurrentTemplateRule();
}
/**
* 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 getCaller().getCurrentGroupIterator();
}
/**
* 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 getCaller().getCurrentRegexIterator();
}
/**
* Get the current date and time for this query or transformation.
* All calls during one transformation return the same answer.
*
* @return Get the current date and time. This will deliver the same value
* for repeated calls within the same transformation
*/
public DateTimeValue getCurrentDateTime() {
return controller.getCurrentDateTime();
}
/**
* Get the implicit timezone, as a positive or negative offset from UTC in minutes.
* The range is -14hours to +14hours
* @return the implicit timezone as an offset from UTC in minutes
*/
public final int getImplicitTimezone() {
return controller.getImplicitTimezone();
}
/**
* Mark the context as being in (or not in) temporary output state
* @param temp true to set temporary output state; false to unset it
*/
public void setTemporaryOutputState(boolean temp) {
isTemporaryDestination = temp;
}
/**
* Set the current exception (in saxon:catch)
* @param exception the current exception
*/
public void setCurrentException(XPathException exception) {
currentException = exception;
}
/**
* Get the current exception (in saxon:catch)
* @return the current exception, or null if there is none defined
*/
public XPathException getCurrentException() {
return currentException;
}
// Note: consider eliminating this class. A new XPathContextMinor is created under two circumstances,
// (a) when the focus changes (i.e., a new current iterator), and (b) when the current
// receiver changes. We could handle these by maintaining a stack of iterators and a stack of
// receivers in the XPathContextMajor object. Adding a new iterator or receiver to the stack would
// generally be cheaper than creating the new XPathContextMinor object. The main difficulty (in the
// case of iterators) is knowing when to pop the stack: currently we rely on the garbage collector.
// We can only really do this when the iterator comes to its end, which is difficult to detect.
// Perhaps we should try to do static allocation, so that fixed slots are allocated for different
// minor-contexts within a Procedure, and a compiled expression that uses the focus knows which
// slot to look in.
// Investigated the above Sept 2008. On xmark, with a 100Mb input, the path expression
// count(site/people/person/watches/watch) takes just 13ms to execute (compared with 6500ms for building
// the tree). Only 6 context objects are created while doing this. This doesn't appear to be a productive
// area to look for new optimizations.
/**
* Container for cached value of the last() function.
* This is shared between all context objects that share the same current iterator.
* Introduced in 9.2 to handle the case where a new context is introduced when the current
* outputter changes, without changing the current iterator: in this case the cached value
* was being lost because each call on last() used a different context object.
*/
protected static class LastValue {
public int value = 0;
public LastValue(int count) {
value = count;
}
}
}
// 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.