package client.net.sf.saxon.ce.expr.instruct;
import client.net.sf.saxon.ce.Controller;
import client.net.sf.saxon.ce.expr.*;
import client.net.sf.saxon.ce.lib.NamespaceConstant;
import client.net.sf.saxon.ce.om.ValueRepresentation;
import client.net.sf.saxon.ce.tree.iter.SingletonIterator;
import client.net.sf.saxon.ce.tree.iter.UnfailingIterator;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.value.SingletonClosure;
/**
* A compiled global variable in a stylesheet or query. <br>
*/
public class GlobalVariable extends GeneralVariable implements Container {
private Executable executable;
private SlotManager stackFrameMap = null;
/**
* Create a global variable
*/
public GlobalVariable(){}
/**
* Get the executable containing this global variable
* @return the containing executable
*/
public Executable getExecutable() {
return executable;
}
/**
* Set the containing executable
* @param executable the executable that contains this global variable
*/
public void setExecutable(Executable executable) {
this.executable = executable;
}
/**
* Get the granularity of the container.
* @return 0 for a temporary container created during parsing; 1 for a container
* that operates at the level of an XPath expression; 2 for a container at the level
* of a global function or template
*/
public int getContainerGranularity() {
return 2;
}
/**
* The expression that initializes a global variable may itself use local variables.
* In this case a stack frame needs to be allocated while evaluating the global variable
* @param map The stack frame map for local variables used while evaluating this global
* variable.
*/
public void setContainsLocals(SlotManager map) {
this.stackFrameMap = map;
}
/**
* Is this a global variable?
* @return true (yes, it is a global variable)
*/
public boolean isGlobal() {
return true;
}
/**
* Process the variable declaration
*/
public TailCall processLeavingTail(XPathContext context) throws XPathException {
// This code is not used. A global variable is not really an instruction, although
// it is modelled as such, and it will be evaluated using the evaluateVariable() call
return null;
}
/**
* Evaluate the variable. That is,
* get the value of the select expression if present or the content
* of the element otherwise, either as a tree or as a sequence
*/
public ValueRepresentation getSelectValue(XPathContext context) throws XPathException {
if (select==null) {
throw new AssertionError("*** No select expression for global variable $" +
getVariableQName().getDisplayName() + "!!");
} else {
try {
XPathContextMajor c2 = context.newCleanContext();
UnfailingIterator initialNode =
SingletonIterator.makeIterator(c2.getController().getContextForGlobalVariables());
initialNode.next();
c2.setCurrentIterator(initialNode);
if (stackFrameMap != null) {
c2.openStackFrame(stackFrameMap);
}
return ExpressionTool.evaluate(select, evaluationMode, c2, referenceCount);
} catch (XPathException e) {
if (!getVariableQName().getNamespaceURI().equals(NamespaceConstant.SAXON_GENERATED_GLOBAL)) {
e.setIsGlobalError(true);
}
throw e;
}
}
}
/**
* Evaluate the variable
*/
public ValueRepresentation evaluateVariable(XPathContext context) throws XPathException {
final Controller controller = context.getController();
final Bindery b = controller.getBindery();
final ValueRepresentation v = b.getGlobalVariable(getSlotNumber());
if (v != null) {
return v;
} else {
return actuallyEvaluate(context);
}
}
/**
* Evaluate the global variable, and save its value for use in subsequent references.
* @param context the XPath dynamic context
* @return the value of the variable
* @throws XPathException if evaluation fails
*/
protected ValueRepresentation actuallyEvaluate(XPathContext context) throws XPathException {
final Controller controller = context.getController();
final Bindery b = controller.getBindery();
try {
// Set a flag to indicate that the variable is being evaluated. This is designed to prevent
// (where possible) the same global variable being evaluated several times in different threads
boolean go = b.setExecuting(this);
if (!go) {
// some other thread has evaluated the variable while we were waiting
return b.getGlobalVariable(getSlotNumber());
}
ValueRepresentation value = getSelectValue(context);
return b.saveGlobalVariableValue(this, value);
} catch (XPathException err) {
b.setNotExecuting(this);
if (err instanceof XPathException.Circularity) {
err.setErrorCode("XTDE0640");
err.setXPathContext(context);
// Detect it more quickly the next time (in a pattern, the error is recoverable)
SingletonClosure closure = new SingletonClosure(new ErrorExpression(err), context);
b.defineGlobalVariable(this, closure);
err.setLocator(getSourceLocator());
throw err;
} else {
throw err;
}
}
}
}
// 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.