1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xpath.internal.operations; 22 23 import com.sun.org.apache.xalan.internal.res.XSLMessages; 24 import com.sun.org.apache.xml.internal.utils.QName; 25 import com.sun.org.apache.xpath.internal.Expression; 26 import com.sun.org.apache.xpath.internal.ExpressionOwner; 27 import com.sun.org.apache.xpath.internal.XPathContext; 28 import com.sun.org.apache.xpath.internal.XPathVisitor; 29 import com.sun.org.apache.xpath.internal.axes.PathComponent; 30 import com.sun.org.apache.xpath.internal.axes.WalkerFactory; 31 import com.sun.org.apache.xpath.internal.objects.XNodeSet; 32 import com.sun.org.apache.xpath.internal.objects.XObject; 33 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; 34 import java.util.List; 35 import javax.xml.transform.TransformerException; 36 37 38 /** 39 * The variable reference expression executer. 40 * 41 * @LastModified: Oct 2017 42 */ 43 public class Variable extends Expression implements PathComponent 44 { 45 static final long serialVersionUID = -4334975375609297049L; 46 /** Tell if fixupVariables was called. 47 * @serial */ 48 private boolean m_fixUpWasCalled = false; 49 50 /** The qualified name of the variable. 51 * @serial */ 52 protected QName m_qname; 53 54 /** 55 * The index of the variable, which is either an absolute index to a 56 * global, or, if higher than the globals area, must be adjusted by adding 57 * the offset to the current stack frame. 58 */ 59 protected int m_index; 60 61 /** 62 * Set the index for the variable into the stack. For advanced use only. You 63 * must know what you are doing to use this. 64 * 65 * @param index a global or local index. 66 */ 67 public void setIndex(int index) 68 { 69 m_index = index; 70 } 71 72 /** 73 * Set the index for the variable into the stack. For advanced use only. 74 * 75 * @return index a global or local index. 76 */ 77 public int getIndex() 78 { 79 return m_index; 80 } 81 82 /** 83 * Set whether or not this is a global reference. For advanced use only. 84 * 85 * @param isGlobal true if this should be a global variable reference. 86 */ 87 public void setIsGlobal(boolean isGlobal) 88 { 89 m_isGlobal = isGlobal; 90 } 91 92 /** 93 * Set the index for the variable into the stack. For advanced use only. 94 * 95 * @return true if this should be a global variable reference. 96 */ 97 public boolean getGlobal() 98 { 99 return m_isGlobal; 100 } 101 102 103 104 105 106 protected boolean m_isGlobal = false; 107 108 /** 109 * This function is used to fixup variables from QNames to stack frame 110 * indexes at stylesheet build time. 111 * @param vars List of QNames that correspond to variables. This list 112 * should be searched backwards for the first qualified name that 113 * corresponds to the variable reference qname. The position of the 114 * QName in the vector from the start of the vector will be its position 115 * in the stack frame (but variables above the globalsTop value will need 116 * to be offset to the current stack frame). 117 */ 118 public void fixupVariables(List<QName> vars, int globalsSize) 119 { 120 m_fixUpWasCalled = true; 121 int sz = vars.size(); 122 123 for (int i = vars.size()-1; i >= 0; i--) 124 { 125 QName qn = vars.get(i); 126 // System.out.println("qn: "+qn); 127 if(qn.equals(m_qname)) 128 { 129 130 if(i < globalsSize) 131 { 132 m_isGlobal = true; 133 m_index = i; 134 } 135 else 136 { 137 m_index = i-globalsSize; 138 } 139 140 return; 141 } 142 } 143 144 java.lang.String msg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_COULD_NOT_FIND_VAR, 145 new Object[]{m_qname.toString()}); 146 147 TransformerException te = new TransformerException(msg, this); 148 149 throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(te); 150 151 } 152 153 154 /** 155 * Set the qualified name of the variable. 156 * 157 * @param qname Must be a non-null reference to a qualified name. 158 */ 159 public void setQName(QName qname) 160 { 161 m_qname = qname; 162 } 163 164 /** 165 * Get the qualified name of the variable. 166 * 167 * @return A non-null reference to a qualified name. 168 */ 169 public QName getQName() 170 { 171 return m_qname; 172 } 173 174 /** 175 * Execute an expression in the XPath runtime context, and return the 176 * result of the expression. 177 * 178 * 179 * @param xctxt The XPath runtime context. 180 * 181 * @return The result of the expression in the form of a <code>XObject</code>. 182 * 183 * @throws javax.xml.transform.TransformerException if a runtime exception 184 * occurs. 185 */ 186 public XObject execute(XPathContext xctxt) 187 throws javax.xml.transform.TransformerException 188 { 189 return execute(xctxt, false); 190 } 191 192 193 /** 194 * Dereference the variable, and return the reference value. Note that lazy 195 * evaluation will occur. If a variable within scope is not found, a warning 196 * will be sent to the error listener, and an empty nodeset will be returned. 197 * 198 * 199 * @param xctxt The runtime execution context. 200 * 201 * @return The evaluated variable, or an empty nodeset if not found. 202 * 203 * @throws javax.xml.transform.TransformerException 204 */ 205 public XObject execute(XPathContext xctxt, boolean destructiveOK) throws javax.xml.transform.TransformerException 206 { 207 com.sun.org.apache.xml.internal.utils.PrefixResolver xprefixResolver = xctxt.getNamespaceContext(); 208 209 XObject result; 210 // Is the variable fetched always the same? 211 // XObject result = xctxt.getVariable(m_qname); 212 if(m_fixUpWasCalled) 213 { 214 if(m_isGlobal) 215 result = xctxt.getVarStack().getGlobalVariable(xctxt, m_index, destructiveOK); 216 else 217 result = xctxt.getVarStack().getLocalVariable(xctxt, m_index, destructiveOK); 218 } 219 else { 220 result = xctxt.getVarStack().getVariableOrParam(xctxt,m_qname); 221 } 222 223 if (null == result) 224 { 225 // This should now never happen... 226 warn(xctxt, XPATHErrorResources.WG_ILLEGAL_VARIABLE_REFERENCE, 227 new Object[]{ m_qname.getLocalPart() }); //"VariableReference given for variable out "+ 228 // (new RuntimeException()).printStackTrace(); 229 // error(xctxt, XPATHErrorResources.ER_COULDNOT_GET_VAR_NAMED, 230 // new Object[]{ m_qname.getLocalPart() }); //"Could not get variable named "+varName); 231 232 result = new XNodeSet(xctxt.getDTMManager()); 233 } 234 235 return result; 236 // } 237 // else 238 // { 239 // // Hack city... big time. This is needed to evaluate xpaths from extensions, 240 // // pending some bright light going off in my head. Some sort of callback? 241 // synchronized(this) 242 // { 243 // com.sun.org.apache.xalan.internal.templates.ElemVariable vvar= getElemVariable(); 244 // if(null != vvar) 245 // { 246 // m_index = vvar.getIndex(); 247 // m_isGlobal = vvar.getIsTopLevel(); 248 // m_fixUpWasCalled = true; 249 // return execute(xctxt); 250 // } 251 // } 252 // throw new javax.xml.transform.TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VAR_NOT_RESOLVABLE, new Object[]{m_qname.toString()})); //"Variable not resolvable: "+m_qname); 253 // } 254 } 255 256 /** 257 * Get the XSLT ElemVariable that this sub-expression references. In order for 258 * this to work, the SourceLocator must be the owning ElemTemplateElement. 259 * @return The dereference to the ElemVariable, or null if not found. 260 */ 261 // J2SE does not support Xalan interpretive 262 /* 263 public com.sun.org.apache.xalan.internal.templates.ElemVariable getElemVariable() 264 { 265 266 // Get the current ElemTemplateElement, and then walk backwards in 267 // document order, searching 268 // for an xsl:param element or xsl:variable element that matches our 269 // qname. If we reach the top level, use the StylesheetRoot's composed 270 // list of top level variables and parameters. 271 272 com.sun.org.apache.xalan.internal.templates.ElemVariable vvar = null; 273 com.sun.org.apache.xpath.internal.ExpressionNode owner = getExpressionOwner(); 274 275 if (null != owner && owner instanceof com.sun.org.apache.xalan.internal.templates.ElemTemplateElement) 276 { 277 278 com.sun.org.apache.xalan.internal.templates.ElemTemplateElement prev = 279 (com.sun.org.apache.xalan.internal.templates.ElemTemplateElement) owner; 280 281 if (!(prev instanceof com.sun.org.apache.xalan.internal.templates.Stylesheet)) 282 { 283 while ( prev != null && !(prev.getParentNode() instanceof com.sun.org.apache.xalan.internal.templates.Stylesheet) ) 284 { 285 com.sun.org.apache.xalan.internal.templates.ElemTemplateElement savedprev = prev; 286 287 while (null != (prev = prev.getPreviousSiblingElem())) 288 { 289 if(prev instanceof com.sun.org.apache.xalan.internal.templates.ElemVariable) 290 { 291 vvar = (com.sun.org.apache.xalan.internal.templates.ElemVariable) prev; 292 293 if (vvar.getName().equals(m_qname)) 294 { 295 return vvar; 296 } 297 vvar = null; 298 } 299 } 300 prev = savedprev.getParentElem(); 301 } 302 } 303 if (prev != null) 304 vvar = prev.getStylesheetRoot().getVariableOrParamComposed(m_qname); 305 } 306 return vvar; 307 308 } 309 */ 310 /** 311 * Tell if this expression returns a stable number that will not change during 312 * iterations within the expression. This is used to determine if a proximity 313 * position predicate can indicate that no more searching has to occur. 314 * 315 * 316 * @return true if the expression represents a stable number. 317 */ 318 public boolean isStableNumber() 319 { 320 return true; 321 } 322 323 /** 324 * Get the analysis bits for this walker, as defined in the WalkerFactory. 325 * @return One of WalkerFactory#BIT_DESCENDANT, etc. 326 */ 327 public int getAnalysisBits() 328 { 329 330 // J2SE does not support Xalan interpretive 331 /* 332 com.sun.org.apache.xalan.internal.templates.ElemVariable vvar = getElemVariable(); 333 if(null != vvar) 334 { 335 XPath xpath = vvar.getSelect(); 336 if(null != xpath) 337 { 338 Expression expr = xpath.getExpression(); 339 if(null != expr && expr instanceof PathComponent) 340 { 341 return ((PathComponent)expr).getAnalysisBits(); 342 } 343 } 344 } 345 */ 346 347 return WalkerFactory.BIT_FILTER; 348 } 349 350 351 /** 352 * @see com.sun.org.apache.xpath.internal.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor) 353 */ 354 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) 355 { 356 visitor.visitVariableRef(owner, this); 357 } 358 /** 359 * @see Expression#deepEquals(Expression) 360 */ 361 public boolean deepEquals(Expression expr) 362 { 363 if(!isSameClass(expr)) 364 return false; 365 366 if(!m_qname.equals(((Variable)expr).m_qname)) 367 return false; 368 369 // J2SE does not support Xalan interpretive 370 /* 371 // We have to make sure that the qname really references 372 // the same variable element. 373 if(getElemVariable() != ((Variable)expr).getElemVariable()) 374 return false; 375 */ 376 377 return true; 378 } 379 380 static final java.lang.String PSUEDOVARNAMESPACE = "http://xml.apache.org/xalan/psuedovar"; 381 382 /** 383 * Tell if this is a psuedo variable reference, declared by Xalan instead 384 * of by the user. 385 */ 386 public boolean isPsuedoVarRef() 387 { 388 java.lang.String ns = m_qname.getNamespaceURI(); 389 if((null != ns) && ns.equals(PSUEDOVARNAMESPACE)) 390 { 391 if(m_qname.getLocalName().startsWith("#")) 392 return true; 393 } 394 return false; 395 } 396 397 398 }