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