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