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: Number.java,v 1.2.4.1 2005/09/21 09:40:51 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.ALOAD;
30 import com.sun.org.apache.bcel.internal.generic.ILOAD;
31 import com.sun.org.apache.bcel.internal.generic.ASTORE;
32 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
33 import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
34 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
35 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
36 import com.sun.org.apache.bcel.internal.generic.GOTO;
37 import com.sun.org.apache.bcel.internal.generic.IFNONNULL;
38 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
39 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
40 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
41 import com.sun.org.apache.bcel.internal.generic.InstructionList;
42 import com.sun.org.apache.bcel.internal.generic.D2I;
43 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
44 import com.sun.org.apache.bcel.internal.generic.NEW;
45 import com.sun.org.apache.bcel.internal.generic.PUSH;
46 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
48 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MatchGenerator;
49 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
50 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeCounterGenerator;
51 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.RealType;
52 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
53 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
54 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
55 
56 /**
57  * @author Jacek Ambroziak
58  * @author Santiago Pericas-Geertsen
59  */
60 final class Number extends Instruction implements Closure {
61     private static final int LEVEL_SINGLE   = 0;
62     private static final int LEVEL_MULTIPLE = 1;
63     private static final int LEVEL_ANY      = 2;
64 
65     static final private String[] ClassNames = {
66         "com.sun.org.apache.xalan.internal.xsltc.dom.SingleNodeCounter",          // LEVEL_SINGLE
67         "com.sun.org.apache.xalan.internal.xsltc.dom.MultipleNodeCounter", // LEVEL_MULTIPLE
68         "com.sun.org.apache.xalan.internal.xsltc.dom.AnyNodeCounter"      // LEVEL_ANY
69     };
70 
71     static final private String[] FieldNames = {
72         "___single_node_counter",                  // LEVEL_SINGLE
73         "___multiple_node_counter",                // LEVEL_MULTIPLE
74         "___any_node_counter"                      // LEVEL_ANY
75     };
76 
77     private Pattern _from = null;
78     private Pattern _count = null;
79     private Expression _value = null;
80 
81     private AttributeValueTemplate _lang = null;
82     private AttributeValueTemplate _format = null;
83     private AttributeValueTemplate _letterValue = null;
84     private AttributeValueTemplate _groupingSeparator = null;
85     private AttributeValueTemplate _groupingSize = null;
86 
87     private int _level = LEVEL_SINGLE;
88     private boolean _formatNeeded = false;
89 
90     private String _className = null;
91     private ArrayList _closureVars = null;
92 
93      // -- Begin Closure interface --------------------
94 
95     /**
96      * Returns true if this closure is compiled in an inner class (i.e.
97      * if this is a real closure).
98      */
inInnerClass()99     public boolean inInnerClass() {
100         return (_className != null);
101     }
102 
103     /**
104      * Returns a reference to its parent closure or null if outermost.
105      */
getParentClosure()106     public Closure getParentClosure() {
107         return null;
108     }
109 
110     /**
111      * Returns the name of the auxiliary class or null if this predicate
112      * is compiled inside the Translet.
113      */
getInnerClassName()114     public String getInnerClassName() {
115         return _className;
116     }
117 
118     /**
119      * Add new variable to the closure.
120      */
addVariable(VariableRefBase variableRef)121     public void addVariable(VariableRefBase variableRef) {
122         if (_closureVars == null) {
123             _closureVars = new ArrayList();
124         }
125 
126         // Only one reference per variable
127         if (!_closureVars.contains(variableRef)) {
128             _closureVars.add(variableRef);
129         }
130     }
131 
132     // -- End Closure interface ----------------------
133 
parseContents(Parser parser)134    public void parseContents(Parser parser) {
135         final int count = _attributes.getLength();
136 
137         for (int i = 0; i < count; i++) {
138             final String name = _attributes.getQName(i);
139             final String value = _attributes.getValue(i);
140 
141             if (name.equals("value")) {
142                 _value = parser.parseExpression(this, name, null);
143             }
144             else if (name.equals("count")) {
145                 _count = parser.parsePattern(this, name, null);
146             }
147             else if (name.equals("from")) {
148                 _from = parser.parsePattern(this, name, null);
149             }
150             else if (name.equals("level")) {
151                 if (value.equals("single")) {
152                     _level = LEVEL_SINGLE;
153                 }
154                 else if (value.equals("multiple")) {
155                     _level = LEVEL_MULTIPLE;
156                 }
157                 else if (value.equals("any")) {
158                     _level = LEVEL_ANY;
159                 }
160             }
161             else if (name.equals("format")) {
162                 _format = new AttributeValueTemplate(value, parser, this);
163                 _formatNeeded = true;
164             }
165             else if (name.equals("lang")) {
166                 _lang = new AttributeValueTemplate(value, parser, this);
167                 _formatNeeded = true;
168             }
169             else if (name.equals("letter-value")) {
170                 _letterValue = new AttributeValueTemplate(value, parser, this);
171                 _formatNeeded = true;
172             }
173             else if (name.equals("grouping-separator")) {
174                 _groupingSeparator = new AttributeValueTemplate(value, parser, this);
175                 _formatNeeded = true;
176             }
177             else if (name.equals("grouping-size")) {
178                 _groupingSize = new AttributeValueTemplate(value, parser, this);
179                 _formatNeeded = true;
180             }
181         }
182     }
183 
typeCheck(SymbolTable stable)184     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
185         if (_value != null) {
186             Type tvalue = _value.typeCheck(stable);
187             if (tvalue instanceof RealType == false) {
188                 _value = new CastExpr(_value, Type.Real);
189             }
190         }
191         if (_count != null) {
192             _count.typeCheck(stable);
193         }
194         if (_from != null) {
195             _from.typeCheck(stable);
196         }
197         if (_format != null) {
198             _format.typeCheck(stable);
199         }
200         if (_lang != null) {
201             _lang.typeCheck(stable);
202         }
203         if (_letterValue != null) {
204             _letterValue.typeCheck(stable);
205         }
206         if (_groupingSeparator != null) {
207             _groupingSeparator.typeCheck(stable);
208         }
209         if (_groupingSize != null) {
210             _groupingSize.typeCheck(stable);
211         }
212         return Type.Void;
213     }
214 
215     /**
216      * True if the has specified a value for this instance of number.
217      */
hasValue()218     public boolean hasValue() {
219         return _value != null;
220     }
221 
222     /**
223      * Returns <tt>true</tt> if this instance of number has neither
224      * a from nor a count pattern.
225      */
isDefault()226     public boolean isDefault() {
227         return _from == null && _count == null;
228     }
229 
compileDefault(ClassGenerator classGen, MethodGenerator methodGen)230     private void compileDefault(ClassGenerator classGen,
231                                 MethodGenerator methodGen) {
232         int index;
233         ConstantPoolGen cpg = classGen.getConstantPool();
234         InstructionList il = methodGen.getInstructionList();
235 
236         int[] fieldIndexes = getXSLTC().getNumberFieldIndexes();
237 
238         if (fieldIndexes[_level] == -1) {
239             Field defaultNode = new Field(ACC_PRIVATE,
240                                           cpg.addUtf8(FieldNames[_level]),
241                                           cpg.addUtf8(NODE_COUNTER_SIG),
242                                           null,
243                                           cpg.getConstantPool());
244 
245             // Add a new private field to this class
246             classGen.addField(defaultNode);
247 
248             // Get a reference to the newly added field
249             fieldIndexes[_level] = cpg.addFieldref(classGen.getClassName(),
250                                                    FieldNames[_level],
251                                                    NODE_COUNTER_SIG);
252         }
253 
254         // Check if field is initialized (runtime)
255         il.append(classGen.loadTranslet());
256         il.append(new GETFIELD(fieldIndexes[_level]));
257         final BranchHandle ifBlock1 = il.append(new IFNONNULL(null));
258 
259         // Create an instance of DefaultNodeCounter
260         index = cpg.addMethodref(ClassNames[_level],
261                                  "getDefaultNodeCounter",
262                                  "(" + TRANSLET_INTF_SIG
263                                  + DOM_INTF_SIG
264                                  + NODE_ITERATOR_SIG
265                                  + ")" + NODE_COUNTER_SIG);
266         il.append(classGen.loadTranslet());
267         il.append(methodGen.loadDOM());
268         il.append(methodGen.loadIterator());
269         il.append(new INVOKESTATIC(index));
270         il.append(DUP);
271 
272         // Store the node counter in the field
273         il.append(classGen.loadTranslet());
274         il.append(SWAP);
275         il.append(new PUTFIELD(fieldIndexes[_level]));
276         final BranchHandle ifBlock2 = il.append(new GOTO(null));
277 
278         // Backpatch conditionals
279         ifBlock1.setTarget(il.append(classGen.loadTranslet()));
280         il.append(new GETFIELD(fieldIndexes[_level]));
281 
282         ifBlock2.setTarget(il.append(NOP));
283     }
284 
285     /**
286      * Compiles a constructor for the class <tt>_className</tt> that
287      * inherits from {Any,Single,Multiple}NodeCounter. This constructor
288      * simply calls the same constructor in the super class.
289      */
compileConstructor(ClassGenerator classGen)290     private void compileConstructor(ClassGenerator classGen) {
291         MethodGenerator cons;
292         final InstructionList il = new InstructionList();
293         final ConstantPoolGen cpg = classGen.getConstantPool();
294 
295         cons = new MethodGenerator(ACC_PUBLIC,
296                                    com.sun.org.apache.bcel.internal.generic.Type.VOID,
297                                    new com.sun.org.apache.bcel.internal.generic.Type[] {
298                                        Util.getJCRefType(TRANSLET_INTF_SIG),
299                                        Util.getJCRefType(DOM_INTF_SIG),
300                                        Util.getJCRefType(NODE_ITERATOR_SIG),
301                                        com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN
302                                    },
303                                    new String[] {
304                                        "dom",
305                                        "translet",
306                                        "iterator",
307                                        "hasFrom"
308                                    },
309                                    "<init>", _className, il, cpg);
310 
311         il.append(ALOAD_0);         // this
312         il.append(ALOAD_1);         // translet
313         il.append(ALOAD_2);         // DOM
314         il.append(new ALOAD(3));    // iterator
315         il.append(new ILOAD(4));    // hasFrom
316 
317         int index = cpg.addMethodref(ClassNames[_level],
318                                      "<init>",
319                                      "(" + TRANSLET_INTF_SIG
320                                      + DOM_INTF_SIG
321                                      + NODE_ITERATOR_SIG
322                                      + "Z)V");
323         il.append(new INVOKESPECIAL(index));
324         il.append(RETURN);
325 
326         classGen.addMethod(cons);
327     }
328 
329     /**
330      * This method compiles code that is common to matchesFrom() and
331      * matchesCount() in the auxillary class.
332      */
compileLocals(NodeCounterGenerator nodeCounterGen, MatchGenerator matchGen, InstructionList il)333     private void compileLocals(NodeCounterGenerator nodeCounterGen,
334                                MatchGenerator matchGen,
335                                InstructionList il)
336     {
337         int field;
338         LocalVariableGen local;
339         ConstantPoolGen cpg = nodeCounterGen.getConstantPool();
340 
341         // Get NodeCounter._iterator and store locally
342         local = matchGen.addLocalVariable("iterator",
343                                           Util.getJCRefType(NODE_ITERATOR_SIG),
344                                           null, null);
345         field = cpg.addFieldref(NODE_COUNTER, "_iterator",
346                                 ITERATOR_FIELD_SIG);
347         il.append(ALOAD_0); // 'this' pointer on stack
348         il.append(new GETFIELD(field));
349         local.setStart(il.append(new ASTORE(local.getIndex())));
350         matchGen.setIteratorIndex(local.getIndex());
351 
352         // Get NodeCounter._translet and store locally
353         local = matchGen.addLocalVariable("translet",
354                                   Util.getJCRefType(TRANSLET_SIG),
355                                   null, null);
356         field = cpg.addFieldref(NODE_COUNTER, "_translet",
357                                 "Lcom/sun/org/apache/xalan/internal/xsltc/Translet;");
358         il.append(ALOAD_0); // 'this' pointer on stack
359         il.append(new GETFIELD(field));
360         il.append(new CHECKCAST(cpg.addClass(TRANSLET_CLASS)));
361         local.setStart(il.append(new ASTORE(local.getIndex())));
362         nodeCounterGen.setTransletIndex(local.getIndex());
363 
364         // Get NodeCounter._document and store locally
365         local = matchGen.addLocalVariable("document",
366                                           Util.getJCRefType(DOM_INTF_SIG),
367                                           null, null);
368         field = cpg.addFieldref(_className, "_document", DOM_INTF_SIG);
369         il.append(ALOAD_0); // 'this' pointer on stack
370         il.append(new GETFIELD(field));
371         // Make sure we have the correct DOM type on the stack!!!
372         local.setStart(il.append(new ASTORE(local.getIndex())));
373         matchGen.setDomIndex(local.getIndex());
374     }
375 
compilePatterns(ClassGenerator classGen, MethodGenerator methodGen)376     private void compilePatterns(ClassGenerator classGen,
377                                  MethodGenerator methodGen)
378     {
379         int current;
380         int field;
381         LocalVariableGen local;
382         MatchGenerator matchGen;
383         NodeCounterGenerator nodeCounterGen;
384 
385         _className = getXSLTC().getHelperClassName();
386         nodeCounterGen = new NodeCounterGenerator(_className,
387                                                   ClassNames[_level],
388                                                   toString(),
389                                                   ACC_PUBLIC | ACC_SUPER,
390                                                   null,
391                                                   classGen.getStylesheet());
392         InstructionList il = null;
393         ConstantPoolGen cpg = nodeCounterGen.getConstantPool();
394 
395         // Add a new instance variable for each var in closure
396         final int closureLen = (_closureVars == null) ? 0 :
397             _closureVars.size();
398 
399         for (int i = 0; i < closureLen; i++) {
400             VariableBase var =
401                 ((VariableRefBase) _closureVars.get(i)).getVariable();
402 
403             nodeCounterGen.addField(new Field(ACC_PUBLIC,
404                                         cpg.addUtf8(var.getEscapedName()),
405                                         cpg.addUtf8(var.getType().toSignature()),
406                                         null, cpg.getConstantPool()));
407         }
408 
409         // Add a single constructor to the class
410         compileConstructor(nodeCounterGen);
411 
412         /*
413          * Compile method matchesFrom()
414          */
415         if (_from != null) {
416             il = new InstructionList();
417             matchGen =
418                 new MatchGenerator(ACC_PUBLIC | ACC_FINAL,
419                                    com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
420                                    new com.sun.org.apache.bcel.internal.generic.Type[] {
421                                        com.sun.org.apache.bcel.internal.generic.Type.INT,
422                                    },
423                                    new String[] {
424                                        "node",
425                                    },
426                                    "matchesFrom", _className, il, cpg);
427 
428             compileLocals(nodeCounterGen,matchGen,il);
429 
430             // Translate Pattern
431             il.append(matchGen.loadContextNode());
432             _from.translate(nodeCounterGen, matchGen);
433             _from.synthesize(nodeCounterGen, matchGen);
434             il.append(IRETURN);
435 
436             nodeCounterGen.addMethod(matchGen);
437         }
438 
439         /*
440          * Compile method matchesCount()
441          */
442         if (_count != null) {
443             il = new InstructionList();
444             matchGen = new MatchGenerator(ACC_PUBLIC | ACC_FINAL,
445                                           com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
446                                           new com.sun.org.apache.bcel.internal.generic.Type[] {
447                                               com.sun.org.apache.bcel.internal.generic.Type.INT,
448                                           },
449                                           new String[] {
450                                               "node",
451                                           },
452                                           "matchesCount", _className, il, cpg);
453 
454             compileLocals(nodeCounterGen,matchGen,il);
455 
456             // Translate Pattern
457             il.append(matchGen.loadContextNode());
458             _count.translate(nodeCounterGen, matchGen);
459             _count.synthesize(nodeCounterGen, matchGen);
460 
461             il.append(IRETURN);
462 
463             nodeCounterGen.addMethod(matchGen);
464         }
465 
466         getXSLTC().dumpClass(nodeCounterGen.getJavaClass());
467 
468         // Push an instance of the newly created class
469         cpg = classGen.getConstantPool();
470         il = methodGen.getInstructionList();
471 
472         final int index = cpg.addMethodref(_className, "<init>",
473                                            "(" + TRANSLET_INTF_SIG
474                                            + DOM_INTF_SIG
475                                            + NODE_ITERATOR_SIG
476                                            + "Z)V");
477         il.append(new NEW(cpg.addClass(_className)));
478         il.append(DUP);
479         il.append(classGen.loadTranslet());
480         il.append(methodGen.loadDOM());
481         il.append(methodGen.loadIterator());
482         il.append(_from != null ? ICONST_1 : ICONST_0);
483         il.append(new INVOKESPECIAL(index));
484 
485         // Initialize closure variables
486         for (int i = 0; i < closureLen; i++) {
487             final VariableRefBase varRef = (VariableRefBase) _closureVars.get(i);
488             final VariableBase var = varRef.getVariable();
489             final Type varType = var.getType();
490 
491             // Store variable in new closure
492             il.append(DUP);
493             il.append(var.loadInstruction());
494             il.append(new PUTFIELD(
495                     cpg.addFieldref(_className, var.getEscapedName(),
496                         varType.toSignature())));
497         }
498     }
499 
translate(ClassGenerator classGen, MethodGenerator methodGen)500     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
501         int index;
502         final ConstantPoolGen cpg = classGen.getConstantPool();
503         final InstructionList il = methodGen.getInstructionList();
504 
505         // Push "this" for the call to characters()
506         il.append(classGen.loadTranslet());
507 
508         if (hasValue()) {
509             compileDefault(classGen, methodGen);
510             _value.translate(classGen, methodGen);
511 
512             // Using java.lang.Math.floor(number + 0.5) to return a double value
513             il.append(new PUSH(cpg, 0.5));
514             il.append(DADD);
515             index = cpg.addMethodref(MATH_CLASS, "floor", "(D)D");
516             il.append(new INVOKESTATIC(index));
517 
518             // Call setValue on the node counter
519             index = cpg.addMethodref(NODE_COUNTER,
520                                      "setValue",
521                                      "(D)" + NODE_COUNTER_SIG);
522             il.append(new INVOKEVIRTUAL(index));
523         }
524         else if (isDefault()) {
525             compileDefault(classGen, methodGen);
526         }
527         else {
528             compilePatterns(classGen, methodGen);
529         }
530 
531         // Call setStartNode()
532         if (!hasValue()) {
533             il.append(methodGen.loadContextNode());
534             index = cpg.addMethodref(NODE_COUNTER,
535                                      SET_START_NODE,
536                                      "(I)" + NODE_COUNTER_SIG);
537             il.append(new INVOKEVIRTUAL(index));
538         }
539 
540         // Call getCounter() with or without args
541         if (_formatNeeded) {
542             if (_format != null) {
543                 _format.translate(classGen, methodGen);
544             }
545             else {
546                 il.append(new PUSH(cpg, "1"));
547             }
548 
549             if (_lang != null) {
550                 _lang.translate(classGen, methodGen);
551             }
552             else {
553                 il.append(new PUSH(cpg, "en"));         // TODO ??
554             }
555 
556             if (_letterValue != null) {
557                 _letterValue.translate(classGen, methodGen);
558             }
559             else {
560                 il.append(new PUSH(cpg, Constants.EMPTYSTRING));
561             }
562 
563             if (_groupingSeparator != null) {
564                 _groupingSeparator.translate(classGen, methodGen);
565             }
566             else {
567                 il.append(new PUSH(cpg, Constants.EMPTYSTRING));
568             }
569 
570             if (_groupingSize != null) {
571                 _groupingSize.translate(classGen, methodGen);
572             }
573             else {
574                 il.append(new PUSH(cpg, "0"));
575             }
576 
577             index = cpg.addMethodref(NODE_COUNTER, "getCounter",
578                                      "(" + STRING_SIG + STRING_SIG
579                                      + STRING_SIG + STRING_SIG
580                                      + STRING_SIG + ")" + STRING_SIG);
581             il.append(new INVOKEVIRTUAL(index));
582         }
583         else {
584             index = cpg.addMethodref(NODE_COUNTER, "setDefaultFormatting",
585                                      "()" + NODE_COUNTER_SIG);
586             il.append(new INVOKEVIRTUAL(index));
587 
588             index = cpg.addMethodref(NODE_COUNTER, "getCounter",
589                                      "()" + STRING_SIG);
590             il.append(new INVOKEVIRTUAL(index));
591         }
592 
593         // Output the resulting string to the handler
594         il.append(methodGen.loadHandler());
595         index = cpg.addMethodref(TRANSLET_CLASS,
596                                  CHARACTERSW,
597                                  CHARACTERSW_SIG);
598         il.append(new INVOKEVIRTUAL(index));
599     }
600 }
601