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