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: Predicate.java 468650 2006-10-28 07:03:30Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import java.util.ArrayList;
025    
026    import org.apache.bcel.classfile.Field;
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.GETFIELD;
031    import org.apache.bcel.generic.INVOKESPECIAL;
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.bcel.generic.PUSH;
036    import org.apache.bcel.generic.PUTFIELD;
037    import org.apache.xalan.xsltc.compiler.util.BooleanType;
038    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
039    import org.apache.xalan.xsltc.compiler.util.FilterGenerator;
040    import org.apache.xalan.xsltc.compiler.util.IntType;
041    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
042    import org.apache.xalan.xsltc.compiler.util.NumberType;
043    import org.apache.xalan.xsltc.compiler.util.ReferenceType;
044    import org.apache.xalan.xsltc.compiler.util.ResultTreeType;
045    import org.apache.xalan.xsltc.compiler.util.TestGenerator;
046    import org.apache.xalan.xsltc.compiler.util.Type;
047    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
048    import org.apache.xalan.xsltc.compiler.util.Util;
049    import org.apache.xalan.xsltc.runtime.Operators;
050    
051    /**
052     * @author Jacek Ambroziak
053     * @author Santiago Pericas-Geertsen
054     * @author Morten Jorgensen
055     */
056    final class Predicate extends Expression implements Closure {
057    
058        /**
059         * The predicate's expression.
060         */
061        private Expression _exp = null; 
062        
063        /**
064         * This flag indicates if optimizations are turned on. The 
065         * method <code>dontOptimize()</code> can be called to turn
066         * optimizations off.
067         */
068        private boolean _canOptimize = true;
069        
070        /**
071         * Flag indicatig if the nth position optimization is on. It
072         * is set in <code>typeCheck()</code>.
073         */
074        private boolean _nthPositionFilter = false;
075            
076        /**
077         * Flag indicatig if the nth position descendant is on. It
078         * is set in <code>typeCheck()</code>.
079         */
080        private boolean _nthDescendant = false;
081    
082        /**
083         * Cached node type of the expression that owns this predicate.
084         */
085        int _ptype = -1;
086    
087        /**
088         * Name of the inner class.
089         */
090        private String _className = null;
091        
092        /**
093         * List of variables in closure.
094         */
095        private ArrayList _closureVars = null;
096        
097        /**
098         * Reference to parent closure.
099         */
100        private Closure _parentClosure = null;
101    
102        /**
103         * Cached value of method <code>getCompareValue()</code>.
104         */
105        private Expression _value = null;
106        
107        /**
108         * Cached value of method <code>getCompareValue()</code>.
109         */
110        private Step _step = null;
111    
112        /**
113         * Initializes a predicate. 
114         */
115        public Predicate(Expression exp) {
116            _exp = exp;
117            _exp.setParent(this);
118    
119        }
120    
121        /** 
122         * Set the parser for this expression.
123         */
124        public void setParser(Parser parser) {
125            super.setParser(parser);
126            _exp.setParser(parser);
127        }
128    
129        /**
130         * Returns a boolean value indicating if the nth position optimization
131         * is on. Must be call after type checking!
132         */
133        public boolean isNthPositionFilter() {
134            return _nthPositionFilter;
135        }
136    
137        /**
138         * Returns a boolean value indicating if the nth descendant optimization
139         * is on. Must be call after type checking!
140         */
141        public boolean isNthDescendant() {
142            return _nthDescendant;
143        }
144    
145        /**
146         * Turns off all optimizations for this predicate.
147         */
148        public void dontOptimize() {
149            _canOptimize = false;
150        }
151        
152        /**
153         * Returns true if the expression in this predicate contains a call 
154         * to position().
155         */
156        public boolean hasPositionCall() {
157            return _exp.hasPositionCall();
158        }
159    
160        /**
161         * Returns true if the expression in this predicate contains a call 
162         * to last().
163         */
164        public boolean hasLastCall() {
165            return _exp.hasLastCall();
166        }
167    
168        // -- Begin Closure interface --------------------
169    
170        /**
171         * Returns true if this closure is compiled in an inner class (i.e.
172         * if this is a real closure).
173         */
174        public boolean inInnerClass() {
175            return (_className != null);
176        }
177    
178        /**
179         * Returns a reference to its parent closure or null if outermost.
180         */
181        public Closure getParentClosure() {
182            if (_parentClosure == null) {
183                SyntaxTreeNode node = getParent();
184                do {
185                    if (node instanceof Closure) {
186                        _parentClosure = (Closure) node;
187                        break;
188                    }
189                    if (node instanceof TopLevelElement) {
190                        break;      // way up in the tree
191                    }
192                    node = node.getParent();
193                } while (node != null);
194            }
195            return _parentClosure;
196        }
197    
198        /**
199         * Returns the name of the auxiliary class or null if this predicate 
200         * is compiled inside the Translet.
201         */
202        public String getInnerClassName() {
203            return _className;
204        }
205    
206        /**
207         * Add new variable to the closure.
208         */
209        public void addVariable(VariableRefBase variableRef) {
210            if (_closureVars == null) {
211                _closureVars = new ArrayList();
212            }
213    
214            // Only one reference per variable
215            if (!_closureVars.contains(variableRef)) {
216                _closureVars.add(variableRef);
217    
218                // Add variable to parent closure as well
219                Closure parentClosure = getParentClosure();
220                if (parentClosure != null) {
221                    parentClosure.addVariable(variableRef);
222                }
223            }
224        }
225    
226        // -- End Closure interface ----------------------
227    
228        /**
229         * Returns the node type of the expression owning this predicate. The
230         * return value is cached in <code>_ptype</code>.
231         */
232        public int getPosType() {
233            if (_ptype == -1) {
234                SyntaxTreeNode parent = getParent();
235                if (parent instanceof StepPattern) {
236                    _ptype = ((StepPattern)parent).getNodeType();
237                }
238                else if (parent instanceof AbsoluteLocationPath) {
239                    AbsoluteLocationPath path = (AbsoluteLocationPath)parent;
240                    Expression exp = path.getPath();
241                    if (exp instanceof Step) {
242                        _ptype = ((Step)exp).getNodeType();
243                    }
244                }
245                else if (parent instanceof VariableRefBase) {
246                    final VariableRefBase ref = (VariableRefBase)parent;
247                    final VariableBase var = ref.getVariable();
248                    final Expression exp = var.getExpression();
249                    if (exp instanceof Step) {
250                        _ptype = ((Step)exp).getNodeType();
251                    }
252                }
253                else if (parent instanceof Step) {
254                    _ptype = ((Step)parent).getNodeType();
255                }
256            }
257            return _ptype;
258        }
259    
260        public boolean parentIsPattern() {
261            return (getParent() instanceof Pattern);
262        }
263    
264        public Expression getExpr() {
265            return _exp;
266        }
267    
268        public String toString() {
269            return "pred(" + _exp + ')';
270        }
271            
272        /**
273         * Type check a predicate expression. If the type of the expression is 
274         * number convert it to boolean by adding a comparison with position().
275         * Note that if the expression is a parameter, we cannot distinguish
276         * at compile time if its type is number or not. Hence, expressions of 
277         * reference type are always converted to booleans.
278         *
279         * This method may be called twice, before and after calling
280         * <code>dontOptimize()</code>. If so, the second time it should honor
281         * the new value of <code>_canOptimize</code>.
282         */
283        public Type typeCheck(SymbolTable stable) throws TypeCheckError {        
284            Type texp = _exp.typeCheck(stable);
285    
286            // We need explicit type information for reference types - no good!
287            if (texp instanceof ReferenceType) {
288                _exp = new CastExpr(_exp, texp = Type.Real);
289            }
290    
291            // A result tree fragment should not be cast directly to a number type,
292            // but rather to a boolean value, and then to a numer (0 or 1).
293            // Ref. section 11.2 of the XSLT 1.0 spec
294            if (texp instanceof ResultTreeType) {
295                _exp = new CastExpr(_exp, Type.Boolean);
296                _exp = new CastExpr(_exp, Type.Real);
297                texp = _exp.typeCheck(stable);
298            }
299    
300            // Numerical types will be converted to a position filter
301            if (texp instanceof NumberType) {
302                // Cast any numerical types to an integer
303                if (texp instanceof IntType == false) {
304                    _exp = new CastExpr(_exp, Type.Int);
305                }
306                        
307                if (_canOptimize) {
308                    // Nth position optimization. Expression must not depend on context
309                    _nthPositionFilter = 
310                        !_exp.hasLastCall() && !_exp.hasPositionCall();
311                    
312                    // _nthDescendant optimization - only if _nthPositionFilter is on
313                    if (_nthPositionFilter) {
314                        SyntaxTreeNode parent = getParent();
315                        _nthDescendant = (parent instanceof Step) &&
316                            (parent.getParent() instanceof AbsoluteLocationPath);
317                        return _type = Type.NodeSet;
318                    }
319                }          
320    
321               // Reset optimization flags
322                _nthPositionFilter = _nthDescendant = false;
323                
324               // Otherwise, expand [e] to [position() = e]
325               final QName position = 
326                    getParser().getQNameIgnoreDefaultNs("position");
327               final PositionCall positionCall =
328                    new PositionCall(position);
329               positionCall.setParser(getParser());
330               positionCall.setParent(this);
331    
332               _exp = new EqualityExpr(Operators.EQ, positionCall,
333                                        _exp);
334               if (_exp.typeCheck(stable) != Type.Boolean) {
335                   _exp = new CastExpr(_exp, Type.Boolean);
336               }
337               return _type = Type.Boolean;
338            }
339            else {
340                // All other types will be handled as boolean values
341                if (texp instanceof BooleanType == false) {
342                    _exp = new CastExpr(_exp, Type.Boolean);
343                }
344                return _type = Type.Boolean;
345            }
346        }
347            
348        /**
349         * Create a new "Filter" class implementing
350         * <code>CurrentNodeListFilter</code>. Allocate registers for local 
351         * variables and local parameters passed in the closure to test().
352         * Notice that local variables need to be "unboxed".
353         */
354        private void compileFilter(ClassGenerator classGen,
355                                   MethodGenerator methodGen) {
356            TestGenerator testGen;
357            LocalVariableGen local;
358            FilterGenerator filterGen;
359    
360            _className = getXSLTC().getHelperClassName();
361            filterGen = new FilterGenerator(_className,
362                                            "java.lang.Object",
363                                            toString(), 
364                                            ACC_PUBLIC | ACC_SUPER,
365                                            new String[] {
366                                                CURRENT_NODE_LIST_FILTER
367                                            },
368                                            classGen.getStylesheet());      
369    
370            final ConstantPoolGen cpg = filterGen.getConstantPool();
371            final int length = (_closureVars == null) ? 0 : _closureVars.size();
372    
373            // Add a new instance variable for each var in closure
374            for (int i = 0; i < length; i++) {
375                VariableBase var = ((VariableRefBase) _closureVars.get(i)).getVariable();
376    
377                filterGen.addField(new Field(ACC_PUBLIC, 
378                                            cpg.addUtf8(var.getEscapedName()),
379                                            cpg.addUtf8(var.getType().toSignature()),
380                                            null, cpg.getConstantPool()));
381            }
382    
383            final InstructionList il = new InstructionList();
384            testGen = new TestGenerator(ACC_PUBLIC | ACC_FINAL,
385                                        org.apache.bcel.generic.Type.BOOLEAN, 
386                                        new org.apache.bcel.generic.Type[] {
387                                            org.apache.bcel.generic.Type.INT,
388                                            org.apache.bcel.generic.Type.INT,
389                                            org.apache.bcel.generic.Type.INT,
390                                            org.apache.bcel.generic.Type.INT,
391                                            Util.getJCRefType(TRANSLET_SIG),
392                                            Util.getJCRefType(NODE_ITERATOR_SIG)
393                                        },
394                                        new String[] {
395                                            "node",
396                                            "position",
397                                            "last",
398                                            "current",
399                                            "translet",
400                                            "iterator"
401                                        },
402                                        "test", _className, il, cpg);
403                    
404            // Store the dom in a local variable
405            local = testGen.addLocalVariable("document",
406                                             Util.getJCRefType(DOM_INTF_SIG),
407                                             null, null);
408            final String className = classGen.getClassName();
409            il.append(filterGen.loadTranslet());
410            il.append(new CHECKCAST(cpg.addClass(className)));
411            il.append(new GETFIELD(cpg.addFieldref(className,
412                                                   DOM_FIELD, DOM_INTF_SIG)));
413            local.setStart(il.append(new ASTORE(local.getIndex())));
414    
415            // Store the dom index in the test generator
416            testGen.setDomIndex(local.getIndex());
417    
418            _exp.translate(filterGen, testGen);
419            il.append(IRETURN);
420            
421            filterGen.addEmptyConstructor(ACC_PUBLIC);
422            filterGen.addMethod(testGen);
423                    
424            getXSLTC().dumpClass(filterGen.getJavaClass());
425        }
426    
427        /**
428         * Returns true if the predicate is a test for the existance of an
429         * element or attribute. All we have to do is to get the first node
430         * from the step, check if it is there, and then return true/false.
431         */
432        public boolean isBooleanTest() {
433            return (_exp instanceof BooleanExpr);
434        }
435    
436        /**
437         * Method to see if we can optimise the predicate by using a specialised
438         * iterator for expressions like '/foo/bar[@attr = $var]', which are
439         * very common in many stylesheets
440         */
441        public boolean isNodeValueTest() {
442            if (!_canOptimize) return false;
443            return (getStep() != null && getCompareValue() != null);
444        }
445    
446       /**
447         * Returns the step in an expression of the form 'step = value'. 
448         * Null is returned if the expression is not of the right form.
449         * Optimization if off if null is returned.
450         */
451        public Step getStep() {
452            // Returned cached value if called more than once
453            if (_step != null) {
454                return _step;
455            }
456            
457            // Nothing to do if _exp is null
458            if (_exp == null) {
459                return null;
460            }
461    
462            // Ignore if not equality expression
463            if (_exp instanceof EqualityExpr) {
464                EqualityExpr exp = (EqualityExpr)_exp;
465                Expression left = exp.getLeft();
466                Expression right = exp.getRight();
467    
468                // Unwrap and set _step if appropriate
469                if (left instanceof CastExpr) {
470                    left = ((CastExpr) left).getExpr();
471                }
472                if (left instanceof Step) {
473                    _step = (Step) left;
474                }
475                
476                // Unwrap and set _step if appropriate
477                if (right instanceof CastExpr) {
478                    right = ((CastExpr)right).getExpr();
479                }
480                if (right instanceof Step) {
481                    _step = (Step)right;
482                }
483            }
484            return _step;
485        }
486    
487        /**
488         * Returns the value in an expression of the form 'step = value'. 
489         * A value may be either a literal string or a variable whose 
490         * type is string. Optimization if off if null is returned.
491         */
492        public Expression getCompareValue() {
493            // Returned cached value if called more than once
494            if (_value != null) {
495                return _value;
496            }
497            
498            // Nothing to to do if _exp is null
499            if (_exp == null) {
500                return null;
501            }
502    
503            // Ignore if not an equality expression
504            if (_exp instanceof EqualityExpr) {
505                EqualityExpr exp = (EqualityExpr) _exp;
506                Expression left = exp.getLeft();
507                Expression right = exp.getRight();
508                
509                // Return if left is literal string
510                if (left instanceof LiteralExpr) {
511                    _value = left;
512                    return _value;
513                }
514                // Return if left is a variable reference of type string
515                if (left instanceof VariableRefBase &&
516                    left.getType() == Type.String) 
517                {
518                    _value = left;
519                    return _value;
520                }
521                
522                // Return if right is literal string
523                if (right instanceof LiteralExpr) {
524                    _value = right;
525                    return _value;
526                }
527                // Return if left is a variable reference whose type is string
528                if (right instanceof VariableRefBase &&
529                    right.getType() == Type.String) 
530                {
531                    _value = right;
532                    return _value;
533                }
534            }
535            return null;
536        }
537     
538        /**
539         * Translate a predicate expression. This translation pushes
540         * two references on the stack: a reference to a newly created
541         * filter object and a reference to the predicate's closure.
542         */
543        public void translateFilter(ClassGenerator classGen,
544                                    MethodGenerator methodGen) 
545        {
546            final ConstantPoolGen cpg = classGen.getConstantPool();
547            final InstructionList il = methodGen.getInstructionList();
548    
549            // Compile auxiliary class for filter
550            compileFilter(classGen, methodGen);
551            
552            // Create new instance of filter
553            il.append(new NEW(cpg.addClass(_className)));
554            il.append(DUP);
555            il.append(new INVOKESPECIAL(cpg.addMethodref(_className,
556                                                         "<init>", "()V")));
557    
558            // Initialize closure variables
559            final int length = (_closureVars == null) ? 0 : _closureVars.size();
560    
561            for (int i = 0; i < length; i++) {
562                VariableRefBase varRef = (VariableRefBase) _closureVars.get(i);
563                VariableBase var = varRef.getVariable();
564                Type varType = var.getType();
565    
566                il.append(DUP);
567    
568                // Find nearest closure implemented as an inner class
569                Closure variableClosure = _parentClosure;
570                while (variableClosure != null) {
571                    if (variableClosure.inInnerClass()) break;
572                    variableClosure = variableClosure.getParentClosure();
573                }
574    
575                // Use getfield if in an inner class
576                if (variableClosure != null) {
577                    il.append(ALOAD_0);
578                    il.append(new GETFIELD(
579                        cpg.addFieldref(variableClosure.getInnerClassName(), 
580                            var.getEscapedName(), varType.toSignature())));
581                }
582                else {
583                    // Use a load of instruction if in translet class
584                    il.append(var.loadInstruction());
585                }
586    
587                // Store variable in new closure
588                il.append(new PUTFIELD(
589                        cpg.addFieldref(_className, var.getEscapedName(), 
590                            varType.toSignature())));
591            }
592        }
593        
594        /**
595         * Translate a predicate expression. If non of the optimizations apply
596         * then this translation pushes two references on the stack: a reference 
597         * to a newly created filter object and a reference to the predicate's 
598         * closure. See class <code>Step</code> for further details.
599         */
600        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
601    
602            final ConstantPoolGen cpg = classGen.getConstantPool();
603            final InstructionList il = methodGen.getInstructionList();
604    
605            if (_nthPositionFilter || _nthDescendant) {
606                _exp.translate(classGen, methodGen);
607            }
608            else if (isNodeValueTest() && (getParent() instanceof Step)) {
609                _value.translate(classGen, methodGen);
610                il.append(new CHECKCAST(cpg.addClass(STRING_CLASS)));
611                il.append(new PUSH(cpg, ((EqualityExpr)_exp).getOp()));
612            }
613            else {
614                translateFilter(classGen, methodGen);
615            }
616        }
617    }