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 }