1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Oct 2017 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xpath.internal.operations; 23 24 import com.sun.org.apache.xalan.internal.res.XSLMessages; 25 import com.sun.org.apache.xml.internal.utils.QName; 26 import com.sun.org.apache.xpath.internal.Expression; 27 import com.sun.org.apache.xpath.internal.ExpressionOwner; 28 import com.sun.org.apache.xpath.internal.XPathContext; 29 import com.sun.org.apache.xpath.internal.XPathVisitor; 30 import com.sun.org.apache.xpath.internal.axes.PathComponent; 31 import com.sun.org.apache.xpath.internal.axes.WalkerFactory; 32 import com.sun.org.apache.xpath.internal.objects.XNodeSet; 33 import com.sun.org.apache.xpath.internal.objects.XObject; 34 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; 35 import java.util.List; 36 import javax.xml.transform.TransformerException; 37 38 39 /** 40 * The variable reference expression executer. 41 */ 42 public class Variable extends Expression implements PathComponent 43 { 44 static final long serialVersionUID = -4334975375609297049L; 45 /** Tell if fixupVariables was called. 46 * @serial */ 47 private boolean m_fixUpWasCalled = false; 48 49 /** The qualified name of the variable. 50 * @serial */ 51 protected QName m_qname; 52 53 /** 54 * The index of the variable, which is either an absolute index to a 55 * global, or, if higher than the globals area, must be adjusted by adding 56 * the offset to the current stack frame. 57 */ 58 protected int m_index; 59 60 /** 61 * Set the index for the variable into the stack. For advanced use only. You 62 * must know what you are doing to use this. 63 * 64 * @param index a global or local index. 65 */ 66 public void setIndex(int index) 67 { 68 m_index = index; 69 } 70 71 /** 72 * Set the index for the variable into the stack. For advanced use only. 73 * 74 * @return index a global or local index. 75 */ 76 public int getIndex() 77 { 78 return m_index; 79 } 80 81 /** 82 * Set whether or not this is a global reference. For advanced use only. 83 * 84 * @param isGlobal true if this should be a global variable reference. 85 */ 86 public void setIsGlobal(boolean isGlobal) 87 { 88 m_isGlobal = isGlobal; 89 } 90 91 /** 92 * Set the index for the variable into the stack. For advanced use only. 93 * 94 * @return true if this should be a global variable reference. 95 */ 96 public boolean getGlobal() 97 { 98 return m_isGlobal; 99 } 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(List<QName> 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 = vars.get(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 com.sun.org.apache.xml.internal.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 com.sun.org.apache.xml.internal.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 // com.sun.org.apache.xalan.internal.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 // J2SE does not support Xalan interpretive 261 /* 262 public com.sun.org.apache.xalan.internal.templates.ElemVariable getElemVariable() 263 { 264 265 // Get the current ElemTemplateElement, and then walk backwards in 266 // document order, searching 267 // for an xsl:param element or xsl:variable element that matches our 268 // qname. If we reach the top level, use the StylesheetRoot's composed 269 // list of top level variables and parameters. 270 271 com.sun.org.apache.xalan.internal.templates.ElemVariable vvar = null; 272 com.sun.org.apache.xpath.internal.ExpressionNode owner = getExpressionOwner(); 273 274 if (null != owner && owner instanceof com.sun.org.apache.xalan.internal.templates.ElemTemplateElement) 275 { 276 277 com.sun.org.apache.xalan.internal.templates.ElemTemplateElement prev = 278 (com.sun.org.apache.xalan.internal.templates.ElemTemplateElement) owner; 279 280 if (!(prev instanceof com.sun.org.apache.xalan.internal.templates.Stylesheet)) 281 { 282 while ( prev != null && !(prev.getParentNode() instanceof com.sun.org.apache.xalan.internal.templates.Stylesheet) ) 283 { 284 com.sun.org.apache.xalan.internal.templates.ElemTemplateElement savedprev = prev; 285 286 while (null != (prev = prev.getPreviousSiblingElem())) 287 { 288 if(prev instanceof com.sun.org.apache.xalan.internal.templates.ElemVariable) 289 { 290 vvar = (com.sun.org.apache.xalan.internal.templates.ElemVariable) prev; 291 292 if (vvar.getName().equals(m_qname)) 293 { 294 return vvar; 295 } 296 vvar = null; 297 } 298 } 299 prev = savedprev.getParentElem(); 300 } 301 } 302 if (prev != null) 303 vvar = prev.getStylesheetRoot().getVariableOrParamComposed(m_qname); 304 } 305 return vvar; 306 307 } 308 */ 309 /** 310 * Tell if this expression returns a stable number that will not change during 311 * iterations within the expression. This is used to determine if a proximity 312 * position predicate can indicate that no more searching has to occur. 313 * 314 * 315 * @return true if the expression represents a stable number. 316 */ 317 public boolean isStableNumber() 318 { 319 return true; 320 } 321 322 /** 323 * Get the analysis bits for this walker, as defined in the WalkerFactory. 324 * @return One of WalkerFactory#BIT_DESCENDANT, etc. 325 */ 326 public int getAnalysisBits() 327 { 328 329 // J2SE does not support Xalan interpretive 330 /* 331 com.sun.org.apache.xalan.internal.templates.ElemVariable vvar = getElemVariable(); 332 if(null != vvar) 333 { 334 XPath xpath = vvar.getSelect(); 335 if(null != xpath) 336 { 337 Expression expr = xpath.getExpression(); 338 if(null != expr && expr instanceof PathComponent) 339 { 340 return ((PathComponent)expr).getAnalysisBits(); 341 } 342 } 343 } 344 */ 345 346 return WalkerFactory.BIT_FILTER; 347 } 348 349 350 /** 351 * @see com.sun.org.apache.xpath.internal.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor) 352 */ 353 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) 354 { 355 visitor.visitVariableRef(owner, this); 356 } 357 /** 358 * @see Expression#deepEquals(Expression) 359 */ 360 public boolean deepEquals(Expression expr) 361 { 362 if(!isSameClass(expr)) 363 return false; 364 365 if(!m_qname.equals(((Variable)expr).m_qname)) 366 return false; 367 368 // J2SE does not support Xalan interpretive 369 /* 370 // We have to make sure that the qname really references 371 // the same variable element. 372 if(getElemVariable() != ((Variable)expr).getElemVariable()) 373 return false; 374 */ 375 376 return true; 377 } 378 379 static final java.lang.String PSUEDOVARNAMESPACE = "http://xml.apache.org/xalan/psuedovar"; 380 381 /** 382 * Tell if this is a psuedo variable reference, declared by Xalan instead 383 * of by the user. 384 */ 385 public boolean isPsuedoVarRef() 386 { 387 java.lang.String ns = m_qname.getNamespaceURI(); 388 if((null != ns) && ns.equals(PSUEDOVARNAMESPACE)) 389 { 390 if(m_qname.getLocalName().startsWith("#")) 391 return true; 392 } 393 return false; 394 } 395 396 397 }