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    }