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: Sort.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 import java.util.Vector; 026 027 import org.apache.bcel.classfile.Field; 028 import org.apache.bcel.generic.ALOAD; 029 import org.apache.bcel.generic.ANEWARRAY; 030 import org.apache.bcel.generic.ASTORE; 031 import org.apache.bcel.generic.CHECKCAST; 032 import org.apache.bcel.generic.ConstantPoolGen; 033 import org.apache.bcel.generic.GETFIELD; 034 import org.apache.bcel.generic.ILOAD; 035 import org.apache.bcel.generic.INVOKEINTERFACE; 036 import org.apache.bcel.generic.INVOKESPECIAL; 037 import org.apache.bcel.generic.InstructionHandle; 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.NOP; 042 import org.apache.bcel.generic.PUSH; 043 import org.apache.bcel.generic.PUTFIELD; 044 import org.apache.bcel.generic.TABLESWITCH; 045 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 046 import org.apache.xalan.xsltc.compiler.util.CompareGenerator; 047 import org.apache.xalan.xsltc.compiler.util.ErrorMsg; 048 import org.apache.xalan.xsltc.compiler.util.IntType; 049 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 050 import org.apache.xalan.xsltc.compiler.util.NodeSortRecordFactGenerator; 051 import org.apache.xalan.xsltc.compiler.util.NodeSortRecordGenerator; 052 import org.apache.xalan.xsltc.compiler.util.StringType; 053 import org.apache.xalan.xsltc.compiler.util.Type; 054 import org.apache.xalan.xsltc.compiler.util.TypeCheckError; 055 import org.apache.xalan.xsltc.compiler.util.Util; 056 import org.apache.xml.dtm.Axis; 057 058 059 /** 060 * @author Jacek Ambroziak 061 * @author Santiago Pericas-Geertsen 062 * @author Morten Jorgensen 063 */ 064 final class Sort extends Instruction implements Closure { 065 066 private Expression _select; 067 private AttributeValue _order; 068 private AttributeValue _caseOrder; 069 private AttributeValue _dataType; 070 private String _lang; // bug! see 26869 071 072 private String _data = null; 073 074 075 private String _className = null; 076 private ArrayList _closureVars = null; 077 private boolean _needsSortRecordFactory = false; 078 079 // -- Begin Closure interface -------------------- 080 081 /** 082 * Returns true if this closure is compiled in an inner class (i.e. 083 * if this is a real closure). 084 */ 085 public boolean inInnerClass() { 086 return (_className != null); 087 } 088 089 /** 090 * Returns a reference to its parent closure or null if outermost. 091 */ 092 public Closure getParentClosure() { 093 return null; 094 } 095 096 /** 097 * Returns the name of the auxiliary class or null if this predicate 098 * is compiled inside the Translet. 099 */ 100 public String getInnerClassName() { 101 return _className; 102 } 103 104 /** 105 * Add new variable to the closure. 106 */ 107 public void addVariable(VariableRefBase variableRef) { 108 if (_closureVars == null) { 109 _closureVars = new ArrayList(); 110 } 111 112 // Only one reference per variable 113 if (!_closureVars.contains(variableRef)) { 114 _closureVars.add(variableRef); 115 _needsSortRecordFactory = true; 116 } 117 } 118 119 // -- End Closure interface ---------------------- 120 121 private void setInnerClassName(String className) { 122 _className = className; 123 } 124 125 /** 126 * Parse the attributes of the xsl:sort element 127 */ 128 public void parseContents(Parser parser) { 129 130 final SyntaxTreeNode parent = getParent(); 131 if (!(parent instanceof ApplyTemplates) && 132 !(parent instanceof ForEach)) { 133 reportError(this, parser, ErrorMsg.STRAY_SORT_ERR, null); 134 return; 135 } 136 137 // Parse the select expression (node string value if no expression) 138 _select = parser.parseExpression(this, "select", "string(.)"); 139 140 // Get the sort order; default is 'ascending' 141 String val = getAttribute("order"); 142 if (val.length() == 0) val = "ascending"; 143 _order = AttributeValue.create(this, val, parser); 144 145 // Get the sort data type; default is text 146 val = getAttribute("data-type"); 147 if (val.length() == 0) { 148 try { 149 final Type type = _select.typeCheck(parser.getSymbolTable()); 150 if (type instanceof IntType) 151 val = "number"; 152 else 153 val = "text"; 154 } 155 catch (TypeCheckError e) { 156 val = "text"; 157 } 158 } 159 _dataType = AttributeValue.create(this, val, parser); 160 161 _lang = getAttribute("lang"); // bug! see 26869 162 // val = getAttribute("lang"); 163 // _lang = AttributeValue.create(this, val, parser); 164 // Get the case order; default is language dependant 165 val = getAttribute("case-order"); 166 _caseOrder = AttributeValue.create(this, val, parser); 167 168 } 169 170 /** 171 * Run type checks on the attributes; expression must return a string 172 * which we will use as a sort key 173 */ 174 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 175 final Type tselect = _select.typeCheck(stable); 176 177 // If the sort data-type is not set we use the natural data-type 178 // of the data we will sort 179 if (!(tselect instanceof StringType)) { 180 _select = new CastExpr(_select, Type.String); 181 } 182 183 _order.typeCheck(stable); 184 _caseOrder.typeCheck(stable); 185 _dataType.typeCheck(stable); 186 return Type.Void; 187 } 188 189 /** 190 * These two methods are needed in the static methods that compile the 191 * overloaded NodeSortRecord.compareType() and NodeSortRecord.sortOrder() 192 */ 193 public void translateSortType(ClassGenerator classGen, 194 MethodGenerator methodGen) { 195 _dataType.translate(classGen, methodGen); 196 } 197 198 public void translateSortOrder(ClassGenerator classGen, 199 MethodGenerator methodGen) { 200 _order.translate(classGen, methodGen); 201 } 202 203 public void translateCaseOrder(ClassGenerator classGen, 204 MethodGenerator methodGen) { 205 _caseOrder.translate(classGen, methodGen); 206 } 207 208 public void translateLang(ClassGenerator classGen, 209 MethodGenerator methodGen) { 210 final ConstantPoolGen cpg = classGen.getConstantPool(); 211 final InstructionList il = methodGen.getInstructionList(); 212 il.append(new PUSH(cpg, _lang)); // bug! see 26869 213 } 214 215 /** 216 * This method compiles code for the select expression for this 217 * xsl:sort element. The method is called from the static code-generating 218 * methods in this class. 219 */ 220 public void translateSelect(ClassGenerator classGen, 221 MethodGenerator methodGen) { 222 _select.translate(classGen,methodGen); 223 } 224 225 /** 226 * This method should not produce any code 227 */ 228 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 229 // empty 230 } 231 232 /** 233 * Compiles code that instantiates a SortingIterator object. 234 * This object's constructor needs referencdes to the current iterator 235 * and a node sort record producing objects as its parameters. 236 */ 237 public static void translateSortIterator(ClassGenerator classGen, 238 MethodGenerator methodGen, 239 Expression nodeSet, 240 Vector sortObjects) 241 { 242 final ConstantPoolGen cpg = classGen.getConstantPool(); 243 final InstructionList il = methodGen.getInstructionList(); 244 245 // SortingIterator.SortingIterator(NodeIterator,NodeSortRecordFactory); 246 final int init = cpg.addMethodref(SORT_ITERATOR, "<init>", 247 "(" 248 + NODE_ITERATOR_SIG 249 + NODE_SORT_FACTORY_SIG 250 + ")V"); 251 252 // Backwards branches are prohibited if an uninitialized object is 253 // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed. 254 // We don't know whether this code might contain backwards branches 255 // so we mustn't create the new object until after we've created 256 // the suspect arguments to its constructor. Instead we calculate 257 // the values of the arguments to the constructor first, store them 258 // in temporary variables, create the object and reload the 259 // arguments from the temporaries to avoid the problem. 260 261 LocalVariableGen nodesTemp = 262 methodGen.addLocalVariable("sort_tmp1", 263 Util.getJCRefType(NODE_ITERATOR_SIG), 264 null, null); 265 266 LocalVariableGen sortRecordFactoryTemp = 267 methodGen.addLocalVariable("sort_tmp2", 268 Util.getJCRefType(NODE_SORT_FACTORY_SIG), 269 null, null); 270 271 // Get the current node iterator 272 if (nodeSet == null) { // apply-templates default 273 final int children = cpg.addInterfaceMethodref(DOM_INTF, 274 "getAxisIterator", 275 "(I)"+ 276 NODE_ITERATOR_SIG); 277 il.append(methodGen.loadDOM()); 278 il.append(new PUSH(cpg, Axis.CHILD)); 279 il.append(new INVOKEINTERFACE(children, 2)); 280 } 281 else { 282 nodeSet.translate(classGen, methodGen); 283 } 284 285 nodesTemp.setStart(il.append(new ASTORE(nodesTemp.getIndex()))); 286 287 // Compile the code for the NodeSortRecord producing class and pass 288 // that as the last argument to the SortingIterator constructor. 289 compileSortRecordFactory(sortObjects, classGen, methodGen); 290 sortRecordFactoryTemp.setStart( 291 il.append(new ASTORE(sortRecordFactoryTemp.getIndex()))); 292 293 il.append(new NEW(cpg.addClass(SORT_ITERATOR))); 294 il.append(DUP); 295 nodesTemp.setEnd(il.append(new ALOAD(nodesTemp.getIndex()))); 296 sortRecordFactoryTemp.setEnd( 297 il.append(new ALOAD(sortRecordFactoryTemp.getIndex()))); 298 il.append(new INVOKESPECIAL(init)); 299 } 300 301 302 /** 303 * Compiles code that instantiates a NodeSortRecordFactory object which 304 * will produce NodeSortRecord objects of a specific type. 305 */ 306 public static void compileSortRecordFactory(Vector sortObjects, 307 ClassGenerator classGen, MethodGenerator methodGen) 308 { 309 String sortRecordClass = 310 compileSortRecord(sortObjects, classGen, methodGen); 311 312 boolean needsSortRecordFactory = false; 313 final int nsorts = sortObjects.size(); 314 for (int i = 0; i < nsorts; i++) { 315 final Sort sort = (Sort) sortObjects.elementAt(i); 316 needsSortRecordFactory |= sort._needsSortRecordFactory; 317 } 318 319 String sortRecordFactoryClass = NODE_SORT_FACTORY; 320 if (needsSortRecordFactory) { 321 sortRecordFactoryClass = 322 compileSortRecordFactory(sortObjects, classGen, methodGen, 323 sortRecordClass); 324 } 325 326 final ConstantPoolGen cpg = classGen.getConstantPool(); 327 final InstructionList il = methodGen.getInstructionList(); 328 329 // Backwards branches are prohibited if an uninitialized object is 330 // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed. 331 // We don't know whether this code might contain backwards branches 332 // so we mustn't create the new object until after we've created 333 // the suspect arguments to its constructor. Instead we calculate 334 // the values of the arguments to the constructor first, store them 335 // in temporary variables, create the object and reload the 336 // arguments from the temporaries to avoid the problem. 337 338 // Compile code that initializes the static _sortOrder 339 LocalVariableGen sortOrderTemp 340 = methodGen.addLocalVariable("sort_order_tmp", 341 Util.getJCRefType("[" + STRING_SIG), 342 null, null); 343 il.append(new PUSH(cpg, nsorts)); 344 il.append(new ANEWARRAY(cpg.addClass(STRING))); 345 for (int level = 0; level < nsorts; level++) { 346 final Sort sort = (Sort)sortObjects.elementAt(level); 347 il.append(DUP); 348 il.append(new PUSH(cpg, level)); 349 sort.translateSortOrder(classGen, methodGen); 350 il.append(AASTORE); 351 } 352 sortOrderTemp.setStart(il.append(new ASTORE(sortOrderTemp.getIndex()))); 353 354 LocalVariableGen sortTypeTemp 355 = methodGen.addLocalVariable("sort_type_tmp", 356 Util.getJCRefType("[" + STRING_SIG), 357 null, null); 358 il.append(new PUSH(cpg, nsorts)); 359 il.append(new ANEWARRAY(cpg.addClass(STRING))); 360 for (int level = 0; level < nsorts; level++) { 361 final Sort sort = (Sort)sortObjects.elementAt(level); 362 il.append(DUP); 363 il.append(new PUSH(cpg, level)); 364 sort.translateSortType(classGen, methodGen); 365 il.append(AASTORE); 366 } 367 sortTypeTemp.setStart(il.append(new ASTORE(sortTypeTemp.getIndex()))); 368 369 LocalVariableGen sortLangTemp 370 = methodGen.addLocalVariable("sort_lang_tmp", 371 Util.getJCRefType("[" + STRING_SIG), 372 null, null); 373 il.append(new PUSH(cpg, nsorts)); 374 il.append(new ANEWARRAY(cpg.addClass(STRING))); 375 for (int level = 0; level < nsorts; level++) { 376 final Sort sort = (Sort)sortObjects.elementAt(level); 377 il.append(DUP); 378 il.append(new PUSH(cpg, level)); 379 sort.translateLang(classGen, methodGen); 380 il.append(AASTORE); 381 } 382 sortLangTemp.setStart(il.append(new ASTORE(sortLangTemp.getIndex()))); 383 384 LocalVariableGen sortCaseOrderTemp 385 = methodGen.addLocalVariable("sort_case_order_tmp", 386 Util.getJCRefType("[" + STRING_SIG), 387 null, null); 388 il.append(new PUSH(cpg, nsorts)); 389 il.append(new ANEWARRAY(cpg.addClass(STRING))); 390 for (int level = 0; level < nsorts; level++) { 391 final Sort sort = (Sort)sortObjects.elementAt(level); 392 il.append(DUP); 393 il.append(new PUSH(cpg, level)); 394 sort.translateCaseOrder(classGen, methodGen); 395 il.append(AASTORE); 396 } 397 sortCaseOrderTemp.setStart( 398 il.append(new ASTORE(sortCaseOrderTemp.getIndex()))); 399 400 il.append(new NEW(cpg.addClass(sortRecordFactoryClass))); 401 il.append(DUP); 402 il.append(methodGen.loadDOM()); 403 il.append(new PUSH(cpg, sortRecordClass)); 404 il.append(classGen.loadTranslet()); 405 406 sortOrderTemp.setEnd(il.append(new ALOAD(sortOrderTemp.getIndex()))); 407 sortTypeTemp.setEnd(il.append(new ALOAD(sortTypeTemp.getIndex()))); 408 sortLangTemp.setEnd(il.append(new ALOAD(sortLangTemp.getIndex()))); 409 sortCaseOrderTemp.setEnd( 410 il.append(new ALOAD(sortCaseOrderTemp.getIndex()))); 411 412 il.append(new INVOKESPECIAL( 413 cpg.addMethodref(sortRecordFactoryClass, "<init>", 414 "(" + DOM_INTF_SIG 415 + STRING_SIG 416 + TRANSLET_INTF_SIG 417 + "[" + STRING_SIG 418 + "[" + STRING_SIG 419 + "[" + STRING_SIG 420 + "[" + STRING_SIG + ")V"))); 421 422 // Initialize closure variables in sortRecordFactory 423 final ArrayList dups = new ArrayList(); 424 425 for (int j = 0; j < nsorts; j++) { 426 final Sort sort = (Sort) sortObjects.get(j); 427 final int length = (sort._closureVars == null) ? 0 : 428 sort._closureVars.size(); 429 430 for (int i = 0; i < length; i++) { 431 VariableRefBase varRef = (VariableRefBase) sort._closureVars.get(i); 432 433 // Discard duplicate variable references 434 if (dups.contains(varRef)) continue; 435 436 final VariableBase var = varRef.getVariable(); 437 438 // Store variable in new closure 439 il.append(DUP); 440 il.append(var.loadInstruction()); 441 il.append(new PUTFIELD( 442 cpg.addFieldref(sortRecordFactoryClass, var.getEscapedName(), 443 var.getType().toSignature()))); 444 dups.add(varRef); 445 } 446 } 447 } 448 449 public static String compileSortRecordFactory(Vector sortObjects, 450 ClassGenerator classGen, MethodGenerator methodGen, 451 String sortRecordClass) 452 { 453 final XSLTC xsltc = ((Sort)sortObjects.firstElement()).getXSLTC(); 454 final String className = xsltc.getHelperClassName(); 455 456 final NodeSortRecordFactGenerator sortRecordFactory = 457 new NodeSortRecordFactGenerator(className, 458 NODE_SORT_FACTORY, 459 className + ".java", 460 ACC_PUBLIC | ACC_SUPER | ACC_FINAL, 461 new String[] {}, 462 classGen.getStylesheet()); 463 464 ConstantPoolGen cpg = sortRecordFactory.getConstantPool(); 465 466 // Add a new instance variable for each var in closure 467 final int nsorts = sortObjects.size(); 468 final ArrayList dups = new ArrayList(); 469 470 for (int j = 0; j < nsorts; j++) { 471 final Sort sort = (Sort) sortObjects.get(j); 472 final int length = (sort._closureVars == null) ? 0 : 473 sort._closureVars.size(); 474 475 for (int i = 0; i < length; i++) { 476 final VariableRefBase varRef = (VariableRefBase) sort._closureVars.get(i); 477 478 // Discard duplicate variable references 479 if (dups.contains(varRef)) continue; 480 481 final VariableBase var = varRef.getVariable(); 482 sortRecordFactory.addField(new Field(ACC_PUBLIC, 483 cpg.addUtf8(var.getEscapedName()), 484 cpg.addUtf8(var.getType().toSignature()), 485 null, cpg.getConstantPool())); 486 dups.add(varRef); 487 } 488 } 489 490 // Define a constructor for this class 491 final org.apache.bcel.generic.Type[] argTypes = 492 new org.apache.bcel.generic.Type[7]; 493 argTypes[0] = Util.getJCRefType(DOM_INTF_SIG); 494 argTypes[1] = Util.getJCRefType(STRING_SIG); 495 argTypes[2] = Util.getJCRefType(TRANSLET_INTF_SIG); 496 argTypes[3] = Util.getJCRefType("[" + STRING_SIG); 497 argTypes[4] = Util.getJCRefType("[" + STRING_SIG); 498 argTypes[5] = Util.getJCRefType("[" + STRING_SIG); 499 argTypes[6] = Util.getJCRefType("[" + STRING_SIG); 500 501 final String[] argNames = new String[7]; 502 argNames[0] = DOCUMENT_PNAME; 503 argNames[1] = "className"; 504 argNames[2] = TRANSLET_PNAME; 505 argNames[3] = "order"; 506 argNames[4] = "type"; 507 argNames[5] = "lang"; 508 argNames[6] = "case_order"; 509 510 511 InstructionList il = new InstructionList(); 512 final MethodGenerator constructor = 513 new MethodGenerator(ACC_PUBLIC, 514 org.apache.bcel.generic.Type.VOID, 515 argTypes, argNames, "<init>", 516 className, il, cpg); 517 518 // Push all parameters onto the stack and called super.<init>() 519 il.append(ALOAD_0); 520 il.append(ALOAD_1); 521 il.append(ALOAD_2); 522 il.append(new ALOAD(3)); 523 il.append(new ALOAD(4)); 524 il.append(new ALOAD(5)); 525 il.append(new ALOAD(6)); 526 il.append(new ALOAD(7)); 527 il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_FACTORY, 528 "<init>", 529 "(" + DOM_INTF_SIG 530 + STRING_SIG 531 + TRANSLET_INTF_SIG 532 + "[" + STRING_SIG 533 + "[" + STRING_SIG 534 + "[" + STRING_SIG 535 + "[" + STRING_SIG + ")V"))); 536 il.append(RETURN); 537 538 // Override the definition of makeNodeSortRecord() 539 il = new InstructionList(); 540 final MethodGenerator makeNodeSortRecord = 541 new MethodGenerator(ACC_PUBLIC, 542 Util.getJCRefType(NODE_SORT_RECORD_SIG), 543 new org.apache.bcel.generic.Type[] { 544 org.apache.bcel.generic.Type.INT, 545 org.apache.bcel.generic.Type.INT }, 546 new String[] { "node", "last" }, "makeNodeSortRecord", 547 className, il, cpg); 548 549 il.append(ALOAD_0); 550 il.append(ILOAD_1); 551 il.append(ILOAD_2); 552 il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_FACTORY, 553 "makeNodeSortRecord", "(II)" + NODE_SORT_RECORD_SIG))); 554 il.append(DUP); 555 il.append(new CHECKCAST(cpg.addClass(sortRecordClass))); 556 557 // Initialize closure in record class 558 final int ndups = dups.size(); 559 for (int i = 0; i < ndups; i++) { 560 final VariableRefBase varRef = (VariableRefBase) dups.get(i); 561 final VariableBase var = varRef.getVariable(); 562 final Type varType = var.getType(); 563 564 il.append(DUP); 565 566 // Get field from factory class 567 il.append(ALOAD_0); 568 il.append(new GETFIELD( 569 cpg.addFieldref(className, 570 var.getEscapedName(), varType.toSignature()))); 571 572 // Put field in record class 573 il.append(new PUTFIELD( 574 cpg.addFieldref(sortRecordClass, 575 var.getEscapedName(), varType.toSignature()))); 576 } 577 il.append(POP); 578 il.append(ARETURN); 579 580 constructor.setMaxLocals(); 581 constructor.setMaxStack(); 582 sortRecordFactory.addMethod(constructor); 583 makeNodeSortRecord.setMaxLocals(); 584 makeNodeSortRecord.setMaxStack(); 585 sortRecordFactory.addMethod(makeNodeSortRecord); 586 xsltc.dumpClass(sortRecordFactory.getJavaClass()); 587 588 return className; 589 } 590 591 /** 592 * Create a new auxillary class extending NodeSortRecord. 593 */ 594 private static String compileSortRecord(Vector sortObjects, 595 ClassGenerator classGen, 596 MethodGenerator methodGen) { 597 final XSLTC xsltc = ((Sort)sortObjects.firstElement()).getXSLTC(); 598 final String className = xsltc.getHelperClassName(); 599 600 // This generates a new class for handling this specific sort 601 final NodeSortRecordGenerator sortRecord = 602 new NodeSortRecordGenerator(className, 603 NODE_SORT_RECORD, 604 "sort$0.java", 605 ACC_PUBLIC | ACC_SUPER | ACC_FINAL, 606 new String[] {}, 607 classGen.getStylesheet()); 608 609 final ConstantPoolGen cpg = sortRecord.getConstantPool(); 610 611 // Add a new instance variable for each var in closure 612 final int nsorts = sortObjects.size(); 613 final ArrayList dups = new ArrayList(); 614 615 for (int j = 0; j < nsorts; j++) { 616 final Sort sort = (Sort) sortObjects.get(j); 617 618 // Set the name of the inner class in this sort object 619 sort.setInnerClassName(className); 620 621 final int length = (sort._closureVars == null) ? 0 : 622 sort._closureVars.size(); 623 for (int i = 0; i < length; i++) { 624 final VariableRefBase varRef = (VariableRefBase) sort._closureVars.get(i); 625 626 // Discard duplicate variable references 627 if (dups.contains(varRef)) continue; 628 629 final VariableBase var = varRef.getVariable(); 630 sortRecord.addField(new Field(ACC_PUBLIC, 631 cpg.addUtf8(var.getEscapedName()), 632 cpg.addUtf8(var.getType().toSignature()), 633 null, cpg.getConstantPool())); 634 dups.add(varRef); 635 } 636 } 637 638 MethodGenerator init = compileInit(sortObjects, sortRecord, 639 cpg, className); 640 MethodGenerator extract = compileExtract(sortObjects, sortRecord, 641 cpg, className); 642 sortRecord.addMethod(init); 643 sortRecord.addMethod(extract); 644 645 xsltc.dumpClass(sortRecord.getJavaClass()); 646 return className; 647 } 648 649 /** 650 * Create a constructor for the new class. Updates the reference to the 651 * collator in the super calls only when the stylesheet specifies a new 652 * language in xsl:sort. 653 */ 654 private static MethodGenerator compileInit(Vector sortObjects, 655 NodeSortRecordGenerator sortRecord, 656 ConstantPoolGen cpg, 657 String className) 658 { 659 final InstructionList il = new InstructionList(); 660 final MethodGenerator init = 661 new MethodGenerator(ACC_PUBLIC, 662 org.apache.bcel.generic.Type.VOID, 663 null, null, "<init>", className, 664 il, cpg); 665 666 // Call the constructor in the NodeSortRecord superclass 667 il.append(ALOAD_0); 668 il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_RECORD, 669 "<init>", "()V"))); 670 671 672 673 il.append(RETURN); 674 675 return init; 676 } 677 678 679 /** 680 * Compiles a method that overloads NodeSortRecord.extractValueFromDOM() 681 */ 682 private static MethodGenerator compileExtract(Vector sortObjects, 683 NodeSortRecordGenerator sortRecord, 684 ConstantPoolGen cpg, 685 String className) { 686 final InstructionList il = new InstructionList(); 687 688 // String NodeSortRecord.extractValueFromDOM(dom,node,level); 689 final CompareGenerator extractMethod = 690 new CompareGenerator(ACC_PUBLIC | ACC_FINAL, 691 org.apache.bcel.generic.Type.STRING, 692 new org.apache.bcel.generic.Type[] { 693 Util.getJCRefType(DOM_INTF_SIG), 694 org.apache.bcel.generic.Type.INT, 695 org.apache.bcel.generic.Type.INT, 696 Util.getJCRefType(TRANSLET_SIG), 697 org.apache.bcel.generic.Type.INT 698 }, 699 new String[] { "dom", 700 "current", 701 "level", 702 "translet", 703 "last" 704 }, 705 "extractValueFromDOM", className, il, cpg); 706 707 // Values needed for the switch statement 708 final int levels = sortObjects.size(); 709 final int match[] = new int[levels]; 710 final InstructionHandle target[] = new InstructionHandle[levels]; 711 InstructionHandle tblswitch = null; 712 713 // Compile switch statement only if the key has multiple levels 714 if (levels > 1) { 715 // Put the parameter to the swtich statement on the stack 716 il.append(new ILOAD(extractMethod.getLocalIndex("level"))); 717 // Append the switch statement here later on 718 tblswitch = il.append(new NOP()); 719 } 720 721 // Append all the cases for the switch statment 722 for (int level = 0; level < levels; level++) { 723 match[level] = level; 724 final Sort sort = (Sort)sortObjects.elementAt(level); 725 target[level] = il.append(NOP); 726 sort.translateSelect(sortRecord, extractMethod); 727 il.append(ARETURN); 728 } 729 730 // Compile def. target for switch statement if key has multiple levels 731 if (levels > 1) { 732 // Append the default target - it will _NEVER_ be reached 733 InstructionHandle defaultTarget = 734 il.append(new PUSH(cpg, EMPTYSTRING)); 735 il.insert(tblswitch,new TABLESWITCH(match, target, defaultTarget)); 736 il.append(ARETURN); 737 } 738 739 return extractMethod; 740 } 741 }