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