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: FilterParentPath.java 468650 2006-10-28 07:03:30Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import org.apache.bcel.generic.ALOAD;
025    import org.apache.bcel.generic.ASTORE;
026    import org.apache.bcel.generic.ConstantPoolGen;
027    import org.apache.bcel.generic.INVOKEINTERFACE;
028    import org.apache.bcel.generic.INVOKESPECIAL;
029    import org.apache.bcel.generic.INVOKEVIRTUAL;
030    import org.apache.bcel.generic.InstructionList;
031    import org.apache.bcel.generic.LocalVariableGen;
032    import org.apache.bcel.generic.NEW;
033    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
034    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
035    import org.apache.xalan.xsltc.compiler.util.NodeSetType;
036    import org.apache.xalan.xsltc.compiler.util.NodeType;
037    import org.apache.xalan.xsltc.compiler.util.ReferenceType;
038    import org.apache.xalan.xsltc.compiler.util.Type;
039    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
040    import org.apache.xalan.xsltc.compiler.util.Util;
041    
042    /**
043     * @author Jacek Ambroziak
044     * @author Santiago Pericas-Geertsen
045     */
046    final class FilterParentPath extends Expression {
047    
048        private Expression _filterExpr;
049        private Expression _path;
050        private boolean _hasDescendantAxis = false;
051    
052        public FilterParentPath(Expression filterExpr, Expression path) {
053            (_path = path).setParent(this);
054            (_filterExpr = filterExpr).setParent(this);
055        }
056                    
057        public void setParser(Parser parser) {
058            super.setParser(parser);
059            _filterExpr.setParser(parser);
060            _path.setParser(parser);
061        }
062        
063        public String toString() {
064            return "FilterParentPath(" + _filterExpr + ", " + _path + ')';
065        }
066    
067        public void setDescendantAxis() {
068            _hasDescendantAxis = true;
069        }
070    
071        /**
072         * Type check a FilterParentPath. If the filter is not a node-set add a 
073         * cast to node-set only if it is of reference type. This type coercion is
074         * needed for expressions like $x/LINE where $x is a parameter reference.
075         */
076        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
077            final Type ftype = _filterExpr.typeCheck(stable);
078            if (ftype instanceof NodeSetType == false) {
079                if (ftype instanceof ReferenceType)  {
080                    _filterExpr = new CastExpr(_filterExpr, Type.NodeSet);
081                }
082                /*
083                else if (ftype instanceof ResultTreeType)  {
084                    _filterExpr = new CastExpr(_filterExpr, Type.NodeSet);
085                }
086                */
087                else if (ftype instanceof NodeType)  {
088                    _filterExpr = new CastExpr(_filterExpr, Type.NodeSet);
089                }
090                else {
091                    throw new TypeCheckError(this);
092                }
093            }
094    
095            // Wrap single node path in a node set
096            final Type ptype = _path.typeCheck(stable);
097            if (!(ptype instanceof NodeSetType)) {
098                _path = new CastExpr(_path, Type.NodeSet);
099            }
100    
101            return _type = Type.NodeSet;    
102        }
103            
104        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
105            final ConstantPoolGen cpg = classGen.getConstantPool();
106            final InstructionList il = methodGen.getInstructionList();
107            // Create new StepIterator
108            final int initSI = cpg.addMethodref(STEP_ITERATOR_CLASS,
109                                                "<init>",
110                                                "("
111                                                +NODE_ITERATOR_SIG
112                                                +NODE_ITERATOR_SIG
113                                                +")V");
114    
115            // Backwards branches are prohibited if an uninitialized object is
116            // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed.
117            // We don't know whether this code might contain backwards branches,
118            // so we mustn't create the new object until after we've created
119            // the suspect arguments to its constructor.  Instead we calculate
120            // the values of the arguments to the constructor first, store them
121            // in temporary variables, create the object and reload the
122            // arguments from the temporaries to avoid the problem.
123    
124            // Recursively compile 2 iterators
125            _filterExpr.translate(classGen, methodGen);
126            LocalVariableGen filterTemp =
127                    methodGen.addLocalVariable("filter_parent_path_tmp1",
128                                               Util.getJCRefType(NODE_ITERATOR_SIG),
129                                               null, null);
130            filterTemp.setStart(il.append(new ASTORE(filterTemp.getIndex())));
131    
132            _path.translate(classGen, methodGen);
133            LocalVariableGen pathTemp =
134                    methodGen.addLocalVariable("filter_parent_path_tmp2",
135                                               Util.getJCRefType(NODE_ITERATOR_SIG),
136                                               null, null);
137            pathTemp.setStart(il.append(new ASTORE(pathTemp.getIndex())));
138    
139            il.append(new NEW(cpg.addClass(STEP_ITERATOR_CLASS)));
140            il.append(DUP);
141            filterTemp.setEnd(il.append(new ALOAD(filterTemp.getIndex())));
142            pathTemp.setEnd(il.append(new ALOAD(pathTemp.getIndex())));
143    
144            // Initialize StepIterator with iterators from the stack
145            il.append(new INVOKESPECIAL(initSI));
146    
147            // This is a special case for the //* path with or without predicates
148            if (_hasDescendantAxis) {
149                final int incl = cpg.addMethodref(NODE_ITERATOR_BASE,
150                                                  "includeSelf",
151                                                  "()" + NODE_ITERATOR_SIG);
152                il.append(new INVOKEVIRTUAL(incl));
153            }
154    
155            SyntaxTreeNode parent = getParent();
156    
157            boolean parentAlreadyOrdered = 
158                (parent instanceof RelativeLocationPath)
159                    || (parent instanceof FilterParentPath)
160                    || (parent instanceof KeyCall)
161                    || (parent instanceof CurrentCall)
162                    || (parent instanceof DocumentCall);
163    
164            if (!parentAlreadyOrdered) {
165                final int order = cpg.addInterfaceMethodref(DOM_INTF,
166                                                            ORDER_ITERATOR,
167                                                            ORDER_ITERATOR_SIG);
168                il.append(methodGen.loadDOM());
169                il.append(SWAP);
170                il.append(methodGen.loadContextNode());
171                il.append(new INVOKEINTERFACE(order, 3));
172            }
173        }
174    }