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: Step.java 468650 2006-10-28 07:03:30Z minchau $ 020 */ 021 022 package org.apache.xalan.xsltc.compiler; 023 024 import java.util.Vector; 025 026 import org.apache.bcel.generic.ALOAD; 027 import org.apache.bcel.generic.ASTORE; 028 import org.apache.bcel.generic.CHECKCAST; 029 import org.apache.bcel.generic.ConstantPoolGen; 030 import org.apache.bcel.generic.ICONST; 031 import org.apache.bcel.generic.ILOAD; 032 import org.apache.bcel.generic.ISTORE; 033 import org.apache.bcel.generic.INVOKEINTERFACE; 034 import org.apache.bcel.generic.INVOKESPECIAL; 035 import org.apache.bcel.generic.InstructionList; 036 import org.apache.bcel.generic.LocalVariableGen; 037 import org.apache.bcel.generic.NEW; 038 import org.apache.bcel.generic.PUSH; 039 import org.apache.xalan.xsltc.DOM; 040 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 041 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 042 import org.apache.xalan.xsltc.compiler.util.Type; 043 import org.apache.xalan.xsltc.compiler.util.TypeCheckError; 044 import org.apache.xalan.xsltc.compiler.util.Util; 045 import org.apache.xml.dtm.Axis; 046 import org.apache.xml.dtm.DTM; 047 048 /** 049 * @author Jacek Ambroziak 050 * @author Santiago Pericas-Geertsen 051 * @author Morten Jorgensen 052 */ 053 final class Step extends RelativeLocationPath { 054 055 /** 056 * This step's axis as defined in class Axis. 057 */ 058 private int _axis; 059 060 /** 061 * A vector of predicates (filters) defined on this step - may be null 062 */ 063 private Vector _predicates; 064 065 /** 066 * Some simple predicates can be handled by this class (and not by the 067 * Predicate class) and will be removed from the above vector as they are 068 * handled. We use this boolean to remember if we did have any predicates. 069 */ 070 private boolean _hadPredicates = false; 071 072 /** 073 * Type of the node test. 074 */ 075 private int _nodeType; 076 077 public Step(int axis, int nodeType, Vector predicates) { 078 _axis = axis; 079 _nodeType = nodeType; 080 _predicates = predicates; 081 } 082 083 /** 084 * Set the parser for this element and all child predicates 085 */ 086 public void setParser(Parser parser) { 087 super.setParser(parser); 088 if (_predicates != null) { 089 final int n = _predicates.size(); 090 for (int i = 0; i < n; i++) { 091 final Predicate exp = (Predicate)_predicates.elementAt(i); 092 exp.setParser(parser); 093 exp.setParent(this); 094 } 095 } 096 } 097 098 /** 099 * Define the axis (defined in Axis class) for this step 100 */ 101 public int getAxis() { 102 return _axis; 103 } 104 105 /** 106 * Get the axis (defined in Axis class) for this step 107 */ 108 public void setAxis(int axis) { 109 _axis = axis; 110 } 111 112 /** 113 * Returns the node-type for this step 114 */ 115 public int getNodeType() { 116 return _nodeType; 117 } 118 119 /** 120 * Returns the vector containing all predicates for this step. 121 */ 122 public Vector getPredicates() { 123 return _predicates; 124 } 125 126 /** 127 * Returns the vector containing all predicates for this step. 128 */ 129 public void addPredicates(Vector predicates) { 130 if (_predicates == null) { 131 _predicates = predicates; 132 } 133 else { 134 _predicates.addAll(predicates); 135 } 136 } 137 138 /** 139 * Returns 'true' if this step has a parent pattern. 140 * This method will return 'false' if this step occurs on its own under 141 * an element like <xsl:for-each> or <xsl:apply-templates>. 142 */ 143 private boolean hasParentPattern() { 144 final SyntaxTreeNode parent = getParent(); 145 return (parent instanceof ParentPattern || 146 parent instanceof ParentLocationPath || 147 parent instanceof UnionPathExpr || 148 parent instanceof FilterParentPath); 149 } 150 151 /** 152 * Returns 'true' if this step has any predicates 153 */ 154 private boolean hasPredicates() { 155 return _predicates != null && _predicates.size() > 0; 156 } 157 158 /** 159 * Returns 'true' if this step is used within a predicate 160 */ 161 private boolean isPredicate() { 162 SyntaxTreeNode parent = this; 163 while (parent != null) { 164 parent = parent.getParent(); 165 if (parent instanceof Predicate) return true; 166 } 167 return false; 168 } 169 170 /** 171 * True if this step is the abbreviated step '.' 172 */ 173 public boolean isAbbreviatedDot() { 174 return _nodeType == NodeTest.ANODE && _axis == Axis.SELF; 175 } 176 177 178 /** 179 * True if this step is the abbreviated step '..' 180 */ 181 public boolean isAbbreviatedDDot() { 182 return _nodeType == NodeTest.ANODE && _axis == Axis.PARENT; 183 } 184 185 /** 186 * Type check this step. The abbreviated steps '.' and '@attr' are 187 * assigned type node if they have no predicates. All other steps 188 * have type node-set. 189 */ 190 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 191 192 // Save this value for later - important for testing for special 193 // combinations of steps and patterns than can be optimised 194 _hadPredicates = hasPredicates(); 195 196 // Special case for '.' 197 // in the case where '.' has a context such as book/. 198 // or .[false()] we can not optimize the nodeset to a single node. 199 if (isAbbreviatedDot()) { 200 _type = (hasParentPattern() || hasPredicates() ) ? 201 Type.NodeSet : Type.Node; 202 } 203 else { 204 _type = Type.NodeSet; 205 } 206 207 // Type check all predicates (expressions applied to the step) 208 if (_predicates != null) { 209 final int n = _predicates.size(); 210 for (int i = 0; i < n; i++) { 211 final Expression pred = (Expression)_predicates.elementAt(i); 212 pred.typeCheck(stable); 213 } 214 } 215 216 // Return either Type.Node or Type.NodeSet 217 return _type; 218 } 219 220 /** 221 * Translate a step by pushing the appropriate iterator onto the stack. 222 * The abbreviated steps '.' and '@attr' do not create new iterators 223 * if they are not part of a LocationPath and have no filters. 224 * In these cases a node index instead of an iterator is pushed 225 * onto the stack. 226 */ 227 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 228 final ConstantPoolGen cpg = classGen.getConstantPool(); 229 final InstructionList il = methodGen.getInstructionList(); 230 231 if (hasPredicates()) { 232 translatePredicates(classGen, methodGen); 233 } else { 234 int star = 0; 235 String name = null; 236 final XSLTC xsltc = getParser().getXSLTC(); 237 238 if (_nodeType >= DTM.NTYPES) { 239 final Vector ni = xsltc.getNamesIndex(); 240 241 name = (String)ni.elementAt(_nodeType-DTM.NTYPES); 242 star = name.lastIndexOf('*'); 243 } 244 245 // If it is an attribute, but not '@*', '@pre:*' or '@node()', 246 // and has no parent 247 if (_axis == Axis.ATTRIBUTE && _nodeType != NodeTest.ATTRIBUTE 248 && _nodeType != NodeTest.ANODE && !hasParentPattern() 249 && star == 0) 250 { 251 int iter = cpg.addInterfaceMethodref(DOM_INTF, 252 "getTypedAxisIterator", 253 "(II)"+NODE_ITERATOR_SIG); 254 il.append(methodGen.loadDOM()); 255 il.append(new PUSH(cpg, Axis.ATTRIBUTE)); 256 il.append(new PUSH(cpg, _nodeType)); 257 il.append(new INVOKEINTERFACE(iter, 3)); 258 return; 259 } 260 261 SyntaxTreeNode parent = getParent(); 262 // Special case for '.' 263 if (isAbbreviatedDot()) { 264 if (_type == Type.Node) { 265 // Put context node on stack if using Type.Node 266 il.append(methodGen.loadContextNode()); 267 } 268 else { 269 if (parent instanceof ParentLocationPath){ 270 // Wrap the context node in a singleton iterator if not. 271 int init = cpg.addMethodref(SINGLETON_ITERATOR, 272 "<init>", 273 "("+NODE_SIG+")V"); 274 il.append(new NEW(cpg.addClass(SINGLETON_ITERATOR))); 275 il.append(DUP); 276 il.append(methodGen.loadContextNode()); 277 il.append(new INVOKESPECIAL(init)); 278 } else { 279 // DOM.getAxisIterator(int axis); 280 int git = cpg.addInterfaceMethodref(DOM_INTF, 281 "getAxisIterator", 282 "(I)"+NODE_ITERATOR_SIG); 283 il.append(methodGen.loadDOM()); 284 il.append(new PUSH(cpg, _axis)); 285 il.append(new INVOKEINTERFACE(git, 2)); 286 } 287 } 288 return; 289 } 290 291 // Special case for /foo/*/bar 292 if ((parent instanceof ParentLocationPath) && 293 (parent.getParent() instanceof ParentLocationPath)) { 294 if ((_nodeType == NodeTest.ELEMENT) && (!_hadPredicates)) { 295 _nodeType = NodeTest.ANODE; 296 } 297 } 298 299 // "ELEMENT" or "*" or "@*" or ".." or "@attr" with a parent. 300 switch (_nodeType) { 301 case NodeTest.ATTRIBUTE: 302 _axis = Axis.ATTRIBUTE; 303 case NodeTest.ANODE: 304 // DOM.getAxisIterator(int axis); 305 int git = cpg.addInterfaceMethodref(DOM_INTF, 306 "getAxisIterator", 307 "(I)"+NODE_ITERATOR_SIG); 308 il.append(methodGen.loadDOM()); 309 il.append(new PUSH(cpg, _axis)); 310 il.append(new INVOKEINTERFACE(git, 2)); 311 break; 312 default: 313 if (star > 1) { 314 final String namespace; 315 if (_axis == Axis.ATTRIBUTE) 316 namespace = name.substring(0,star-2); 317 else 318 namespace = name.substring(0,star-1); 319 320 final int nsType = xsltc.registerNamespace(namespace); 321 final int ns = cpg.addInterfaceMethodref(DOM_INTF, 322 "getNamespaceAxisIterator", 323 "(II)"+NODE_ITERATOR_SIG); 324 il.append(methodGen.loadDOM()); 325 il.append(new PUSH(cpg, _axis)); 326 il.append(new PUSH(cpg, nsType)); 327 il.append(new INVOKEINTERFACE(ns, 3)); 328 break; 329 } 330 case NodeTest.ELEMENT: 331 // DOM.getTypedAxisIterator(int axis, int type); 332 final int ty = cpg.addInterfaceMethodref(DOM_INTF, 333 "getTypedAxisIterator", 334 "(II)"+NODE_ITERATOR_SIG); 335 // Get the typed iterator we're after 336 il.append(methodGen.loadDOM()); 337 il.append(new PUSH(cpg, _axis)); 338 il.append(new PUSH(cpg, _nodeType)); 339 il.append(new INVOKEINTERFACE(ty, 3)); 340 341 break; 342 } 343 } 344 } 345 346 347 /** 348 * Translate a sequence of predicates. Each predicate is translated 349 * by constructing an instance of <code>CurrentNodeListIterator</code> 350 * which is initialized from another iterator (recursive call), 351 * a filter and a closure (call to translate on the predicate) and "this". 352 */ 353 public void translatePredicates(ClassGenerator classGen, 354 MethodGenerator methodGen) { 355 final ConstantPoolGen cpg = classGen.getConstantPool(); 356 final InstructionList il = methodGen.getInstructionList(); 357 358 int idx = 0; 359 360 if (_predicates.size() == 0) { 361 translate(classGen, methodGen); 362 } 363 else { 364 final Predicate predicate = (Predicate)_predicates.lastElement(); 365 _predicates.remove(predicate); 366 367 // Special case for predicates that can use the NodeValueIterator 368 // instead of an auxiliary class. Certain path/predicates pairs 369 // are translated into a base path, on top of which we place a 370 // node value iterator that tests for the desired value: 371 // foo[@attr = 'str'] -> foo/@attr + test(value='str') 372 // foo[bar = 'str'] -> foo/bar + test(value='str') 373 // foo/bar[. = 'str'] -> foo/bar + test(value='str') 374 if (predicate.isNodeValueTest()) { 375 Step step = predicate.getStep(); 376 377 il.append(methodGen.loadDOM()); 378 // If the predicate's Step is simply '.' we translate this Step 379 // and place the node test on top of the resulting iterator 380 if (step.isAbbreviatedDot()) { 381 translate(classGen, methodGen); 382 il.append(new ICONST(DOM.RETURN_CURRENT)); 383 } 384 // Otherwise we create a parent location path with this Step and 385 // the predicates Step, and place the node test on top of that 386 else { 387 ParentLocationPath path = new ParentLocationPath(this,step); 388 try { 389 path.typeCheck(getParser().getSymbolTable()); 390 } 391 catch (TypeCheckError e) { } 392 path.translate(classGen, methodGen); 393 il.append(new ICONST(DOM.RETURN_PARENT)); 394 } 395 predicate.translate(classGen, methodGen); 396 idx = cpg.addInterfaceMethodref(DOM_INTF, 397 GET_NODE_VALUE_ITERATOR, 398 GET_NODE_VALUE_ITERATOR_SIG); 399 il.append(new INVOKEINTERFACE(idx, 5)); 400 } 401 // Handle '//*[n]' expression 402 else if (predicate.isNthDescendant()) { 403 il.append(methodGen.loadDOM()); 404 // il.append(new ICONST(NodeTest.ELEMENT)); 405 il.append(new ICONST(predicate.getPosType())); 406 predicate.translate(classGen, methodGen); 407 il.append(new ICONST(0)); 408 idx = cpg.addInterfaceMethodref(DOM_INTF, 409 "getNthDescendant", 410 "(IIZ)"+NODE_ITERATOR_SIG); 411 il.append(new INVOKEINTERFACE(idx, 4)); 412 } 413 // Handle 'elem[n]' expression 414 else if (predicate.isNthPositionFilter()) { 415 idx = cpg.addMethodref(NTH_ITERATOR_CLASS, 416 "<init>", 417 "("+NODE_ITERATOR_SIG+"I)V"); 418 419 // Backwards branches are prohibited if an uninitialized object 420 // is on the stack by section 4.9.4 of the JVM Specification, 421 // 2nd Ed. We don't know whether this code might contain 422 // backwards branches, so we mustn't create the new object until 423 // after we've created the suspect arguments to its constructor. 424 // Instead we calculate the values of the arguments to the 425 // constructor first, store them in temporary variables, create 426 // the object and reload the arguments from the temporaries to 427 // avoid the problem. 428 translatePredicates(classGen, methodGen); // recursive call 429 LocalVariableGen iteratorTemp 430 = methodGen.addLocalVariable("step_tmp1", 431 Util.getJCRefType(NODE_ITERATOR_SIG), 432 null, null); 433 iteratorTemp.setStart( 434 il.append(new ASTORE(iteratorTemp.getIndex()))); 435 436 predicate.translate(classGen, methodGen); 437 LocalVariableGen predicateValueTemp 438 = methodGen.addLocalVariable("step_tmp2", 439 Util.getJCRefType("I"), 440 null, null); 441 predicateValueTemp.setStart( 442 il.append(new ISTORE(predicateValueTemp.getIndex()))); 443 444 il.append(new NEW(cpg.addClass(NTH_ITERATOR_CLASS))); 445 il.append(DUP); 446 iteratorTemp.setEnd( 447 il.append(new ALOAD(iteratorTemp.getIndex()))); 448 predicateValueTemp.setEnd( 449 il.append(new ILOAD(predicateValueTemp.getIndex()))); 450 il.append(new INVOKESPECIAL(idx)); 451 } 452 else { 453 idx = cpg.addMethodref(CURRENT_NODE_LIST_ITERATOR, 454 "<init>", 455 "(" 456 + NODE_ITERATOR_SIG 457 + CURRENT_NODE_LIST_FILTER_SIG 458 + NODE_SIG 459 + TRANSLET_SIG 460 + ")V"); 461 462 // Backwards branches are prohibited if an uninitialized object 463 // is on the stack by section 4.9.4 of the JVM Specification, 464 // 2nd Ed. We don't know whether this code might contain 465 // backwards branches, so we mustn't create the new object until 466 // after we've created the suspect arguments to its constructor. 467 // Instead we calculate the values of the arguments to the 468 // constructor first, store them in temporary variables, create 469 // the object and reload the arguments from the temporaries to 470 // avoid the problem. 471 translatePredicates(classGen, methodGen); // recursive call 472 LocalVariableGen iteratorTemp 473 = methodGen.addLocalVariable("step_tmp1", 474 Util.getJCRefType(NODE_ITERATOR_SIG), 475 null, null); 476 iteratorTemp.setStart( 477 il.append(new ASTORE(iteratorTemp.getIndex()))); 478 479 predicate.translateFilter(classGen, methodGen); 480 LocalVariableGen filterTemp 481 = methodGen.addLocalVariable("step_tmp2", 482 Util.getJCRefType(CURRENT_NODE_LIST_FILTER_SIG), 483 null, null); 484 filterTemp.setStart( 485 il.append(new ASTORE(filterTemp.getIndex()))); 486 487 // create new CurrentNodeListIterator 488 il.append(new NEW(cpg.addClass(CURRENT_NODE_LIST_ITERATOR))); 489 il.append(DUP); 490 491 iteratorTemp.setEnd( 492 il.append(new ALOAD(iteratorTemp.getIndex()))); 493 filterTemp.setEnd(il.append(new ALOAD(filterTemp.getIndex()))); 494 495 il.append(methodGen.loadCurrentNode()); 496 il.append(classGen.loadTranslet()); 497 if (classGen.isExternal()) { 498 final String className = classGen.getClassName(); 499 il.append(new CHECKCAST(cpg.addClass(className))); 500 } 501 il.append(new INVOKESPECIAL(idx)); 502 } 503 } 504 } 505 506 /** 507 * Returns a string representation of this step. 508 */ 509 public String toString() { 510 final StringBuffer buffer = new StringBuffer("step(\""); 511 buffer.append(Axis.getNames(_axis)).append("\", ").append(_nodeType); 512 if (_predicates != null) { 513 final int n = _predicates.size(); 514 for (int i = 0; i < n; i++) { 515 final Predicate pred = (Predicate)_predicates.elementAt(i); 516 buffer.append(", ").append(pred.toString()); 517 } 518 } 519 return buffer.append(')').toString(); 520 } 521 }