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