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 }