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