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 }