- /*
- * Copyright 2001-2004 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /*
- * $Id: Sort.java,v 1.23 2004/02/24 02:58:42 zongaro Exp $
- */
-
- package com.sun.org.apache.xalan.internal.xsltc.compiler;
-
- import java.text.Collator;
- import java.util.ArrayList;
- import java.util.NoSuchElementException;
- import java.util.StringTokenizer;
- import java.util.Vector;
-
- import com.sun.org.apache.bcel.internal.classfile.Field;
- import com.sun.org.apache.bcel.internal.classfile.Method;
- import com.sun.org.apache.bcel.internal.generic.ALOAD;
- import com.sun.org.apache.bcel.internal.generic.ANEWARRAY;
- import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
- import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
- import com.sun.org.apache.bcel.internal.generic.GETFIELD;
- import com.sun.org.apache.bcel.internal.generic.ICONST;
- import com.sun.org.apache.bcel.internal.generic.ILOAD;
- import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
- import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
- import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
- import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
- import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
- import com.sun.org.apache.bcel.internal.generic.InstructionList;
- import com.sun.org.apache.bcel.internal.generic.NEW;
- import com.sun.org.apache.bcel.internal.generic.NOP;
- import com.sun.org.apache.bcel.internal.generic.PUSH;
- import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
- import com.sun.org.apache.bcel.internal.generic.TABLESWITCH;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.CompareGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSortRecordFactGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSortRecordGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
- import com.sun.org.apache.xalan.internal.xsltc.dom.Axis;
-
-
- /**
- * @author Jacek Ambroziak
- * @author Santiago Pericas-Geertsen
- * @author Morten Jorgensen
- */
- final class Sort extends Instruction implements Closure {
-
- private Expression _select;
- private AttributeValue _order;
- private AttributeValue _caseOrder;
- private AttributeValue _dataType;
- private String _lang; // bug! see 26869
-
- private String _data = null;
-
-
- private String _className = null;
- private ArrayList _closureVars = null;
- private boolean _needsSortRecordFactory = false;
-
- // -- Begin Closure interface --------------------
-
- /**
- * Returns true if this closure is compiled in an inner class (i.e.
- * if this is a real closure).
- */
- public boolean inInnerClass() {
- return (_className != null);
- }
-
- /**
- * Returns a reference to its parent closure or null if outermost.
- */
- public Closure getParentClosure() {
- return null;
- }
-
- /**
- * Returns the name of the auxiliary class or null if this predicate
- * is compiled inside the Translet.
- */
- public String getInnerClassName() {
- return _className;
- }
-
- /**
- * Add new variable to the closure.
- */
- public void addVariable(VariableRefBase variableRef) {
- if (_closureVars == null) {
- _closureVars = new ArrayList();
- }
-
- // Only one reference per variable
- if (!_closureVars.contains(variableRef)) {
- _closureVars.add(variableRef);
- _needsSortRecordFactory = true;
- }
- }
-
- // -- End Closure interface ----------------------
-
- private void setInnerClassName(String className) {
- _className = className;
- }
-
- /**
- * Parse the attributes of the xsl:sort element
- */
- public void parseContents(Parser parser) {
-
- final SyntaxTreeNode parent = getParent();
- if (!(parent instanceof ApplyTemplates) &&
- !(parent instanceof ForEach)) {
- reportError(this, parser, ErrorMsg.STRAY_SORT_ERR, null);
- return;
- }
-
- // Parse the select expression (node string value if no expression)
- _select = parser.parseExpression(this, "select", "string(.)");
-
- // Get the sort order; default is 'ascending'
- String val = getAttribute("order");
- if (val.length() == 0) val = "ascending";
- _order = AttributeValue.create(this, val, parser);
-
- // Get the sort data type; default is text
- val = getAttribute("data-type");
- if (val.length() == 0) {
- try {
- final Type type = _select.typeCheck(parser.getSymbolTable());
- if (type instanceof IntType)
- val = "number";
- else
- val = "text";
- }
- catch (TypeCheckError e) {
- val = "text";
- }
- }
- _dataType = AttributeValue.create(this, val, parser);
-
- _lang = getAttribute("lang"); // bug! see 26869
- // val = getAttribute("lang");
- // _lang = AttributeValue.create(this, val, parser);
- // Get the case order; default is language dependant
- val = getAttribute("case-order");
- _caseOrder = AttributeValue.create(this, val, parser);
-
- }
-
- /**
- * Run type checks on the attributes; expression must return a string
- * which we will use as a sort key
- */
- public Type typeCheck(SymbolTable stable) throws TypeCheckError {
- final Type tselect = _select.typeCheck(stable);
-
- // If the sort data-type is not set we use the natural data-type
- // of the data we will sort
- if (!(tselect instanceof StringType)) {
- _select = new CastExpr(_select, Type.String);
- }
-
- _order.typeCheck(stable);
- _caseOrder.typeCheck(stable);
- _dataType.typeCheck(stable);
- return Type.Void;
- }
-
- /**
- * These two methods are needed in the static methods that compile the
- * overloaded NodeSortRecord.compareType() and NodeSortRecord.sortOrder()
- */
- public void translateSortType(ClassGenerator classGen,
- MethodGenerator methodGen) {
- _dataType.translate(classGen, methodGen);
- }
-
- public void translateSortOrder(ClassGenerator classGen,
- MethodGenerator methodGen) {
- _order.translate(classGen, methodGen);
- }
-
- public void translateCaseOrder(ClassGenerator classGen,
- MethodGenerator methodGen) {
- _caseOrder.translate(classGen, methodGen);
- }
-
- public void translateLang(ClassGenerator classGen,
- MethodGenerator methodGen) {
- final ConstantPoolGen cpg = classGen.getConstantPool();
- final InstructionList il = methodGen.getInstructionList();
- il.append(new PUSH(cpg, _lang)); // bug! see 26869
- }
-
- /**
- * This method compiles code for the select expression for this
- * xsl:sort element. The method is called from the static code-generating
- * methods in this class.
- */
- public void translateSelect(ClassGenerator classGen,
- MethodGenerator methodGen) {
- _select.translate(classGen,methodGen);
- }
-
- /**
- * This method should not produce any code
- */
- public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
- // empty
- }
-
- /**
- * Compiles code that instantiates a SortingIterator object.
- * This object's constructor needs referencdes to the current iterator
- * and a node sort record producing objects as its parameters.
- */
- public static void translateSortIterator(ClassGenerator classGen,
- MethodGenerator methodGen,
- Expression nodeSet,
- Vector sortObjects)
- {
- final ConstantPoolGen cpg = classGen.getConstantPool();
- final InstructionList il = methodGen.getInstructionList();
-
- // SortingIterator.SortingIterator(NodeIterator,NodeSortRecordFactory);
- final int init = cpg.addMethodref(SORT_ITERATOR, "<init>",
- "("
- + NODE_ITERATOR_SIG
- + NODE_SORT_FACTORY_SIG
- + ")V");
-
- il.append(new NEW(cpg.addClass(SORT_ITERATOR)));
- il.append(DUP);
-
- // Get the current node iterator
- if (nodeSet == null) { // apply-templates default
- final int children = cpg.addInterfaceMethodref(DOM_INTF,
- "getAxisIterator",
- "(I)"+
- NODE_ITERATOR_SIG);
- il.append(methodGen.loadDOM());
- il.append(new PUSH(cpg, Axis.CHILD));
- il.append(new INVOKEINTERFACE(children, 2));
- }
- else {
- nodeSet.translate(classGen, methodGen);
- }
-
- // Compile the code for the NodeSortRecord producing class and pass
- // that as the last argument to the SortingIterator constructor.
- compileSortRecordFactory(sortObjects, classGen, methodGen);
- il.append(new INVOKESPECIAL(init));
- }
-
-
- /**
- * Compiles code that instantiates a NodeSortRecordFactory object which
- * will produce NodeSortRecord objects of a specific type.
- */
- public static void compileSortRecordFactory(Vector sortObjects,
- ClassGenerator classGen, MethodGenerator methodGen)
- {
- String sortRecordClass =
- compileSortRecord(sortObjects, classGen, methodGen);
-
- boolean needsSortRecordFactory = false;
- final int nsorts = sortObjects.size();
- for (int i = 0; i < nsorts; i++) {
- final Sort sort = (Sort) sortObjects.elementAt(i);
- needsSortRecordFactory |= sort._needsSortRecordFactory;
- }
-
- String sortRecordFactoryClass = NODE_SORT_FACTORY;
- if (needsSortRecordFactory) {
- sortRecordFactoryClass =
- compileSortRecordFactory(sortObjects, classGen, methodGen,
- sortRecordClass);
- }
-
- final ConstantPoolGen cpg = classGen.getConstantPool();
- final InstructionList il = methodGen.getInstructionList();
-
- il.append(new NEW(cpg.addClass(sortRecordFactoryClass)));
- il.append(DUP);
- il.append(methodGen.loadDOM());
- il.append(new PUSH(cpg, sortRecordClass));
- il.append(classGen.loadTranslet());
-
- // Compile code that initializes the static _sortOrder
- il.append(new PUSH(cpg, nsorts));
- il.append(new ANEWARRAY(cpg.addClass(STRING)));
- for (int level = 0; level < nsorts; level++) {
- final Sort sort = (Sort)sortObjects.elementAt(level);
- il.append(DUP);
- il.append(new PUSH(cpg, level));
- sort.translateSortOrder(classGen, methodGen);
- il.append(AASTORE);
- }
-
- il.append(new PUSH(cpg, nsorts));
- il.append(new ANEWARRAY(cpg.addClass(STRING)));
- for (int level = 0; level < nsorts; level++) {
- final Sort sort = (Sort)sortObjects.elementAt(level);
- il.append(DUP);
- il.append(new PUSH(cpg, level));
- sort.translateSortType(classGen, methodGen);
- il.append(AASTORE);
- }
-
- il.append(new PUSH(cpg, nsorts));
- il.append(new ANEWARRAY(cpg.addClass(STRING)));
- for (int level = 0; level < nsorts; level++) {
- final Sort sort = (Sort)sortObjects.elementAt(level);
- il.append(DUP);
- il.append(new PUSH(cpg, level));
- sort.translateLang(classGen, methodGen);
- il.append(AASTORE);
- }
-
- il.append(new PUSH(cpg, nsorts));
- il.append(new ANEWARRAY(cpg.addClass(STRING)));
- for (int level = 0; level < nsorts; level++) {
- final Sort sort = (Sort)sortObjects.elementAt(level);
- il.append(DUP);
- il.append(new PUSH(cpg, level));
- sort.translateCaseOrder(classGen, methodGen);
- il.append(AASTORE);
- }
-
- il.append(new INVOKESPECIAL(
- cpg.addMethodref(sortRecordFactoryClass, "<init>",
- "(" + DOM_INTF_SIG
- + STRING_SIG
- + TRANSLET_INTF_SIG
- + "[" + STRING_SIG
- + "[" + STRING_SIG
- + "[" + STRING_SIG
- + "[" + STRING_SIG + ")V")));
-
- // Initialize closure variables in sortRecordFactory
- final ArrayList dups = new ArrayList();
-
- for (int j = 0; j < nsorts; j++) {
- final Sort sort = (Sort) sortObjects.get(j);
- final int length = (sort._closureVars == null) ? 0 :
- sort._closureVars.size();
-
- for (int i = 0; i < length; i++) {
- VariableRefBase varRef = (VariableRefBase) sort._closureVars.get(i);
-
- // Discard duplicate variable references
- if (dups.contains(varRef)) continue;
-
- final VariableBase var = varRef.getVariable();
-
- // Store variable in new closure
- il.append(DUP);
- il.append(var.loadInstruction());
- il.append(new PUTFIELD(
- cpg.addFieldref(sortRecordFactoryClass, var.getEscapedName(),
- var.getType().toSignature())));
- dups.add(varRef);
- }
- }
- }
-
- public static String compileSortRecordFactory(Vector sortObjects,
- ClassGenerator classGen, MethodGenerator methodGen,
- String sortRecordClass)
- {
- final XSLTC xsltc = ((Sort)sortObjects.firstElement()).getXSLTC();
- final String className = xsltc.getHelperClassName();
-
- final NodeSortRecordFactGenerator sortRecordFactory =
- new NodeSortRecordFactGenerator(className,
- NODE_SORT_FACTORY,
- className + ".java",
- ACC_PUBLIC | ACC_SUPER | ACC_FINAL,
- new String[] {},
- classGen.getStylesheet());
-
- ConstantPoolGen cpg = sortRecordFactory.getConstantPool();
-
- // Add a new instance variable for each var in closure
- final int nsorts = sortObjects.size();
- final ArrayList dups = new ArrayList();
-
- for (int j = 0; j < nsorts; j++) {
- final Sort sort = (Sort) sortObjects.get(j);
- final int length = (sort._closureVars == null) ? 0 :
- sort._closureVars.size();
-
- for (int i = 0; i < length; i++) {
- final VariableRefBase varRef = (VariableRefBase) sort._closureVars.get(i);
-
- // Discard duplicate variable references
- if (dups.contains(varRef)) continue;
-
- final VariableBase var = varRef.getVariable();
- sortRecordFactory.addField(new Field(ACC_PUBLIC,
- cpg.addUtf8(var.getEscapedName()),
- cpg.addUtf8(var.getType().toSignature()),
- null, cpg.getConstantPool()));
- dups.add(varRef);
- }
- }
-
- // Define a constructor for this class
- final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
- new com.sun.org.apache.bcel.internal.generic.Type[7];
- argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
- argTypes[1] = Util.getJCRefType(STRING_SIG);
- argTypes[2] = Util.getJCRefType(TRANSLET_INTF_SIG);
- argTypes[3] = Util.getJCRefType("[" + STRING_SIG);
- argTypes[4] = Util.getJCRefType("[" + STRING_SIG);
- argTypes[5] = Util.getJCRefType("[" + STRING_SIG);
- argTypes[6] = Util.getJCRefType("[" + STRING_SIG);
-
- final String[] argNames = new String[7];
- argNames[0] = DOCUMENT_PNAME;
- argNames[1] = "className";
- argNames[2] = TRANSLET_PNAME;
- argNames[3] = "order";
- argNames[4] = "type";
- argNames[5] = "lang";
- argNames[6] = "case_order";
-
-
- InstructionList il = new InstructionList();
- final MethodGenerator constructor =
- new MethodGenerator(ACC_PUBLIC,
- com.sun.org.apache.bcel.internal.generic.Type.VOID,
- argTypes, argNames, "<init>",
- className, il, cpg);
-
- // Push all parameters onto the stack and called super.<init>()
- il.append(ALOAD_0);
- il.append(ALOAD_1);
- il.append(ALOAD_2);
- il.append(new ALOAD(3));
- il.append(new ALOAD(4));
- il.append(new ALOAD(5));
- il.append(new ALOAD(6));
- il.append(new ALOAD(7));
- il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_FACTORY,
- "<init>",
- "(" + DOM_INTF_SIG
- + STRING_SIG
- + TRANSLET_INTF_SIG
- + "[" + STRING_SIG
- + "[" + STRING_SIG
- + "[" + STRING_SIG
- + "[" + STRING_SIG + ")V")));
- il.append(RETURN);
-
- // Override the definition of makeNodeSortRecord()
- il = new InstructionList();
- final MethodGenerator makeNodeSortRecord =
- new MethodGenerator(ACC_PUBLIC,
- Util.getJCRefType(NODE_SORT_RECORD_SIG),
- new com.sun.org.apache.bcel.internal.generic.Type[] {
- com.sun.org.apache.bcel.internal.generic.Type.INT,
- com.sun.org.apache.bcel.internal.generic.Type.INT },
- new String[] { "node", "last" }, "makeNodeSortRecord",
- className, il, cpg);
-
- il.append(ALOAD_0);
- il.append(ILOAD_1);
- il.append(ILOAD_2);
- il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_FACTORY,
- "makeNodeSortRecord", "(II)" + NODE_SORT_RECORD_SIG)));
- il.append(DUP);
- il.append(new CHECKCAST(cpg.addClass(sortRecordClass)));
-
- // Initialize closure in record class
- final int ndups = dups.size();
- for (int i = 0; i < ndups; i++) {
- final VariableRefBase varRef = (VariableRefBase) dups.get(i);
- final VariableBase var = varRef.getVariable();
- final Type varType = var.getType();
-
- il.append(DUP);
-
- // Get field from factory class
- il.append(ALOAD_0);
- il.append(new GETFIELD(
- cpg.addFieldref(className,
- var.getEscapedName(), varType.toSignature())));
-
- // Put field in record class
- il.append(new PUTFIELD(
- cpg.addFieldref(sortRecordClass,
- var.getEscapedName(), varType.toSignature())));
- }
- il.append(POP);
- il.append(ARETURN);
-
- constructor.setMaxLocals();
- constructor.setMaxStack();
- sortRecordFactory.addMethod(constructor.getMethod());
- makeNodeSortRecord.setMaxLocals();
- makeNodeSortRecord.setMaxStack();
- sortRecordFactory.addMethod(makeNodeSortRecord.getMethod());
- xsltc.dumpClass(sortRecordFactory.getJavaClass());
-
- return className;
- }
-
- /**
- * Create a new auxillary class extending NodeSortRecord.
- */
- private static String compileSortRecord(Vector sortObjects,
- ClassGenerator classGen,
- MethodGenerator methodGen) {
- final XSLTC xsltc = ((Sort)sortObjects.firstElement()).getXSLTC();
- final String className = xsltc.getHelperClassName();
-
- // This generates a new class for handling this specific sort
- final NodeSortRecordGenerator sortRecord =
- new NodeSortRecordGenerator(className,
- NODE_SORT_RECORD,
- "sort$0.java",
- ACC_PUBLIC | ACC_SUPER | ACC_FINAL,
- new String[] {},
- classGen.getStylesheet());
-
- final ConstantPoolGen cpg = sortRecord.getConstantPool();
-
- // Add a new instance variable for each var in closure
- final int nsorts = sortObjects.size();
- final ArrayList dups = new ArrayList();
-
- for (int j = 0; j < nsorts; j++) {
- final Sort sort = (Sort) sortObjects.get(j);
-
- // Set the name of the inner class in this sort object
- sort.setInnerClassName(className);
-
- final int length = (sort._closureVars == null) ? 0 :
- sort._closureVars.size();
- for (int i = 0; i < length; i++) {
- final VariableRefBase varRef = (VariableRefBase) sort._closureVars.get(i);
-
- // Discard duplicate variable references
- if (dups.contains(varRef)) continue;
-
- final VariableBase var = varRef.getVariable();
- sortRecord.addField(new Field(ACC_PUBLIC,
- cpg.addUtf8(var.getEscapedName()),
- cpg.addUtf8(var.getType().toSignature()),
- null, cpg.getConstantPool()));
- dups.add(varRef);
- }
- }
-
- Method init = compileInit(sortObjects, sortRecord,
- cpg, className);
- Method extract = compileExtract(sortObjects, sortRecord,
- cpg, className);
- sortRecord.addMethod(init);
- sortRecord.addMethod(extract);
-
- xsltc.dumpClass(sortRecord.getJavaClass());
- return className;
- }
-
- /**
- * Create a constructor for the new class. Updates the reference to the
- * collator in the super calls only when the stylesheet specifies a new
- * language in xsl:sort.
- */
- private static Method compileInit(Vector sortObjects,
- NodeSortRecordGenerator sortRecord,
- ConstantPoolGen cpg,
- String className)
- {
- final InstructionList il = new InstructionList();
- final MethodGenerator init =
- new MethodGenerator(ACC_PUBLIC,
- com.sun.org.apache.bcel.internal.generic.Type.VOID,
- null, null, "<init>", className,
- il, cpg);
-
- // Call the constructor in the NodeSortRecord superclass
- il.append(ALOAD_0);
- il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_RECORD,
- "<init>", "()V")));
-
-
-
- il.append(RETURN);
-
- init.stripAttributes(true);
- init.setMaxLocals();
- init.setMaxStack();
-
- return init.getMethod();
- }
-
-
- /**
- * Compiles a method that overloads NodeSortRecord.extractValueFromDOM()
- */
- private static Method compileExtract(Vector sortObjects,
- NodeSortRecordGenerator sortRecord,
- ConstantPoolGen cpg,
- String className) {
- final InstructionList il = new InstructionList();
-
- // String NodeSortRecord.extractValueFromDOM(dom,node,level);
- final CompareGenerator extractMethod =
- new CompareGenerator(ACC_PUBLIC | ACC_FINAL,
- com.sun.org.apache.bcel.internal.generic.Type.STRING,
- new com.sun.org.apache.bcel.internal.generic.Type[] {
- Util.getJCRefType(DOM_INTF_SIG),
- com.sun.org.apache.bcel.internal.generic.Type.INT,
- com.sun.org.apache.bcel.internal.generic.Type.INT,
- Util.getJCRefType(TRANSLET_SIG),
- com.sun.org.apache.bcel.internal.generic.Type.INT
- },
- new String[] { "dom",
- "current",
- "level",
- "translet",
- "last"
- },
- "extractValueFromDOM", className, il, cpg);
-
- // Values needed for the switch statement
- final int levels = sortObjects.size();
- final int match[] = new int[levels];
- final InstructionHandle target[] = new InstructionHandle[levels];
- InstructionHandle tblswitch = null;
-
- // Compile switch statement only if the key has multiple levels
- if (levels > 1) {
- // Put the parameter to the swtich statement on the stack
- il.append(new ILOAD(extractMethod.getLocalIndex("level")));
- // Append the switch statement here later on
- tblswitch = il.append(new NOP());
- }
-
- // Append all the cases for the switch statment
- for (int level = 0; level < levels; level++) {
- match[level] = level;
- final Sort sort = (Sort)sortObjects.elementAt(level);
- target[level] = il.append(NOP);
- sort.translateSelect(sortRecord, extractMethod);
- il.append(ARETURN);
- }
-
- // Compile def. target for switch statement if key has multiple levels
- if (levels > 1) {
- // Append the default target - it will _NEVER_ be reached
- InstructionHandle defaultTarget =
- il.append(new PUSH(cpg, EMPTYSTRING));
- il.insert(tblswitch,new TABLESWITCH(match, target, defaultTarget));
- il.append(ARETURN);
- }
-
- extractMethod.stripAttributes(true);
- extractMethod.setMaxLocals();
- extractMethod.setMaxStack();
- extractMethod.removeNOPs();
-
- return extractMethod.getMethod();
- }
- }