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