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