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 }