KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xalan > xsltc > compiler > Predicate


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: Predicate.java,v 1.34 2004/02/24 02:58:42 zongaro Exp $
18  */

19
20 package org.apache.xalan.xsltc.compiler;
21
22 import java.util.ArrayList JavaDoc;
23
24 import org.apache.bcel.classfile.Field;
25 import org.apache.bcel.generic.ASTORE;
26 import org.apache.bcel.generic.CHECKCAST;
27 import org.apache.bcel.generic.ConstantPoolGen;
28 import org.apache.bcel.generic.GETFIELD;
29 import org.apache.bcel.generic.INVOKESPECIAL;
30 import org.apache.bcel.generic.InstructionList;
31 import org.apache.bcel.generic.LocalVariableGen;
32 import org.apache.bcel.generic.NEW;
33 import org.apache.bcel.generic.PUSH;
34 import org.apache.bcel.generic.PUTFIELD;
35 import org.apache.xalan.xsltc.compiler.util.BooleanType;
36 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
37 import org.apache.xalan.xsltc.compiler.util.FilterGenerator;
38 import org.apache.xalan.xsltc.compiler.util.IntType;
39 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
40 import org.apache.xalan.xsltc.compiler.util.NumberType;
41 import org.apache.xalan.xsltc.compiler.util.ReferenceType;
42 import org.apache.xalan.xsltc.compiler.util.ResultTreeType;
43 import org.apache.xalan.xsltc.compiler.util.TestGenerator;
44 import org.apache.xalan.xsltc.compiler.util.Type;
45 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
46 import org.apache.xalan.xsltc.compiler.util.Util;
47
48 /**
49  * @author Jacek Ambroziak
50  * @author Santiago Pericas-Geertsen
51  * @author Morten Jorgensen
52  */

53 final class Predicate extends Expression implements Closure {
54
55     /**
56      * The predicate's expression.
57      */

58     private Expression _exp = null;
59     
60     /**
61      * This flag indicates if optimizations are turned on. The
62      * method <code>dontOptimize()</code> can be called to turn
63      * optimizations off.
64      */

65     private boolean _canOptimize = true;
66     
67     /**
68      * Flag indicatig if the nth position optimization is on. It
69      * is set in <code>typeCheck()</code>.
70      */

71     private boolean _nthPositionFilter = false;
72         
73     /**
74      * Flag indicatig if the nth position descendant is on. It
75      * is set in <code>typeCheck()</code>.
76      */

77     private boolean _nthDescendant = false;
78
79     /**
80      * Cached node type of the expression that owns this predicate.
81      */

82     int _ptype = -1;
83
84     /**
85      * Name of the inner class.
86      */

87     private String JavaDoc _className = null;
88     
89     /**
90      * List of variables in closure.
91      */

92     private ArrayList JavaDoc _closureVars = null;
93     
94     /**
95      * Reference to parent closure.
96      */

97     private Closure _parentClosure = null;
98
99     /**
100      * Cached value of method <code>getCompareValue()</code>.
101      */

102     private Expression _value = null;
103     
104     /**
105      * Cached value of method <code>getCompareValue()</code>.
106      */

107     private Step _step = null;
108
109     /**
110      * Initializes a predicate.
111      */

112     public Predicate(Expression exp) {
113         _exp = exp;
114         _exp.setParent(this);
115
116     }
117
118     /**
119      * Set the parser for this expression.
120      */

121     public void setParser(Parser parser) {
122     super.setParser(parser);
123     _exp.setParser(parser);
124     }
125
126     /**
127      * Returns a boolean value indicating if the nth position optimization
128      * is on. Must be call after type checking!
129      */

130     public boolean isNthPositionFilter() {
131     return _nthPositionFilter;
132     }
133
134     /**
135      * Returns a boolean value indicating if the nth descendant optimization
136      * is on. Must be call after type checking!
137      */

138     public boolean isNthDescendant() {
139     return _nthDescendant;
140     }
141
142     /**
143      * Turns off all optimizations for this predicate.
144      */

145     public void dontOptimize() {
146     _canOptimize = false;
147     }
148     
149     /**
150      * Returns true if the expression in this predicate contains a call
151      * to position().
152      */

153     public boolean hasPositionCall() {
154     return _exp.hasPositionCall();
155     }
156
157     /**
158      * Returns true if the expression in this predicate contains a call
159      * to last().
160      */

161     public boolean hasLastCall() {
162     return _exp.hasLastCall();
163     }
164
165     // -- Begin Closure interface --------------------
166

167     /**
168      * Returns true if this closure is compiled in an inner class (i.e.
169      * if this is a real closure).
170      */

171     public boolean inInnerClass() {
172     return (_className != null);
173     }
174
175     /**
176      * Returns a reference to its parent closure or null if outermost.
177      */

178     public Closure getParentClosure() {
179     if (_parentClosure == null) {
180         SyntaxTreeNode node = getParent();
181         do {
182         if (node instanceof Closure) {
183             _parentClosure = (Closure) node;
184             break;
185         }
186         if (node instanceof TopLevelElement) {
187             break; // way up in the tree
188
}
189         node = node.getParent();
190         } while (node != null);
191     }
192     return _parentClosure;
193     }
194
195     /**
196      * Returns the name of the auxiliary class or null if this predicate
197      * is compiled inside the Translet.
198      */

199     public String JavaDoc getInnerClassName() {
200     return _className;
201     }
202
203     /**
204      * Add new variable to the closure.
205      */

206     public void addVariable(VariableRefBase variableRef) {
207     if (_closureVars == null) {
208         _closureVars = new ArrayList JavaDoc();
209     }
210
211     // Only one reference per variable
212
if (!_closureVars.contains(variableRef)) {
213         _closureVars.add(variableRef);
214
215         // Add variable to parent closure as well
216
Closure parentClosure = getParentClosure();
217         if (parentClosure != null) {
218         parentClosure.addVariable(variableRef);
219         }
220     }
221     }
222
223     // -- End Closure interface ----------------------
224

225     /**
226      * Returns the node type of the expression owning this predicate. The
227      * return value is cached in <code>_ptype</code>.
228      */

229     public int getPosType() {
230     if (_ptype == -1) {
231         SyntaxTreeNode parent = getParent();
232         if (parent instanceof StepPattern) {
233         _ptype = ((StepPattern)parent).getNodeType();
234         }
235         else if (parent instanceof AbsoluteLocationPath) {
236         AbsoluteLocationPath path = (AbsoluteLocationPath)parent;
237         Expression exp = path.getPath();
238         if (exp instanceof Step) {
239             _ptype = ((Step)exp).getNodeType();
240         }
241         }
242         else if (parent instanceof VariableRefBase) {
243         final VariableRefBase ref = (VariableRefBase)parent;
244         final VariableBase var = ref.getVariable();
245         final Expression exp = var.getExpression();
246         if (exp instanceof Step) {
247             _ptype = ((Step)exp).getNodeType();
248         }
249         }
250         else if (parent instanceof Step) {
251         _ptype = ((Step)parent).getNodeType();
252         }
253     }
254     return _ptype;
255     }
256
257     public boolean parentIsPattern() {
258     return (getParent() instanceof Pattern);
259     }
260
261     public Expression getExpr() {
262     return _exp;
263     }
264
265     public String JavaDoc toString() {
266         return "pred(" + _exp + ')';
267     }
268     
269     /**
270      * Type check a predicate expression. If the type of the expression is
271      * number convert it to boolean by adding a comparison with position().
272      * Note that if the expression is a parameter, we cannot distinguish
273      * at compile time if its type is number or not. Hence, expressions of
274      * reference type are always converted to booleans.
275      *
276      * This method may be called twice, before and after calling
277      * <code>dontOptimize()</code>. If so, the second time it should honor
278      * the new value of <code>_canOptimize</code>.
279      */

280     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
281     Type texp = _exp.typeCheck(stable);
282
283     // We need explicit type information for reference types - no good!
284
if (texp instanceof ReferenceType) {
285         _exp = new CastExpr(_exp, texp = Type.Real);
286     }
287
288     // A result tree fragment should not be cast directly to a number type,
289
// but rather to a boolean value, and then to a numer (0 or 1).
290
// Ref. section 11.2 of the XSLT 1.0 spec
291
if (texp instanceof ResultTreeType) {
292         _exp = new CastExpr(_exp, Type.Boolean);
293         _exp = new CastExpr(_exp, Type.Real);
294         texp = _exp.typeCheck(stable);
295     }
296
297     // Numerical types will be converted to a position filter
298
if (texp instanceof NumberType) {
299         // Cast any numerical types to an integer
300
if (texp instanceof IntType == false) {
301         _exp = new CastExpr(_exp, Type.Int);
302         }
303                     
304             if (_canOptimize) {
305                 // Nth position optimization. Expression must not depend on context
306
_nthPositionFilter =
307                     !_exp.hasLastCall() && !_exp.hasPositionCall();
308                 
309                 // _nthDescendant optimization - only if _nthPositionFilter is on
310
if (_nthPositionFilter) {
311                     SyntaxTreeNode parent = getParent();
312                     _nthDescendant = (parent instanceof Step) &&
313                         (parent.getParent() instanceof AbsoluteLocationPath);
314                     return _type = Type.NodeSet;
315                 }
316             }
317
318            // Reset optimization flags
319
_nthPositionFilter = _nthDescendant = false;
320             
321            // Otherwise, expand [e] to [position() = e]
322
final QName position =
323                 getParser().getQNameIgnoreDefaultNs("position");
324            final PositionCall positionCall =
325                 new PositionCall(position);
326            positionCall.setParser(getParser());
327            positionCall.setParent(this);
328
329            _exp = new EqualityExpr(EqualityExpr.EQ, positionCall,
330                                     _exp);
331            if (_exp.typeCheck(stable) != Type.Boolean) {
332                _exp = new CastExpr(_exp, Type.Boolean);
333            }
334            return _type = Type.Boolean;
335     }
336     else {
337             // All other types will be handled as boolean values
338
if (texp instanceof BooleanType == false) {
339         _exp = new CastExpr(_exp, Type.Boolean);
340             }
341             return _type = Type.Boolean;
342     }
343     }
344     
345     /**
346      * Create a new "Filter" class implementing
347      * <code>CurrentNodeListFilter</code>. Allocate registers for local
348      * variables and local parameters passed in the closure to test().
349      * Notice that local variables need to be "unboxed".
350      */

351     private void compileFilter(ClassGenerator classGen,
352                    MethodGenerator methodGen) {
353     TestGenerator testGen;
354     LocalVariableGen local;
355     FilterGenerator filterGen;
356
357     _className = getXSLTC().getHelperClassName();
358     filterGen = new FilterGenerator(_className,
359                     "java.lang.Object",
360                     toString(),
361                     ACC_PUBLIC | ACC_SUPER,
362                     new String JavaDoc[] {
363                         CURRENT_NODE_LIST_FILTER
364                     },
365                     classGen.getStylesheet());
366
367     final ConstantPoolGen cpg = filterGen.getConstantPool();
368     final int length = (_closureVars == null) ? 0 : _closureVars.size();
369
370     // Add a new instance variable for each var in closure
371
for (int i = 0; i < length; i++) {
372         VariableBase var = ((VariableRefBase) _closureVars.get(i)).getVariable();
373
374         filterGen.addField(new Field(ACC_PUBLIC,
375                     cpg.addUtf8(var.getEscapedName()),
376                     cpg.addUtf8(var.getType().toSignature()),
377                     null, cpg.getConstantPool()));
378     }
379
380     final InstructionList il = new InstructionList();
381     testGen = new TestGenerator(ACC_PUBLIC | ACC_FINAL,
382                     org.apache.bcel.generic.Type.BOOLEAN,
383                     new org.apache.bcel.generic.Type[] {
384                     org.apache.bcel.generic.Type.INT,
385                     org.apache.bcel.generic.Type.INT,
386                     org.apache.bcel.generic.Type.INT,
387                     org.apache.bcel.generic.Type.INT,
388                     Util.getJCRefType(TRANSLET_SIG),
389                     Util.getJCRefType(NODE_ITERATOR_SIG)
390                     },
391                     new String JavaDoc[] {
392                     "node",
393                     "position",
394                     "last",
395                     "current",
396                     "translet",
397                     "iterator"
398                     },
399                     "test", _className, il, cpg);
400         
401     // Store the dom in a local variable
402
local = testGen.addLocalVariable("document",
403                      Util.getJCRefType(DOM_INTF_SIG),
404                      null, null);
405     final String JavaDoc className = classGen.getClassName();
406     il.append(filterGen.loadTranslet());
407     il.append(new CHECKCAST(cpg.addClass(className)));
408     il.append(new GETFIELD(cpg.addFieldref(className,
409                            DOM_FIELD, DOM_INTF_SIG)));
410     il.append(new ASTORE(local.getIndex()));
411
412     // Store the dom index in the test generator
413
testGen.setDomIndex(local.getIndex());
414
415     _exp.translate(filterGen, testGen);
416     il.append(IRETURN);
417     
418     testGen.stripAttributes(true);
419     testGen.setMaxLocals();
420     testGen.setMaxStack();
421     testGen.removeNOPs();
422     filterGen.addEmptyConstructor(ACC_PUBLIC);
423     filterGen.addMethod(testGen.getMethod());
424         
425     getXSLTC().dumpClass(filterGen.getJavaClass());
426     }
427
428     /**
429      * Returns true if the predicate is a test for the existance of an
430      * element or attribute. All we have to do is to get the first node
431      * from the step, check if it is there, and then return true/false.
432      */

433     public boolean isBooleanTest() {
434     return (_exp instanceof BooleanExpr);
435     }
436
437     /**
438      * Method to see if we can optimise the predicate by using a specialised
439      * iterator for expressions like '/foo/bar[@attr = $var]', which are
440      * very common in many stylesheets
441      */

442     public boolean isNodeValueTest() {
443     if (!_canOptimize) return false;
444     return (getStep() != null && getCompareValue() != null);
445     }
446
447    /**
448      * Returns the step in an expression of the form 'step = value'.
449      * Null is returned if the expression is not of the right form.
450      * Optimization if off if null is returned.
451      */

452     public Step getStep() {
453         // Returned cached value if called more than once
454
if (_step != null) {
455             return _step;
456         }
457         
458         // Nothing to do if _exp is null
459
if (_exp == null) {
460             return null;
461         }
462
463         // Ignore if not equality expression
464
if (_exp instanceof EqualityExpr) {
465         EqualityExpr exp = (EqualityExpr)_exp;
466         Expression left = exp.getLeft();
467         Expression right = exp.getRight();
468
469             // Unwrap and set _step if appropriate
470
if (left instanceof CastExpr) {
471                 left = ((CastExpr) left).getExpr();
472             }
473         if (left instanceof Step) {
474                 _step = (Step) left;
475             }
476         
477             // Unwrap and set _step if appropriate
478
if (right instanceof CastExpr) {
479                 right = ((CastExpr)right).getExpr();
480             }
481         if (right instanceof Step) {
482                 _step = (Step)right;
483             }
484     }
485     return _step;
486     }
487
488     /**
489      * Returns the value in an expression of the form 'step = value'.
490      * A value may be either a literal string or a variable whose
491      * type is string. Optimization if off if null is returned.
492      */

493     public Expression getCompareValue() {
494         // Returned cached value if called more than once
495
if (_value != null) {
496             return _value;
497         }
498         
499         // Nothing to to do if _exp is null
500
if (_exp == null) {
501             return null;
502         }
503
504         // Ignore if not an equality expression
505
if (_exp instanceof EqualityExpr) {
506         EqualityExpr exp = (EqualityExpr) _exp;
507         Expression left = exp.getLeft();
508         Expression right = exp.getRight();
509             
510             // Return if left is literal string
511
if (left instanceof LiteralExpr) {
512                 _value = left;
513                 return _value;
514             }
515             // Return if left is a variable reference of type string
516
if (left instanceof VariableRefBase &&
517                 left.getType() == Type.String)
518             {
519                 _value = left;
520                 return _value;
521             }
522             
523             // Return if right is literal string
524
if (right instanceof LiteralExpr) {
525                 _value = right;
526                 return _value;
527             }
528             // Return if left is a variable reference whose type is string
529
if (right instanceof VariableRefBase &&
530                 right.getType() == Type.String)
531             {
532                 _value = right;
533                 return _value;
534             }
535     }
536     return null;
537     }
538  
539     /**
540      * Translate a predicate expression. This translation pushes
541      * two references on the stack: a reference to a newly created
542      * filter object and a reference to the predicate's closure.
543      */

544     public void translateFilter(ClassGenerator classGen,
545                 MethodGenerator methodGen)
546     {
547     final ConstantPoolGen cpg = classGen.getConstantPool();
548     final InstructionList il = methodGen.getInstructionList();
549
550     // Compile auxiliary class for filter
551
compileFilter(classGen, methodGen);
552     
553     // Create new instance of filter
554
il.append(new NEW(cpg.addClass(_className)));
555     il.append(DUP);
556     il.append(new INVOKESPECIAL(cpg.addMethodref(_className,
557                              "<init>", "()V")));
558
559     // Initialize closure variables
560
final int length = (_closureVars == null) ? 0 : _closureVars.size();
561
562     for (int i = 0; i < length; i++) {
563         VariableRefBase varRef = (VariableRefBase) _closureVars.get(i);
564         VariableBase var = varRef.getVariable();
565         Type varType = var.getType();
566
567         il.append(DUP);
568
569         // Find nearest closure implemented as an inner class
570
Closure variableClosure = _parentClosure;
571         while (variableClosure != null) {
572         if (variableClosure.inInnerClass()) break;
573         variableClosure = variableClosure.getParentClosure();
574         }
575
576         // Use getfield if in an inner class
577
if (variableClosure != null) {
578         il.append(ALOAD_0);
579         il.append(new GETFIELD(
580             cpg.addFieldref(variableClosure.getInnerClassName(),
581             var.getEscapedName(), varType.toSignature())));
582         }
583         else {
584         // Use a load of instruction if in translet class
585
il.append(var.loadInstruction());
586         }
587
588         // Store variable in new closure
589
il.append(new PUTFIELD(
590             cpg.addFieldref(_className, var.getEscapedName(),
591             varType.toSignature())));
592     }
593     }
594     
595     /**
596      * Translate a predicate expression. If non of the optimizations apply
597      * then this translation pushes two references on the stack: a reference
598      * to a newly created filter object and a reference to the predicate's
599      * closure. See class <code>Step</code> for further details.
600      */

601     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
602
603     final ConstantPoolGen cpg = classGen.getConstantPool();
604     final InstructionList il = methodGen.getInstructionList();
605
606     if (_nthPositionFilter || _nthDescendant) {
607         _exp.translate(classGen, methodGen);
608     }
609     else if (isNodeValueTest() && (getParent() instanceof Step)) {
610         _value.translate(classGen, methodGen);
611         il.append(new CHECKCAST(cpg.addClass(STRING_CLASS)));
612         il.append(new PUSH(cpg, ((EqualityExpr)_exp).getOp()));
613     }
614     else {
615         translateFilter(classGen, methodGen);
616     }
617     }
618 }
619
Popular Tags