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