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: FilterExpr.java 1225842 2011-12-30 15:14:35Z mrglavas $
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.ConstantPoolGen;
029    import org.apache.bcel.generic.ILOAD;
030    import org.apache.bcel.generic.INVOKESPECIAL;
031    import org.apache.bcel.generic.ISTORE;
032    import org.apache.bcel.generic.InstructionList;
033    import org.apache.bcel.generic.LocalVariableGen;
034    import org.apache.bcel.generic.NEW;
035    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
036    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
037    import org.apache.xalan.xsltc.compiler.util.NodeSetType;
038    import org.apache.xalan.xsltc.compiler.util.ReferenceType;
039    import org.apache.xalan.xsltc.compiler.util.Type;
040    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
041    import org.apache.xalan.xsltc.compiler.util.Util;
042    
043    /**
044     * @author Jacek Ambroziak
045     * @author Santiago Pericas-Geertsen
046     * @author Morten Jorgensen
047     */
048    class FilterExpr extends Expression {
049        
050        /**
051         * Primary expression of this filter. I.e., 'e' in '(e)[p1]...[pn]'.
052         */
053        private Expression   _primary;
054        
055        /**
056         * Array of predicates in '(e)[p1]...[pn]'.
057         */
058        private final Vector _predicates;
059    
060        public FilterExpr(Expression primary, Vector predicates) {
061            _primary = primary;
062            _predicates = predicates;
063            primary.setParent(this);
064        }
065    
066        protected Expression getExpr() {
067            if (_primary instanceof CastExpr)
068                return ((CastExpr)_primary).getExpr();
069            else
070                return _primary;
071        }
072    
073        public void setParser(Parser parser) {
074            super.setParser(parser);
075            _primary.setParser(parser);
076            if (_predicates != null) {
077                final int n = _predicates.size();
078                for (int i = 0; i < n; i++) {
079                    final Expression exp = (Expression)_predicates.elementAt(i);
080                    exp.setParser(parser);
081                    exp.setParent(this);
082                }
083            }
084        }
085        
086        public String toString() {
087            return "filter-expr(" + _primary + ", " + _predicates + ")";
088        }
089    
090        /**
091         * Type check a FilterParentPath. If the filter is not a node-set add a 
092         * cast to node-set only if it is of reference type. This type coercion 
093         * is needed for expressions like $x where $x is a parameter reference.
094         * All optimizations are turned off before type checking underlying
095         * predicates.
096         */
097        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
098            Type ptype = _primary.typeCheck(stable);
099            boolean canOptimize = _primary instanceof KeyCall;
100    
101            if (ptype instanceof NodeSetType == false) {
102                if (ptype instanceof ReferenceType)  {
103                    _primary = new CastExpr(_primary, Type.NodeSet);
104                }
105                else {
106                    throw new TypeCheckError(this);
107                }
108            }
109    
110            // Type check predicates and turn all optimizations off if appropriate
111            int n = _predicates.size();
112            for (int i = 0; i < n; i++) {
113                Predicate pred = (Predicate) _predicates.elementAt(i);
114    
115                if (!canOptimize) {
116                    pred.dontOptimize();
117                }
118                pred.typeCheck(stable);
119            }
120            return _type = Type.NodeSet;    
121        }
122            
123        /**
124         * Translate a filter expression by pushing the appropriate iterator
125         * onto the stack.
126         */
127        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
128            if (_predicates.size() > 0) {
129                translatePredicates(classGen, methodGen);
130            }
131            else {
132                _primary.translate(classGen, methodGen);
133            }
134        }
135    
136        /**
137         * Translate a sequence of predicates. Each predicate is translated 
138         * by constructing an instance of <code>CurrentNodeListIterator</code> 
139         * which is initialized from another iterator (recursive call), a 
140         * filter and a closure (call to translate on the predicate) and "this". 
141         */
142        public void translatePredicates(ClassGenerator classGen,
143                                        MethodGenerator methodGen) {
144            final ConstantPoolGen cpg = classGen.getConstantPool();
145            final InstructionList il = methodGen.getInstructionList();
146    
147            // If not predicates left, translate primary expression
148            if (_predicates.size() == 0) {
149                translate(classGen, methodGen);
150            } else {
151                // Remove the next predicate to be translated
152                Predicate predicate = (Predicate)_predicates.lastElement();
153                _predicates.remove(predicate);
154    
155                // Translate the rest of the predicates from right to left
156                translatePredicates(classGen, methodGen);
157    
158                if (predicate.isNthPositionFilter()) {
159                    int nthIteratorIdx = cpg.addMethodref(NTH_ITERATOR_CLASS,
160                                           "<init>",
161                                           "("+NODE_ITERATOR_SIG+"I)V");
162    
163                    // Backwards branches are prohibited if an uninitialized object
164                    // is on the stack by section 4.9.4 of the JVM Specification,
165                    // 2nd Ed.  We don't know whether this code might contain
166                    // backwards branches, so we mustn't create the new object unti
167    
168                    // after we've created the suspect arguments to its constructor
169    
170                    // Instead we calculate the values of the arguments to the
171                    // constructor first, store them in temporary variables, create
172                    // the object and reload the arguments from the temporaries to
173                    // avoid the problem.
174                    LocalVariableGen iteratorTemp
175                            = methodGen.addLocalVariable("filter_expr_tmp1",
176                                             Util.getJCRefType(NODE_ITERATOR_SIG),
177                                             null, null);
178                    iteratorTemp.setStart(
179                            il.append(new ASTORE(iteratorTemp.getIndex())));
180    
181                    predicate.translate(classGen, methodGen);
182                    LocalVariableGen predicateValueTemp
183                            = methodGen.addLocalVariable("filter_expr_tmp2",
184                                             Util.getJCRefType("I"),
185                                             null, null);
186                    predicateValueTemp.setStart(
187                            il.append(new ISTORE(predicateValueTemp.getIndex())));
188    
189                    il.append(new NEW(cpg.addClass(NTH_ITERATOR_CLASS)));
190                    il.append(DUP);
191                    iteratorTemp.setEnd(
192                            il.append(new ALOAD(iteratorTemp.getIndex())));
193                    predicateValueTemp.setEnd(
194                            il.append(new ILOAD(predicateValueTemp.getIndex())));
195                    il.append(new INVOKESPECIAL(nthIteratorIdx));
196                } else {
197                    // Translate predicates from right to left
198                    final int initCNLI =
199                                     cpg.addMethodref(CURRENT_NODE_LIST_ITERATOR,
200                                                     "<init>",
201                                                     "("+NODE_ITERATOR_SIG+"Z"+
202                                                     CURRENT_NODE_LIST_FILTER_SIG +
203                                                     NODE_SIG+TRANSLET_SIG+")V");
204    
205                    // Backwards branches are prohibited if an uninitialized object
206                    // is on the stack by section 4.9.4 of the JVM Specification,
207                    // 2nd Ed.  We don't know whether this code might contain
208                    // backwards branches, so we mustn't create the new object
209                    // until after we've created the suspect arguments to its
210                    // constructor.  Instead we calculate the values of the
211                    // arguments to the constructor first, store them in temporary
212                    // variables, create the object and reload the arguments from
213                    // the temporaries to avoid the problem.
214    
215    
216                    LocalVariableGen nodeIteratorTemp =
217                        methodGen.addLocalVariable("filter_expr_tmp1",
218                                               Util.getJCRefType(NODE_ITERATOR_SIG),
219                                               null, null);
220                    nodeIteratorTemp.setStart(
221                            il.append(new ASTORE(nodeIteratorTemp.getIndex())));
222    
223                    predicate.translate(classGen, methodGen);
224                    LocalVariableGen filterTemp =
225                        methodGen.addLocalVariable("filter_expr_tmp2",
226                                  Util.getJCRefType(CURRENT_NODE_LIST_FILTER_SIG),
227                                  null, null);
228                    filterTemp.setStart(
229                            il.append(new ASTORE(filterTemp.getIndex())));
230    
231                    // Create a CurrentNodeListIterator
232                    il.append(new NEW(cpg.addClass(CURRENT_NODE_LIST_ITERATOR)));
233                    il.append(DUP);
234                
235                    // Initialize CurrentNodeListIterator
236                    nodeIteratorTemp.setEnd(
237                            il.append(new ALOAD(nodeIteratorTemp.getIndex())));
238                    il.append(ICONST_1);
239                    filterTemp.setEnd(il.append(new ALOAD(filterTemp.getIndex())));
240                    il.append(methodGen.loadCurrentNode());
241                    il.append(classGen.loadTranslet());
242                    il.append(new INVOKESPECIAL(initCNLI));
243                }
244            }
245        }
246    }