001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.NamePool;
004: import net.sf.saxon.om.ValueRepresentation;
005: import net.sf.saxon.trans.XPathException;
006: import net.sf.saxon.value.EmptySequence;
007: import net.sf.saxon.value.MemoClosure;
008:
009: import java.util.ArrayList;
010: import java.util.Iterator;
011: import java.util.List;
012:
013: /**
014: * Assignation is an abstract superclass for the kinds of expression
015: * that declare range variables: for, some, and every.
016: */
017:
018: public abstract class Assignation extends ComputedExpression implements
019: Binding {
020:
021: protected int slotNumber = -999; // slot number for range variable
022: // (initialized to ensure a crash if no real slot is allocated)
023: protected Expression sequence; // the expression over which the variable ranges
024: protected Expression action; // the action performed for each value of the variable
025: protected String variableName;
026: protected int nameCode;
027:
028: protected transient RangeVariableDeclaration declaration;
029:
030: /**
031: * Set the reference to the variable declaration
032: */
033:
034: public void setVariableDeclaration(RangeVariableDeclaration decl) {
035: declaration = decl;
036: nameCode = decl.getNameCode();
037: variableName = decl.getVariableName();
038: }
039:
040: /**
041: * Get the variable declaration
042: */
043:
044: public RangeVariableDeclaration getVariableDeclaration() {
045: return declaration;
046: }
047:
048: /**
049: * Add the "return" or "satisfies" expression, and fix up all references to the
050: * range variable that occur within that expression
051: * @param action the expression that occurs after the "return" keyword of a "for"
052: * expression, the "satisfies" keyword of "some/every", or the ":=" operator of
053: * a "let" expression.
054: *
055: * <p>This method must be called <b>after</b> calling setVariableDeclaration()</p>
056: */
057:
058: public void setAction(Expression action) {
059: this .action = action;
060: if (declaration != null) {
061: declaration.fixupReferences(this );
062: }
063: adoptChildExpression(action);
064: }
065:
066: /**
067: * Indicate whether the binding is local or global. A global binding is one that has a fixed
068: * value for the life of a query or transformation; any other binding is local.
069: */
070:
071: public final boolean isGlobal() {
072: return false;
073: }
074:
075: /**
076: * Test whether it is permitted to assign to the variable using the saxon:assign
077: * extension element. This will only be for an XSLT global variable where the extra
078: * attribute saxon:assignable="yes" is present.
079: */
080:
081: public final boolean isAssignable() {
082: return false;
083: }
084:
085: /**
086: * If this is a local variable held on the local stack frame, return the corresponding slot number.
087: * In other cases, return -1.
088: */
089:
090: public int getLocalSlotNumber() {
091: return slotNumber;
092: }
093:
094: /**
095: * Get the action expression
096: */
097:
098: public Expression getAction() {
099: return action;
100: }
101:
102: /**
103: * Set the "sequence" expression - the one to which the variable is bound
104: */
105:
106: public void setSequence(Expression sequence) {
107: this .sequence = sequence;
108: adoptChildExpression(sequence);
109: }
110:
111: /**
112: * Set the slot number for the range variable
113: */
114:
115: public void setSlotNumber(int nr) {
116: slotNumber = nr;
117: }
118:
119: /**
120: * Get the number of slots required. Normally 1, except for a FOR expression with an AT clause, where it is 2.
121: */
122:
123: public int getRequiredSlots() {
124: return 1;
125: }
126:
127: /**
128: * Simplify the expression
129: */
130:
131: public Expression simplify(StaticContext env) throws XPathException {
132: sequence = sequence.simplify(env);
133: action = action.simplify(env);
134: return this ;
135: }
136:
137: /**
138: * Promote this expression if possible
139: */
140:
141: public Expression promote(PromotionOffer offer)
142: throws XPathException {
143: Expression exp = offer.accept(this );
144: if (exp != null) {
145: return exp;
146: } else {
147: sequence = doPromotion(sequence, offer);
148: if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES
149: || offer.action == PromotionOffer.UNORDERED
150: || offer.action == PromotionOffer.REPLACE_CURRENT) {
151: action = doPromotion(action, offer);
152: } else if (offer.action == PromotionOffer.RANGE_INDEPENDENT) {
153: // Pass the offer to the action expression only if the action isn't dependent on the
154: // variable bound by this assignation
155: Binding[] savedBindingList = offer.bindingList;
156: Binding[] newBindingList = extendBindingList(offer.bindingList);
157: offer.bindingList = newBindingList;
158: action = doPromotion(action, offer);
159: offer.bindingList = savedBindingList;
160: }
161: return this ;
162: }
163: }
164:
165: /**
166: * Suppress validation on contained element constructors, on the grounds that the parent element
167: * is already performing validation. The default implementation does nothing.
168: */
169:
170: public void suppressValidation(int validationMode) {
171: if (action instanceof ComputedExpression) {
172: ((ComputedExpression) action)
173: .suppressValidation(validationMode);
174: }
175: }
176:
177: /**
178: * Extend an array of variable bindings to include the binding(s) defined in this expression
179: */
180:
181: protected Binding[] extendBindingList(Binding[] in) {
182: Binding[] newBindingList = new Binding[in.length + 1];
183: System.arraycopy(in, 0, newBindingList, 0, in.length);
184: newBindingList[in.length] = this ;
185: return newBindingList;
186: }
187:
188: /**
189: * Promote a WHERE clause whose condition doesn't depend on the variable being bound.
190: * This rewrites an expression of the form
191: *
192: * <p>let $i := SEQ return if (C) then R else ()</p>
193: *
194: * <p>to the form:</p>
195: *
196: * <p>if (C) then (let $i := SEQ return R) else ()
197: */
198:
199: protected Expression promoteWhereClause(Binding positionBinding) {
200: if (action instanceof IfExpression) {
201: Container container = getParentExpression();
202: IfExpression ifex = (IfExpression) action;
203: Expression condition = ifex.getCondition();
204: Expression elseex = ifex.getElseExpression();
205: if (elseex instanceof EmptySequence) {
206: Binding[] bindingList;
207: if (positionBinding == null) {
208: Binding[] bl = { this };
209: bindingList = bl;
210: } else {
211: Binding[] bl = { this , positionBinding };
212: bindingList = bl;
213: }
214: List list = new ArrayList(5);
215: Expression promotedCondition = null;
216: BooleanExpression.listAndComponents(condition, list);
217: for (int i = list.size() - 1; i >= 0; i--) {
218: Expression term = (Expression) list.get(i);
219: if (!ExpressionTool.dependsOnVariable(term,
220: bindingList)) {
221: if (promotedCondition == null) {
222: promotedCondition = term;
223: } else {
224: promotedCondition = new BooleanExpression(
225: term, Token.AND, promotedCondition);
226: }
227: list.remove(i);
228: }
229: }
230: if (promotedCondition != null) {
231: if (list.size() == 0) {
232: // the whole if() condition has been promoted
233: Expression oldThen = ifex.getThenExpression();
234: setAction(oldThen);
235: ifex.setParentExpression(container);
236: ifex.setThenExpression(this );
237: return ifex;
238: } else {
239: // one or more terms of the if() condition have been promoted
240: Expression retainedCondition = (Expression) list
241: .get(0);
242: for (int i = 1; i < list.size(); i++) {
243: retainedCondition = new BooleanExpression(
244: retainedCondition, Token.AND,
245: (Expression) list.get(i));
246: }
247: ifex.setCondition(retainedCondition);
248: IfExpression newIf = new IfExpression(
249: promotedCondition, this , EmptySequence
250: .getInstance());
251: newIf.setParentExpression(container);
252: return newIf;
253: }
254: }
255:
256: }
257: }
258: return null;
259: }
260:
261: /**
262: * Get the immediate subexpressions of this expression
263: */
264:
265: public Iterator iterateSubExpressions() {
266: return new PairIterator(sequence, action);
267: }
268:
269: // Following methods implement the VariableDeclaration interface, in relation to the range
270: // variable
271:
272: public int getVariableNameCode() {
273: return nameCode;
274: }
275:
276: public int getVariableFingerprint() {
277: return nameCode & 0xfffff;
278: }
279:
280: /**
281: * Get the display name of the range variable, for diagnostics only
282: */
283:
284: public String getVariableName(NamePool pool) {
285: if (variableName == null) {
286: return "zz:var" + hashCode();
287: } else {
288: return variableName;
289: }
290: }
291:
292: /**
293: * Get the value of the range variable
294: */
295:
296: public ValueRepresentation evaluateVariable(XPathContext context)
297: throws XPathException {
298: ValueRepresentation actual = context
299: .evaluateLocalVariable(slotNumber);
300: if (actual instanceof MemoClosure
301: && ((MemoClosure) actual).isFullyRead()) {
302: actual = ((MemoClosure) actual).materialize();
303: context.setLocalVariable(slotNumber, actual);
304: }
305: return actual;
306: }
307:
308: }
309:
310: //
311: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
312: // you may not use this file except in compliance with the License. You may obtain a copy of the
313: // License at http://www.mozilla.org/MPL/
314: //
315: // Software distributed under the License is distributed on an "AS IS" basis,
316: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
317: // See the License for the specific language governing rights and limitations under the License.
318: //
319: // The Original Code is: all this file.
320: //
321: // The Initial Developer of the Original Code is Michael H. Kay
322: //
323: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
324: //
325: // Contributor(s): none.
326: //
|