001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: Variable.java,v 1.26 2005/05/17 17:22:15 mkwan Exp $
018: */
019: package org.apache.xpath.operations;
020:
021: import javax.xml.transform.TransformerException;
022:
023: import org.apache.xalan.res.XSLMessages;
024: import org.apache.xml.utils.QName;
025: import org.apache.xpath.Expression;
026: import org.apache.xpath.ExpressionOwner;
027: import org.apache.xpath.XPath;
028: import org.apache.xpath.XPathContext;
029: import org.apache.xpath.XPathVisitor;
030: import org.apache.xpath.axes.PathComponent;
031: import org.apache.xpath.axes.WalkerFactory;
032: import org.apache.xpath.objects.XNodeSet;
033: import org.apache.xpath.objects.XObject;
034: import org.apache.xpath.res.XPATHErrorResources;
035:
036: /**
037: * The variable reference expression executer.
038: */
039: public class Variable extends Expression implements PathComponent {
040: static final long serialVersionUID = -4334975375609297049L;
041: /** Tell if fixupVariables was called.
042: * @serial */
043: private boolean m_fixUpWasCalled = false;
044:
045: /** The qualified name of the variable.
046: * @serial */
047: protected QName m_qname;
048:
049: /**
050: * The index of the variable, which is either an absolute index to a
051: * global, or, if higher than the globals area, must be adjusted by adding
052: * the offset to the current stack frame.
053: */
054: protected int m_index;
055:
056: /**
057: * Set the index for the variable into the stack. For advanced use only. You
058: * must know what you are doing to use this.
059: *
060: * @param index a global or local index.
061: */
062: public void setIndex(int index) {
063: m_index = index;
064: }
065:
066: /**
067: * Set the index for the variable into the stack. For advanced use only.
068: *
069: * @return index a global or local index.
070: */
071: public int getIndex() {
072: return m_index;
073: }
074:
075: /**
076: * Set whether or not this is a global reference. For advanced use only.
077: *
078: * @param isGlobal true if this should be a global variable reference.
079: */
080: public void setIsGlobal(boolean isGlobal) {
081: m_isGlobal = isGlobal;
082: }
083:
084: /**
085: * Set the index for the variable into the stack. For advanced use only.
086: *
087: * @return true if this should be a global variable reference.
088: */
089: public boolean getGlobal() {
090: return m_isGlobal;
091: }
092:
093: protected boolean m_isGlobal = false;
094:
095: /**
096: * This function is used to fixup variables from QNames to stack frame
097: * indexes at stylesheet build time.
098: * @param vars List of QNames that correspond to variables. This list
099: * should be searched backwards for the first qualified name that
100: * corresponds to the variable reference qname. The position of the
101: * QName in the vector from the start of the vector will be its position
102: * in the stack frame (but variables above the globalsTop value will need
103: * to be offset to the current stack frame).
104: */
105: public void fixupVariables(java.util.Vector vars, int globalsSize) {
106: m_fixUpWasCalled = true;
107: int sz = vars.size();
108:
109: for (int i = vars.size() - 1; i >= 0; i--) {
110: QName qn = (QName) vars.elementAt(i);
111: // System.out.println("qn: "+qn);
112: if (qn.equals(m_qname)) {
113:
114: if (i < globalsSize) {
115: m_isGlobal = true;
116: m_index = i;
117: } else {
118: m_index = i - globalsSize;
119: }
120:
121: return;
122: }
123: }
124:
125: java.lang.String msg = XSLMessages.createXPATHMessage(
126: XPATHErrorResources.ER_COULD_NOT_FIND_VAR,
127: new Object[] { m_qname.toString() });
128:
129: TransformerException te = new TransformerException(msg, this );
130:
131: throw new org.apache.xml.utils.WrappedRuntimeException(te);
132:
133: }
134:
135: /**
136: * Set the qualified name of the variable.
137: *
138: * @param qname Must be a non-null reference to a qualified name.
139: */
140: public void setQName(QName qname) {
141: m_qname = qname;
142: }
143:
144: /**
145: * Get the qualified name of the variable.
146: *
147: * @return A non-null reference to a qualified name.
148: */
149: public QName getQName() {
150: return m_qname;
151: }
152:
153: /**
154: * Execute an expression in the XPath runtime context, and return the
155: * result of the expression.
156: *
157: *
158: * @param xctxt The XPath runtime context.
159: *
160: * @return The result of the expression in the form of a <code>XObject</code>.
161: *
162: * @throws javax.xml.transform.TransformerException if a runtime exception
163: * occurs.
164: */
165: public XObject execute(XPathContext xctxt)
166: throws javax.xml.transform.TransformerException {
167: return execute(xctxt, false);
168: }
169:
170: /**
171: * Dereference the variable, and return the reference value. Note that lazy
172: * evaluation will occur. If a variable within scope is not found, a warning
173: * will be sent to the error listener, and an empty nodeset will be returned.
174: *
175: *
176: * @param xctxt The runtime execution context.
177: *
178: * @return The evaluated variable, or an empty nodeset if not found.
179: *
180: * @throws javax.xml.transform.TransformerException
181: */
182: public XObject execute(XPathContext xctxt, boolean destructiveOK)
183: throws javax.xml.transform.TransformerException {
184: org.apache.xml.utils.PrefixResolver xprefixResolver = xctxt
185: .getNamespaceContext();
186:
187: XObject result;
188: // Is the variable fetched always the same?
189: // XObject result = xctxt.getVariable(m_qname);
190: if (m_fixUpWasCalled) {
191: if (m_isGlobal)
192: result = xctxt.getVarStack().getGlobalVariable(xctxt,
193: m_index, destructiveOK);
194: else
195: result = xctxt.getVarStack().getLocalVariable(xctxt,
196: m_index, destructiveOK);
197: } else {
198: result = xctxt.getVarStack().getVariableOrParam(xctxt,
199: m_qname);
200: }
201:
202: if (null == result) {
203: // This should now never happen...
204: warn(xctxt,
205: XPATHErrorResources.WG_ILLEGAL_VARIABLE_REFERENCE,
206: new Object[] { m_qname.getLocalPart() }); //"VariableReference given for variable out "+
207: // (new RuntimeException()).printStackTrace();
208: // error(xctxt, XPATHErrorResources.ER_COULDNOT_GET_VAR_NAMED,
209: // new Object[]{ m_qname.getLocalPart() }); //"Could not get variable named "+varName);
210:
211: result = new XNodeSet(xctxt.getDTMManager());
212: }
213:
214: return result;
215: // }
216: // else
217: // {
218: // // Hack city... big time. This is needed to evaluate xpaths from extensions,
219: // // pending some bright light going off in my head. Some sort of callback?
220: // synchronized(this)
221: // {
222: // org.apache.xalan.templates.ElemVariable vvar= getElemVariable();
223: // if(null != vvar)
224: // {
225: // m_index = vvar.getIndex();
226: // m_isGlobal = vvar.getIsTopLevel();
227: // m_fixUpWasCalled = true;
228: // return execute(xctxt);
229: // }
230: // }
231: // throw new javax.xml.transform.TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VAR_NOT_RESOLVABLE, new Object[]{m_qname.toString()})); //"Variable not resolvable: "+m_qname);
232: // }
233: }
234:
235: /**
236: * Get the XSLT ElemVariable that this sub-expression references. In order for
237: * this to work, the SourceLocator must be the owning ElemTemplateElement.
238: * @return The dereference to the ElemVariable, or null if not found.
239: */
240: public org.apache.xalan.templates.ElemVariable getElemVariable() {
241:
242: // Get the current ElemTemplateElement, and then walk backwards in
243: // document order, searching
244: // for an xsl:param element or xsl:variable element that matches our
245: // qname. If we reach the top level, use the StylesheetRoot's composed
246: // list of top level variables and parameters.
247:
248: org.apache.xalan.templates.ElemVariable vvar = null;
249: org.apache.xpath.ExpressionNode owner = getExpressionOwner();
250:
251: if (null != owner
252: && owner instanceof org.apache.xalan.templates.ElemTemplateElement) {
253:
254: org.apache.xalan.templates.ElemTemplateElement prev = (org.apache.xalan.templates.ElemTemplateElement) owner;
255:
256: if (!(prev instanceof org.apache.xalan.templates.Stylesheet)) {
257: while (prev != null
258: && !(prev.getParentNode() instanceof org.apache.xalan.templates.Stylesheet)) {
259: org.apache.xalan.templates.ElemTemplateElement savedprev = prev;
260:
261: while (null != (prev = prev
262: .getPreviousSiblingElem())) {
263: if (prev instanceof org.apache.xalan.templates.ElemVariable) {
264: vvar = (org.apache.xalan.templates.ElemVariable) prev;
265:
266: if (vvar.getName().equals(m_qname)) {
267: return vvar;
268: }
269: vvar = null;
270: }
271: }
272: prev = savedprev.getParentElem();
273: }
274: }
275: if (prev != null)
276: vvar = prev.getStylesheetRoot()
277: .getVariableOrParamComposed(m_qname);
278: }
279: return vvar;
280:
281: }
282:
283: /**
284: * Tell if this expression returns a stable number that will not change during
285: * iterations within the expression. This is used to determine if a proximity
286: * position predicate can indicate that no more searching has to occur.
287: *
288: *
289: * @return true if the expression represents a stable number.
290: */
291: public boolean isStableNumber() {
292: return true;
293: }
294:
295: /**
296: * Get the analysis bits for this walker, as defined in the WalkerFactory.
297: * @return One of WalkerFactory#BIT_DESCENDANT, etc.
298: */
299: public int getAnalysisBits() {
300: org.apache.xalan.templates.ElemVariable vvar = getElemVariable();
301: if (null != vvar) {
302: XPath xpath = vvar.getSelect();
303: if (null != xpath) {
304: Expression expr = xpath.getExpression();
305: if (null != expr && expr instanceof PathComponent) {
306: return ((PathComponent) expr).getAnalysisBits();
307: }
308: }
309: }
310: return WalkerFactory.BIT_FILTER;
311: }
312:
313: /**
314: * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
315: */
316: public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) {
317: visitor.visitVariableRef(owner, this );
318: }
319:
320: /**
321: * @see Expression#deepEquals(Expression)
322: */
323: public boolean deepEquals(Expression expr) {
324: if (!isSameClass(expr))
325: return false;
326:
327: if (!m_qname.equals(((Variable) expr).m_qname))
328: return false;
329:
330: // We have to make sure that the qname really references
331: // the same variable element.
332: if (getElemVariable() != ((Variable) expr).getElemVariable())
333: return false;
334:
335: return true;
336: }
337:
338: static final java.lang.String PSUEDOVARNAMESPACE = "http://xml.apache.org/xalan/psuedovar";
339:
340: /**
341: * Tell if this is a psuedo variable reference, declared by Xalan instead
342: * of by the user.
343: */
344: public boolean isPsuedoVarRef() {
345: java.lang.String ns = m_qname.getNamespaceURI();
346: if ((null != ns) && ns.equals(PSUEDOVARNAMESPACE)) {
347: if (m_qname.getLocalName().startsWith("#"))
348: return true;
349: }
350: return false;
351: }
352:
353: }
|