package client.net.sf.saxon.ce.expr;
import java.util.logging.Logger;
import client.net.sf.saxon.ce.expr.instruct.Executable;
import client.net.sf.saxon.ce.expr.instruct.GlobalParam;
import client.net.sf.saxon.ce.expr.instruct.UserFunctionParameter;
import client.net.sf.saxon.ce.om.Item;
import client.net.sf.saxon.ce.om.NodeInfo;
import client.net.sf.saxon.ce.om.SequenceIterator;
import client.net.sf.saxon.ce.om.ValueRepresentation;
import client.net.sf.saxon.ce.pattern.NodeTest;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.AnyItemType;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.type.TypeHierarchy;
import client.net.sf.saxon.ce.value.Cardinality;
import client.net.sf.saxon.ce.value.SequenceType;
import client.net.sf.saxon.ce.value.SingletonItem;
import client.net.sf.saxon.ce.value.Value;
/**
* Variable reference: a reference to a variable. This may be an XSLT-defined variable, a range
* variable defined within the XPath expression, or a variable defined in some other static context.
*/
public class VariableReference extends Expression {
protected Binding binding = null; // This will be null until fixup() is called; it will also be null
// if the variable reference has been inlined
protected SequenceType staticType = null;
protected Value constantValue = null;
transient String displayName = null;
private boolean flattened = false;
private boolean inLoop = true;
/**
* Create a Variable Reference
*/
public VariableReference() {
//System.err.println("Creating varRef");
}
/**
* Create a Variable Reference
* @param binding the variable binding to which this variable refers
*/
public VariableReference(Binding binding) {
//System.err.println("Creating varRef1");
displayName = binding.getVariableQName().getDisplayName();
fixup(binding);
}
/**
* Set static type. This is a callback from the variable declaration object. As well
* as supplying the static type, it may also supply a compile-time value for the variable.
* As well as the type information, other static properties of the value are supplied:
* for example, whether the value is an ordered node-set.
* @param type the static type of the variable
* @param value the value of the variable if this is a compile-time constant
* @param properties static properties of the expression to which the variable is bound
*/
public void setStaticType(SequenceType type, Value value, int properties) {
// System.err.println(this + " Set static type = " + type);
staticType = type;
constantValue = value;
// Although the variable may be a context document node-set at the point it is defined,
// the context at the point of use may be different, so this property cannot be transferred.
int dependencies = getDependencies();
staticProperties = (properties & ~StaticProperty.CONTEXT_DOCUMENT_NODESET) |
StaticProperty.NON_CREATIVE |
type.getCardinality() |
dependencies;
}
/**
* Mark an expression as being "flattened". This is a collective term that includes extracting the
* string value or typed value, or operations such as simple value construction that concatenate text
* nodes before atomizing. The implication of all of these is that although the expression might
* return nodes, the identity of the nodes has no significance. This is called during type checking
* of the parent expression. At present, only variable references take any notice of this notification.
*/
public void setFlattened(boolean flattened) {
super.setFlattened(flattened);
this.flattened = flattened;
}
/**
* Test whether this variable reference is flattened - that is, whether it is atomized etc
* @return true if the value of the variable is atomized, or converted to a string or number
*/
public boolean isFlattened() {
return flattened;
}
/**
* Type-check the expression. At this stage details of the static type must be known.
* If the variable has a compile-time value, this is substituted for the variable reference
*/
public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
if (constantValue != null) {
binding = null;
return Literal.makeLiteral(constantValue);
}
// if (staticType == null) {
// throw new IllegalStateException("Variable $" + getDisplayName() + " has not been fixed up");
// }
if (binding instanceof Expression) {
inLoop = visitor.isLoopingSubexpression((Expression)binding);
// following code removed because it causes error181 to blow the stack - need to check for circularities well
// if (binding instanceof GlobalVariable) {
// ((GlobalVariable)binding).typeCheck(visitor, AnyItemType.getInstance());
// }
} else if (binding instanceof UserFunctionParameter) {
inLoop = visitor.isLoopingSubexpression(null);
}
if (binding instanceof Assignation) {
((Assignation)binding).addReference(inLoop);
}
return this;
}
/**
* Type-check the expression. At this stage details of the static type must be known.
* If the variable has a compile-time value, this is substituted for the variable reference
*/
public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
if (constantValue != null) {
binding = null;
return Literal.makeLiteral(constantValue);
}
return this;
}
/**
* Fix up this variable reference to a Binding object, which enables the value of the variable
* to be located at run-time.
*/
public void fixup(Binding binding) {
this.binding = binding;
resetLocalStaticProperties();
}
/**
* Provide additional information about the type of the variable, typically derived by analyzing
* the initializer of the variable binding
* @param type the item type of the variable
* @param cardinality the cardinality of the variable
* @param constantValue the actual value of the variable, if this is known statically, otherwise null
* @param properties additional static properties of the variable's initializer
* @param visitor an ExpressionVisitor
*/
public void refineVariableType(
ItemType type, int cardinality, Value constantValue, int properties, ExpressionVisitor visitor) {
Executable exec = visitor.getExecutable();
if (exec == null) {
// happens during use-when evaluation
return;
}
TypeHierarchy th = exec.getConfiguration().getTypeHierarchy();
ItemType oldItemType = getItemType(th);
ItemType newItemType = oldItemType;
if (th.isSubType(type, oldItemType)) {
newItemType = type;
}
int newcard = cardinality & getCardinality();
if (newcard==0) {
// this will probably lead to a type error later
newcard = getCardinality();
}
SequenceType seqType = SequenceType.makeSequenceType(newItemType, newcard);
setStaticType(seqType, constantValue, properties);
}
/**
* Determine the data type of the expression, if possible
*
* @param th the type hierarchy cache
* @return the type of the variable, if this can be determined statically;
* otherwise Type.ITEM (meaning not known in advance)
*/
public ItemType getItemType(TypeHierarchy th) {
if (staticType == null || staticType.getPrimaryType() == AnyItemType.getInstance()) {
if (binding != null) {
return binding.getRequiredType().getPrimaryType();
}
return AnyItemType.getInstance();
} else {
return staticType.getPrimaryType();
}
}
/**
* Get the static cardinality
*/
public int computeCardinality() {
if (staticType == null) {
if (binding == null) {
return StaticProperty.ALLOWS_ZERO_OR_MORE;
} else if (binding instanceof LetExpression) {
return binding.getRequiredType().getCardinality();
} else if (binding instanceof Assignation) {
return StaticProperty.EXACTLY_ONE;
} else {
return binding.getRequiredType().getCardinality();
}
} else {
return staticType.getCardinality();
}
}
/**
* Determine the special properties of this expression
*
* @return {@link StaticProperty#NON_CREATIVE}
*/
public int computeSpecialProperties() {
int p = super.computeSpecialProperties();
p |= StaticProperty.NON_CREATIVE;
if (binding instanceof Assignation) {
Expression exp = ((Assignation)binding).getSequence();
if (exp != null) {
p |= (exp.getSpecialProperties() & StaticProperty.NOT_UNTYPED);
}
}
if (staticType != null &&
!Cardinality.allowsMany(staticType.getCardinality()) &&
staticType.getPrimaryType() instanceof NodeTest) {
p |= StaticProperty.SINGLE_DOCUMENT_NODESET;
}
return p;
}
/**
* Test if this expression is the same as another expression.
* (Note, we only compare expressions that
* have the same static and dynamic context).
*/
public boolean equals(Object other) {
return (other instanceof VariableReference &&
binding == ((VariableReference) other).binding &&
binding != null);
}
/**
* get HashCode for comparing two expressions
*/
public int hashCode() {
return binding == null ? 73619830 : binding.hashCode();
}
public int getIntrinsicDependencies() {
int d = 0;
if (binding == null) {
// assume the worst
d |= (StaticProperty.DEPENDS_ON_LOCAL_VARIABLES |
StaticProperty.DEPENDS_ON_RUNTIME_ENVIRONMENT);
} else if (binding.isGlobal()) {
if (binding instanceof GlobalParam) {
d |= StaticProperty.DEPENDS_ON_RUNTIME_ENVIRONMENT;
}
} else {
d |= StaticProperty.DEPENDS_ON_LOCAL_VARIABLES;
}
return d;
}
/**
* Promote this expression if possible
*/
public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
return this;
}
/**
* An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
* This method indicates which of these methods is provided. This implementation provides both all three methods
* natively.
*/
public int getImplementationMethod() {
return (Cardinality.allowsMany(getCardinality()) ? 0 : EVALUATE_METHOD)
| ITERATE_METHOD | PROCESS_METHOD;
}
/**
* Get the value of this variable in a given context.
*
* @param c the XPathContext which contains the relevant variable bindings
* @return the value of the variable, if it is defined
* @throws XPathException if the variable is undefined
*/
public SequenceIterator iterate(XPathContext c) throws XPathException {
try {
ValueRepresentation actual = evaluateVariable(c);
return Value.getIterator(actual);
} catch (XPathException err) {
err.maybeSetLocation(getSourceLocator());
throw err;
} catch (AssertionError err) {
//err.printStackTrace();
String msg = err.getMessage() + ". Variable reference $" + getDisplayName() +
(getSystemId() == null ? "" : " of " + getSystemId());
// log here in case this is not handled properly
Logger logger = Logger.getLogger("VariableReference");
logger.severe("internal null reference error: " + msg);
throw new XPathException(msg);
}
}
public Item evaluateItem(XPathContext c) throws XPathException {
try {
ValueRepresentation actual = evaluateVariable(c);
if (actual instanceof Item) {
return (Item) actual;
}
return Value.asItem(actual);
} catch (XPathException err) {
err.maybeSetLocation(getSourceLocator());
throw err;
}
}
public void process(XPathContext c) throws XPathException {
try {
ValueRepresentation actual = evaluateVariable(c);
if (actual instanceof NodeInfo) {
actual = new SingletonItem((NodeInfo) actual);
}
((Value) actual).process(c);
} catch (XPathException err) {
err.maybeSetLocation(getSourceLocator());
throw err;
}
}
/**
* Evaluate this variable
* @param c the XPath dynamic context
* @return the value of the variable
* @throws XPathException if any error occurs
*/
public ValueRepresentation evaluateVariable(XPathContext c) throws XPathException {
try {
return binding.evaluateVariable(c);
} catch (NullPointerException err) {
if (binding == null) {
throw new IllegalStateException("Variable $" + displayName + " has not been fixed up");
} else {
throw err;
}
}
}
/**
* Get the object bound to the variable
* @return the Binding which declares this variable and associates it with a value
*/
public Binding getBinding() {
return binding;
}
/**
* Get the display name of the variable. This is taken from the variable binding if possible
* @return the display name (a lexical QName
*/
public String getDisplayName() {
if (binding != null) {
return binding.getVariableQName().getDisplayName();
} else {
return displayName;
}
}
/**
* The toString() method for an expression attempts to give a representation of the expression
* in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
* In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
*/
public String toString() {
String d = getDisplayName();
return "$" + (d == null ? "$" : d);
}
}
// 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.