001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.*;
004: import net.sf.saxon.trans.XPathException;
005: import net.sf.saxon.type.AnyItemType;
006: import net.sf.saxon.type.ItemType;
007: import net.sf.saxon.type.TypeHierarchy;
008: import net.sf.saxon.value.SequenceType;
009: import net.sf.saxon.value.Value;
010: import net.sf.saxon.value.SingletonNode;
011:
012: import java.io.PrintStream;
013:
014: /**
015: * Variable reference: a reference to a variable. This may be an XSLT-defined variable, a range
016: * variable defined within the XPath expression, or a variable defined in some other static context.
017: */
018:
019: public class VariableReference extends ComputedExpression implements
020: BindingReference {
021:
022: Binding binding = null; // This will be null until fixup() is called; it will also be null
023: // if the variable reference has been inlined
024: SequenceType staticType = null;
025: Value constantValue = null;
026: transient String displayName = null;
027:
028: /**
029: * Constructor
030: * @param declaration the variable declaration to which this variable refers
031: */
032:
033: public VariableReference(VariableDeclaration declaration) {
034:
035: // Register this variable reference with the variable declaration. When the variable declaration
036: // is compiled, the declaration will call the fixup() method of the variable reference. Note
037: // that the object does not retain a pointer to the variable declaration, which would cause the
038: // stylesheet to be locked in memory.
039:
040: // System.err.println("Register reference " + this + " with declaration " + declaration + " name=" + declaration.getVariableName());
041: declaration.registerReference(this );
042: displayName = declaration.getVariableName();
043: }
044:
045: /**
046: * Simplify the expression. Does nothing.
047: */
048:
049: public Expression simplify(StaticContext env) {
050: return this ;
051: }
052:
053: /**
054: * Set static type. This is a callback from the variable declaration object. As well
055: * as supplying the static type, it may also supply a compile-time value for the variable.
056: * As well as the type information, other static properties of the value are supplied:
057: * for example, whether the value is an ordered node-set.
058: */
059:
060: public void setStaticType(SequenceType type, Value value,
061: int properties) {
062: // System.err.println(this + " Set static type = " + type);
063: staticType = type;
064: constantValue = value;
065: // Although the variable may be a context document node-set at the point it is defined,
066: // the context at the point of use may be different, so this property cannot be transferred.
067: staticProperties = (properties & ~StaticProperty.CONTEXT_DOCUMENT_NODESET)
068: | type.getCardinality() | getDependencies();
069: }
070:
071: /**
072: * Type-check the expression. At this stage details of the static type must be known.
073: * If the variable has a compile-time value, this is substituted for the variable reference
074: */
075:
076: public Expression typeCheck(StaticContext env,
077: ItemType contextItemType) throws XPathException {
078: if (constantValue != null) {
079: binding = null;
080: return constantValue;
081: }
082: if (staticType == null) {
083: throw new IllegalStateException("Variable $" + displayName
084: + " has not been fixed up");
085: } else {
086: return this ;
087: }
088: }
089:
090: /**
091: * Type-check the expression. At this stage details of the static type must be known.
092: * If the variable has a compile-time value, this is substituted for the variable reference
093: */
094:
095: public Expression optimize(Optimizer opt, StaticContext env,
096: ItemType contextItemType) throws XPathException {
097: if (constantValue != null) {
098: binding = null;
099: return constantValue;
100: }
101: return this ;
102: }
103:
104: /**
105: * Fix up this variable reference to a Binding object, which enables the value of the variable
106: * to be located at run-time.
107: */
108:
109: public void fixup(Binding binding) {
110: // System.err.println("Binding for " + this + " is " + binding.getVariableName());
111: this .binding = binding;
112: resetStaticProperties();
113: }
114:
115: /**
116: * Determine the data type of the expression, if possible
117: * @return the type of the variable, if this can be determined statically;
118: * otherwise Type.ITEM (meaning not known in advance)
119: * @param th
120: */
121:
122: public ItemType getItemType(TypeHierarchy th) {
123: if (staticType == null) {
124: return AnyItemType.getInstance();
125: } else {
126: return staticType.getPrimaryType();
127: }
128: }
129:
130: /**
131: * Get the static cardinality
132: */
133:
134: public int computeCardinality() {
135: if (staticType == null) {
136: return StaticProperty.ALLOWS_ZERO_OR_MORE;
137: } else {
138: return staticType.getCardinality();
139: }
140: }
141:
142: /**
143: * Determine the special properties of this expression
144: * @return {@link StaticProperty#NON_CREATIVE} (unless the variable is assignable using saxon:assign)
145: */
146:
147: public int computeSpecialProperties() {
148: int p = super .computeSpecialProperties();
149: if (binding == null || !binding.isAssignable()) {
150: // if the variable reference is assignable, we mustn't move it, or any expression that contains it,
151: // out of a loop. The way to achieve this is to treat it as a "creative" expression, because the
152: // optimizer recognizes such expressions and handles them with care...
153: p |= StaticProperty.NON_CREATIVE;
154: }
155: return p;
156: }
157:
158: /**
159: * Test if this expression is the same as another expression.
160: * (Note, we only compare expressions that
161: * have the same static and dynamic context).
162: */
163:
164: public boolean equals(Object other) {
165: return (other instanceof VariableReference
166: && binding == ((VariableReference) other).binding && binding != null);
167: }
168:
169: /**
170: * get HashCode for comparing two expressions
171: */
172:
173: public int hashCode() {
174: return binding == null ? 73619830 : binding.hashCode();
175: }
176:
177: public int getIntrinsicDependencies() {
178: if (binding == null || !binding.isGlobal()) {
179: return StaticProperty.DEPENDS_ON_LOCAL_VARIABLES;
180: } else {
181: return 0;
182: }
183: }
184:
185: /**
186: * Promote this expression if possible
187: */
188:
189: public Expression promote(PromotionOffer offer)
190: throws XPathException {
191: if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES) {
192: Expression exp = offer.accept(this );
193: if (exp != null) {
194: // Replace the variable reference with the given expression.
195: //binding = null;
196: offer.accepted = true;
197: return exp;
198: }
199: }
200: return this ;
201: }
202:
203: /**
204: * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
205: * This method indicates which of these methods is provided. This implementation provides both all three methods
206: * natively.
207: */
208:
209: public int getImplementationMethod() {
210: return EVALUATE_METHOD | ITERATE_METHOD | PROCESS_METHOD;
211: }
212:
213: /**
214: * Get the value of this variable in a given context.
215: * @param c the XPathContext which contains the relevant variable bindings
216: * @return the value of the variable, if it is defined
217: * @throws XPathException if the variable is undefined
218: */
219:
220: public SequenceIterator iterate(XPathContext c)
221: throws XPathException {
222: ValueRepresentation actual = evaluateVariable(c);
223: return Value.getIterator(actual);
224: }
225:
226: public Item evaluateItem(XPathContext c) throws XPathException {
227: ValueRepresentation actual = evaluateVariable(c);
228: if (actual instanceof Item) {
229: return (Item) actual;
230: }
231: return Value.asItem(actual, c);
232: }
233:
234: public void process(XPathContext c) throws XPathException {
235: ValueRepresentation actual = evaluateVariable(c);
236: if (actual instanceof NodeInfo) {
237: actual = new SingletonNode((NodeInfo) actual);
238: }
239: ((Value) actual).process(c);
240: }
241:
242: public ValueRepresentation evaluateVariable(XPathContext c)
243: throws XPathException {
244:
245: if (binding == null) {
246: // System.err.println("No binding for " + this);
247: throw new IllegalStateException("Variable $" + displayName
248: + " has not been fixed up");
249: }
250:
251: return binding.evaluateVariable(c);
252: }
253:
254: /**
255: * Get the object bound to the variable
256: */
257:
258: public Binding getBinding() {
259: return binding;
260: }
261:
262: /**
263: * Diagnostic print of expression structure
264: */
265:
266: public void display(int level, NamePool pool, PrintStream out) {
267: if (displayName != null) {
268: out.println(ExpressionTool.indent(level) + '$'
269: + displayName);
270: } else {
271: out.println(ExpressionTool.indent(level)
272: + "$(unbound variable)");
273: }
274: }
275: }
276:
277: //
278: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
279: // you may not use this file except in compliance with the License. You may obtain a copy of the
280: // License at http://www.mozilla.org/MPL/
281: //
282: // Software distributed under the License is distributed on an "AS IS" basis,
283: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
284: // See the License for the specific language governing rights and limitations under the License.
285: //
286: // The Original Code is: all this file.
287: //
288: // The Initial Developer of the Original Code is Michael H. Kay.
289: //
290: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
291: //
292: // Contributor(s):
293: // Portions marked "e.g." are from Edwin Glaser (edwin@pannenleiter.de)
294: //
|