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 }