001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.Controller;
005: import net.sf.saxon.expr.*;
006: import net.sf.saxon.om.AxisIterator;
007: import net.sf.saxon.om.SingletonIterator;
008: import net.sf.saxon.om.ValueRepresentation;
009: import net.sf.saxon.om.NamePool;
010: import net.sf.saxon.style.StandardNames;
011: import net.sf.saxon.trace.InstructionInfo;
012: import net.sf.saxon.trans.DynamicError;
013: import net.sf.saxon.trans.StaticError;
014: import net.sf.saxon.trans.XPathException;
015:
016: import java.util.ArrayList;
017: import java.util.List;
018: import java.util.Stack;
019:
020: /**
021: * A compiled global variable in a stylesheet or query. <br>
022: */
023:
024: public class GlobalVariable extends GeneralVariable implements
025: Container {
026:
027: private Executable executable;
028: private SlotManager stackFrameMap = null;
029:
030: public GlobalVariable() {
031: }
032:
033: public Executable getExecutable() {
034: return executable;
035: }
036:
037: public void setExecutable(Executable executable) {
038: this .executable = executable;
039: }
040:
041: public void setContainsLocals(SlotManager map) {
042: this .stackFrameMap = map;
043: }
044:
045: public boolean isGlobal() {
046: return true;
047: }
048:
049: /**
050: * Check for cycles in this variable definition
051: * @param referees the calls leading up to this one; it's an error if this variable is on the
052: * stack, because that means it calls itself directly or indirectly. The stack may contain
053: * variable definitions (GlobalVariable objects) and user-defined functions (UserFunction objects).
054: * It will never contain the same object more than once.
055: */
056:
057: public void lookForCycles(Stack referees) throws StaticError {
058: if (referees.contains(this )) {
059: int s = referees.indexOf(this );
060: referees.push(this );
061: String message = "Circular definition of global variable. ";
062: NamePool pool = executable.getConfiguration().getNamePool();
063: for (int i = s; i < referees.size() - 1; i++) {
064: if (referees.get(i + 1) instanceof GlobalVariable) {
065: GlobalVariable next = (GlobalVariable) referees
066: .get(i + 1);
067: if (i == s) {
068: message += '$' + getVariableName() + " uses $"
069: + next.getVariableName();
070: } else {
071: message += ", which uses $"
072: + next.getVariableName();
073: }
074: } else if (referees.get(i + 1) instanceof UserFunction) {
075: UserFunction next = (UserFunction) referees
076: .get(i + 1);
077: message += ", which calls "
078: + pool.getDisplayName(next
079: .getFunctionNameCode()) + "()";
080: }
081: }
082: message += '.';
083: StaticError err = new StaticError(message);
084: err.setErrorCode("XQST0054");
085: err.setLocator(this );
086: throw err;
087: }
088: if (select != null) {
089: referees.push(this );
090: List list = new ArrayList(10);
091: ExpressionTool.gatherReferencedVariables(select, list);
092: for (int i = 0; i < list.size(); i++) {
093: Binding b = (Binding) list.get(i);
094: if (b instanceof GlobalVariable) {
095: ((GlobalVariable) b).lookForCycles(referees);
096: }
097: }
098: list.clear();
099: ExpressionTool.gatherCalledFunctions(select, list);
100: for (int i = 0; i < list.size(); i++) {
101: UserFunction f = (UserFunction) list.get(i);
102: if (!referees.contains(f)) {
103: // recursive function calls are allowed
104: lookForFunctionCycles(f, referees);
105: }
106: }
107: referees.pop();
108: }
109: }
110:
111: /**
112: * Look for cyclic variable references that go via one or more function calls
113: */
114:
115: private static void lookForFunctionCycles(UserFunction f,
116: Stack referees) throws StaticError {
117: Expression body = f.getBody();
118: referees.push(f);
119: List list = new ArrayList(10);
120: ExpressionTool.gatherReferencedVariables(body, list);
121: for (int i = 0; i < list.size(); i++) {
122: Binding b = (Binding) list.get(i);
123: if (b instanceof GlobalVariable) {
124: ((GlobalVariable) b).lookForCycles(referees);
125: }
126: }
127: list.clear();
128: ExpressionTool.gatherCalledFunctions(body, list);
129: for (int i = 0; i < list.size(); i++) {
130: UserFunction fn = (UserFunction) list.get(i);
131: if (!referees.contains(fn)) {
132: // recursive function calls are allowed
133: lookForFunctionCycles(fn, referees);
134: }
135: }
136: referees.pop();
137: }
138:
139: /**
140: * Process the variable declaration
141: */
142:
143: public TailCall processLeavingTail(XPathContext context)
144: throws XPathException {
145:
146: // This code is not used. A global variable is not really an instruction, although
147: // it is modelled as such, and it will be evaluated using the evaluateVariable() call
148: return null;
149: }
150:
151: /**
152: * Evaluate the variable. That is,
153: * get the value of the select expression if present or the content
154: * of the element otherwise, either as a tree or as a sequence
155: */
156:
157: public ValueRepresentation getSelectValue(XPathContext context)
158: throws XPathException {
159: if (select == null) {
160: throw new AssertionError(
161: "*** No select expression for global variable!!");
162: } else {
163: XPathContextMajor c2 = context.newCleanContext();
164: c2.setOrigin(this );
165: AxisIterator initialNode = SingletonIterator
166: .makeIterator(c2.getController()
167: .getPrincipalSourceDocument());
168: initialNode.next();
169: c2.setCurrentIterator(initialNode);
170: if (stackFrameMap != null) {
171: c2.openStackFrame(stackFrameMap);
172: }
173: return ExpressionTool.lazyEvaluate(select, c2,
174: referenceCount);
175: }
176: }
177:
178: /**
179: * Evaluate the variable
180: */
181:
182: public ValueRepresentation evaluateVariable(XPathContext context)
183: throws XPathException {
184: Controller controller = context.getController();
185: Bindery b = controller.getBindery();
186:
187: ValueRepresentation v = b.getGlobalVariableValue(this );
188:
189: if (v != null) {
190: return v;
191: } else {
192:
193: // This is the first reference to a global variable; try to evaluate it now.
194: // But first set a flag to stop looping. This flag is set in the Bindery because
195: // the VariableReference itself can be used by multiple threads simultaneously
196:
197: try {
198: b.setExecuting(this , true);
199: ValueRepresentation value = getSelectValue(context);
200: b.defineGlobalVariable(this , value);
201: b.setExecuting(this , false);
202: return value;
203:
204: } catch (XPathException err) {
205: b.setExecuting(this , false);
206: if (err instanceof XPathException.Circularity) {
207: DynamicError e = new DynamicError(
208: "Circular definition of variable "
209: + getVariableName());
210: int lang = context.getController().getExecutable()
211: .getHostLanguage();
212: e
213: .setErrorCode(lang == Configuration.XQUERY ? "XQST0054"
214: : "XTDE0640");
215: e.setXPathContext(context);
216: // Detect it more quickly the next time (in a pattern, the error is recoverable)
217: select = new ErrorExpression(e);
218: e.setLocator(this );
219: throw e;
220: } else {
221: throw err;
222: }
223: }
224: }
225: }
226:
227: /**
228: * Get InstructionInfo for this expression
229: */
230:
231: public InstructionInfo getInstructionInfo() {
232: InstructionDetails details = new InstructionDetails();
233: details.setConstructType(StandardNames.XSL_VARIABLE);
234: details.setObjectNameCode(getVariableFingerprint());
235: details.setProperty("expression", this );
236: details.setSystemId(getSystemId());
237: details.setLineNumber(getLineNumber());
238: details.setColumnNumber(getColumnNumber());
239: return details;
240: }
241:
242: }
243:
244: //
245: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
246: // you may not use this file except in compliance with the License. You may obtain a copy of the
247: // License at http://www.mozilla.org/MPL/
248: //
249: // Software distributed under the License is distributed on an "AS IS" basis,
250: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
251: // See the License for the specific language governing rights and limitations under the License.
252: //
253: // The Original Code is: all this file.
254: //
255: // The Initial Developer of the Original Code is Michael H. Kay.
256: //
257: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
258: //
259: // Contributor(s): none.
260: //
|