| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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$ |
| */ |
| |
| package org.apache.xalan.xsltc.compiler; |
| |
| import java.util.ArrayList; |
| |
| import org.apache.bcel.classfile.Field; |
| import org.apache.bcel.generic.ALOAD; |
| import org.apache.bcel.generic.ASTORE; |
| import org.apache.bcel.generic.BranchHandle; |
| import org.apache.bcel.generic.CHECKCAST; |
| import org.apache.bcel.generic.ConstantPoolGen; |
| import org.apache.bcel.generic.GETFIELD; |
| import org.apache.bcel.generic.GOTO; |
| import org.apache.bcel.generic.IFNONNULL; |
| import org.apache.bcel.generic.INVOKESPECIAL; |
| import org.apache.bcel.generic.INVOKESTATIC; |
| import org.apache.bcel.generic.INVOKEVIRTUAL; |
| import org.apache.bcel.generic.InstructionList; |
| import org.apache.bcel.generic.D2I; |
| import org.apache.bcel.generic.LocalVariableGen; |
| import org.apache.bcel.generic.NEW; |
| import org.apache.bcel.generic.PUSH; |
| import org.apache.bcel.generic.PUTFIELD; |
| import org.apache.xalan.xsltc.compiler.util.ClassGenerator; |
| import org.apache.xalan.xsltc.compiler.util.MatchGenerator; |
| import org.apache.xalan.xsltc.compiler.util.MethodGenerator; |
| import org.apache.xalan.xsltc.compiler.util.NodeCounterGenerator; |
| import org.apache.xalan.xsltc.compiler.util.RealType; |
| import org.apache.xalan.xsltc.compiler.util.Type; |
| import org.apache.xalan.xsltc.compiler.util.TypeCheckError; |
| import org.apache.xalan.xsltc.compiler.util.Util; |
| |
| /** |
| * @author Jacek Ambroziak |
| * @author Santiago Pericas-Geertsen |
| */ |
| final class Number extends Instruction implements Closure { |
| private static final int LEVEL_SINGLE = 0; |
| private static final int LEVEL_MULTIPLE = 1; |
| private static final int LEVEL_ANY = 2; |
| |
| static final private String[] ClassNames = { |
| "org.apache.xalan.xsltc.dom.SingleNodeCounter", // LEVEL_SINGLE |
| "org.apache.xalan.xsltc.dom.MultipleNodeCounter", // LEVEL_MULTIPLE |
| "org.apache.xalan.xsltc.dom.AnyNodeCounter" // LEVEL_ANY |
| }; |
| |
| static final private String[] FieldNames = { |
| "___single_node_counter", // LEVEL_SINGLE |
| "___multiple_node_counter", // LEVEL_MULTIPLE |
| "___any_node_counter" // LEVEL_ANY |
| }; |
| |
| private Pattern _from = null; |
| private Pattern _count = null; |
| private Expression _value = null; |
| |
| private AttributeValueTemplate _lang = null; |
| private AttributeValueTemplate _format = null; |
| private AttributeValueTemplate _letterValue = null; |
| private AttributeValueTemplate _groupingSeparator = null; |
| private AttributeValueTemplate _groupingSize = null; |
| |
| private int _level = LEVEL_SINGLE; |
| private boolean _formatNeeded = false; |
| |
| private String _className = null; |
| private ArrayList _closureVars = null; |
| |
| // -- 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); |
| } |
| } |
| |
| // -- End Closure interface ---------------------- |
| |
| public void parseContents(Parser parser) { |
| final int count = _attributes.getLength(); |
| |
| for (int i = 0; i < count; i++) { |
| final String name = _attributes.getQName(i); |
| final String value = _attributes.getValue(i); |
| |
| if (name.equals("value")) { |
| _value = parser.parseExpression(this, name, null); |
| } |
| else if (name.equals("count")) { |
| _count = parser.parsePattern(this, name, null); |
| } |
| else if (name.equals("from")) { |
| _from = parser.parsePattern(this, name, null); |
| } |
| else if (name.equals("level")) { |
| if (value.equals("single")) { |
| _level = LEVEL_SINGLE; |
| } |
| else if (value.equals("multiple")) { |
| _level = LEVEL_MULTIPLE; |
| } |
| else if (value.equals("any")) { |
| _level = LEVEL_ANY; |
| } |
| } |
| else if (name.equals("format")) { |
| _format = new AttributeValueTemplate(value, parser, this); |
| _formatNeeded = true; |
| } |
| else if (name.equals("lang")) { |
| _lang = new AttributeValueTemplate(value, parser, this); |
| _formatNeeded = true; |
| } |
| else if (name.equals("letter-value")) { |
| _letterValue = new AttributeValueTemplate(value, parser, this); |
| _formatNeeded = true; |
| } |
| else if (name.equals("grouping-separator")) { |
| _groupingSeparator = new AttributeValueTemplate(value, parser, this); |
| _formatNeeded = true; |
| } |
| else if (name.equals("grouping-size")) { |
| _groupingSize = new AttributeValueTemplate(value, parser, this); |
| _formatNeeded = true; |
| } |
| } |
| } |
| |
| public Type typeCheck(SymbolTable stable) throws TypeCheckError { |
| if (_value != null) { |
| Type tvalue = _value.typeCheck(stable); |
| if (tvalue instanceof RealType == false) { |
| _value = new CastExpr(_value, Type.Real); |
| } |
| } |
| if (_count != null) { |
| _count.typeCheck(stable); |
| } |
| if (_from != null) { |
| _from.typeCheck(stable); |
| } |
| if (_format != null) { |
| _format.typeCheck(stable); |
| } |
| if (_lang != null) { |
| _lang.typeCheck(stable); |
| } |
| if (_letterValue != null) { |
| _letterValue.typeCheck(stable); |
| } |
| if (_groupingSeparator != null) { |
| _groupingSeparator.typeCheck(stable); |
| } |
| if (_groupingSize != null) { |
| _groupingSize.typeCheck(stable); |
| } |
| return Type.Void; |
| } |
| |
| /** |
| * True if the has specified a value for this instance of number. |
| */ |
| public boolean hasValue() { |
| return _value != null; |
| } |
| |
| /** |
| * Returns <tt>true</tt> if this instance of number has neither |
| * a from nor a count pattern. |
| */ |
| public boolean isDefault() { |
| return _from == null && _count == null; |
| } |
| |
| private void compileDefault(ClassGenerator classGen, |
| MethodGenerator methodGen) { |
| int index; |
| ConstantPoolGen cpg = classGen.getConstantPool(); |
| InstructionList il = methodGen.getInstructionList(); |
| |
| int[] fieldIndexes = getXSLTC().getNumberFieldIndexes(); |
| |
| if (fieldIndexes[_level] == -1) { |
| Field defaultNode = new Field(ACC_PRIVATE, |
| cpg.addUtf8(FieldNames[_level]), |
| cpg.addUtf8(NODE_COUNTER_SIG), |
| null, |
| cpg.getConstantPool()); |
| |
| // Add a new private field to this class |
| classGen.addField(defaultNode); |
| |
| // Get a reference to the newly added field |
| fieldIndexes[_level] = cpg.addFieldref(classGen.getClassName(), |
| FieldNames[_level], |
| NODE_COUNTER_SIG); |
| } |
| |
| // Check if field is initialized (runtime) |
| il.append(classGen.loadTranslet()); |
| il.append(new GETFIELD(fieldIndexes[_level])); |
| final BranchHandle ifBlock1 = il.append(new IFNONNULL(null)); |
| |
| // Create an instance of DefaultNodeCounter |
| index = cpg.addMethodref(ClassNames[_level], |
| "getDefaultNodeCounter", |
| "(" + TRANSLET_INTF_SIG |
| + DOM_INTF_SIG |
| + NODE_ITERATOR_SIG |
| + ")" + NODE_COUNTER_SIG); |
| il.append(classGen.loadTranslet()); |
| il.append(methodGen.loadDOM()); |
| il.append(methodGen.loadIterator()); |
| il.append(new INVOKESTATIC(index)); |
| il.append(DUP); |
| |
| // Store the node counter in the field |
| il.append(classGen.loadTranslet()); |
| il.append(SWAP); |
| il.append(new PUTFIELD(fieldIndexes[_level])); |
| final BranchHandle ifBlock2 = il.append(new GOTO(null)); |
| |
| // Backpatch conditionals |
| ifBlock1.setTarget(il.append(classGen.loadTranslet())); |
| il.append(new GETFIELD(fieldIndexes[_level])); |
| |
| ifBlock2.setTarget(il.append(NOP)); |
| } |
| |
| /** |
| * Compiles a constructor for the class <tt>_className</tt> that |
| * inherits from {Any,Single,Multiple}NodeCounter. This constructor |
| * simply calls the same constructor in the super class. |
| */ |
| private void compileConstructor(ClassGenerator classGen) { |
| MethodGenerator cons; |
| final InstructionList il = new InstructionList(); |
| final ConstantPoolGen cpg = classGen.getConstantPool(); |
| |
| cons = new MethodGenerator(ACC_PUBLIC, |
| org.apache.bcel.generic.Type.VOID, |
| new org.apache.bcel.generic.Type[] { |
| Util.getJCRefType(TRANSLET_INTF_SIG), |
| Util.getJCRefType(DOM_INTF_SIG), |
| Util.getJCRefType(NODE_ITERATOR_SIG) |
| }, |
| new String[] { |
| "dom", |
| "translet", |
| "iterator" |
| }, |
| "<init>", _className, il, cpg); |
| |
| il.append(ALOAD_0); // this |
| il.append(ALOAD_1); // translet |
| il.append(ALOAD_2); // DOM |
| il.append(new ALOAD(3));// iterator |
| |
| int index = cpg.addMethodref(ClassNames[_level], |
| "<init>", |
| "(" + TRANSLET_INTF_SIG |
| + DOM_INTF_SIG |
| + NODE_ITERATOR_SIG |
| + ")V"); |
| il.append(new INVOKESPECIAL(index)); |
| il.append(RETURN); |
| |
| classGen.addMethod(cons); |
| } |
| |
| /** |
| * This method compiles code that is common to matchesFrom() and |
| * matchesCount() in the auxillary class. |
| */ |
| private void compileLocals(NodeCounterGenerator nodeCounterGen, |
| MatchGenerator matchGen, |
| InstructionList il) |
| { |
| int field; |
| LocalVariableGen local; |
| ConstantPoolGen cpg = nodeCounterGen.getConstantPool(); |
| |
| // Get NodeCounter._iterator and store locally |
| local = matchGen.addLocalVariable("iterator", |
| Util.getJCRefType(NODE_ITERATOR_SIG), |
| null, null); |
| field = cpg.addFieldref(NODE_COUNTER, "_iterator", |
| ITERATOR_FIELD_SIG); |
| il.append(ALOAD_0); // 'this' pointer on stack |
| il.append(new GETFIELD(field)); |
| local.setStart(il.append(new ASTORE(local.getIndex()))); |
| matchGen.setIteratorIndex(local.getIndex()); |
| |
| // Get NodeCounter._translet and store locally |
| local = matchGen.addLocalVariable("translet", |
| Util.getJCRefType(TRANSLET_SIG), |
| null, null); |
| field = cpg.addFieldref(NODE_COUNTER, "_translet", |
| "Lorg/apache/xalan/xsltc/Translet;"); |
| il.append(ALOAD_0); // 'this' pointer on stack |
| il.append(new GETFIELD(field)); |
| il.append(new CHECKCAST(cpg.addClass(TRANSLET_CLASS))); |
| local.setStart(il.append(new ASTORE(local.getIndex()))); |
| nodeCounterGen.setTransletIndex(local.getIndex()); |
| |
| // Get NodeCounter._document and store locally |
| local = matchGen.addLocalVariable("document", |
| Util.getJCRefType(DOM_INTF_SIG), |
| null, null); |
| field = cpg.addFieldref(_className, "_document", DOM_INTF_SIG); |
| il.append(ALOAD_0); // 'this' pointer on stack |
| il.append(new GETFIELD(field)); |
| // Make sure we have the correct DOM type on the stack!!! |
| local.setStart(il.append(new ASTORE(local.getIndex()))); |
| matchGen.setDomIndex(local.getIndex()); |
| } |
| |
| private void compilePatterns(ClassGenerator classGen, |
| MethodGenerator methodGen) |
| { |
| int current; |
| int field; |
| LocalVariableGen local; |
| MatchGenerator matchGen; |
| NodeCounterGenerator nodeCounterGen; |
| |
| _className = getXSLTC().getHelperClassName(); |
| nodeCounterGen = new NodeCounterGenerator(_className, |
| ClassNames[_level], |
| toString(), |
| ACC_PUBLIC | ACC_SUPER, |
| null, |
| classGen.getStylesheet()); |
| InstructionList il = null; |
| ConstantPoolGen cpg = nodeCounterGen.getConstantPool(); |
| |
| // Add a new instance variable for each var in closure |
| final int closureLen = (_closureVars == null) ? 0 : |
| _closureVars.size(); |
| |
| for (int i = 0; i < closureLen; i++) { |
| VariableBase var = |
| ((VariableRefBase) _closureVars.get(i)).getVariable(); |
| |
| nodeCounterGen.addField(new Field(ACC_PUBLIC, |
| cpg.addUtf8(var.getEscapedName()), |
| cpg.addUtf8(var.getType().toSignature()), |
| null, cpg.getConstantPool())); |
| } |
| |
| // Add a single constructor to the class |
| compileConstructor(nodeCounterGen); |
| |
| /* |
| * Compile method matchesFrom() |
| */ |
| if (_from != null) { |
| il = new InstructionList(); |
| matchGen = |
| new MatchGenerator(ACC_PUBLIC | ACC_FINAL, |
| org.apache.bcel.generic.Type.BOOLEAN, |
| new org.apache.bcel.generic.Type[] { |
| org.apache.bcel.generic.Type.INT, |
| }, |
| new String[] { |
| "node", |
| }, |
| "matchesFrom", _className, il, cpg); |
| |
| compileLocals(nodeCounterGen,matchGen,il); |
| |
| // Translate Pattern |
| il.append(matchGen.loadContextNode()); |
| _from.translate(nodeCounterGen, matchGen); |
| _from.synthesize(nodeCounterGen, matchGen); |
| il.append(IRETURN); |
| |
| nodeCounterGen.addMethod(matchGen); |
| } |
| |
| /* |
| * Compile method matchesCount() |
| */ |
| if (_count != null) { |
| il = new InstructionList(); |
| matchGen = new MatchGenerator(ACC_PUBLIC | ACC_FINAL, |
| org.apache.bcel.generic.Type.BOOLEAN, |
| new org.apache.bcel.generic.Type[] { |
| org.apache.bcel.generic.Type.INT, |
| }, |
| new String[] { |
| "node", |
| }, |
| "matchesCount", _className, il, cpg); |
| |
| compileLocals(nodeCounterGen,matchGen,il); |
| |
| // Translate Pattern |
| il.append(matchGen.loadContextNode()); |
| _count.translate(nodeCounterGen, matchGen); |
| _count.synthesize(nodeCounterGen, matchGen); |
| |
| il.append(IRETURN); |
| |
| nodeCounterGen.addMethod(matchGen); |
| } |
| |
| getXSLTC().dumpClass(nodeCounterGen.getJavaClass()); |
| |
| // Push an instance of the newly created class |
| cpg = classGen.getConstantPool(); |
| il = methodGen.getInstructionList(); |
| |
| final int index = cpg.addMethodref(_className, "<init>", |
| "(" + TRANSLET_INTF_SIG |
| + DOM_INTF_SIG |
| + NODE_ITERATOR_SIG |
| + ")V"); |
| il.append(new NEW(cpg.addClass(_className))); |
| il.append(DUP); |
| il.append(classGen.loadTranslet()); |
| il.append(methodGen.loadDOM()); |
| il.append(methodGen.loadIterator()); |
| il.append(new INVOKESPECIAL(index)); |
| |
| // Initialize closure variables |
| for (int i = 0; i < closureLen; i++) { |
| final VariableRefBase varRef = (VariableRefBase) _closureVars.get(i); |
| final VariableBase var = varRef.getVariable(); |
| final Type varType = var.getType(); |
| |
| // Store variable in new closure |
| il.append(DUP); |
| il.append(var.loadInstruction()); |
| il.append(new PUTFIELD( |
| cpg.addFieldref(_className, var.getEscapedName(), |
| varType.toSignature()))); |
| } |
| } |
| |
| public void translate(ClassGenerator classGen, MethodGenerator methodGen) { |
| int index; |
| final ConstantPoolGen cpg = classGen.getConstantPool(); |
| final InstructionList il = methodGen.getInstructionList(); |
| |
| // Push "this" for the call to characters() |
| il.append(classGen.loadTranslet()); |
| |
| if (hasValue()) { |
| compileDefault(classGen, methodGen); |
| _value.translate(classGen, methodGen); |
| |
| // Using java.lang.Math.floor(number + 0.5) to return a double value |
| il.append(new PUSH(cpg, 0.5)); |
| il.append(DADD); |
| index = cpg.addMethodref(MATH_CLASS, "floor", "(D)D"); |
| il.append(new INVOKESTATIC(index)); |
| |
| // Call setValue on the node counter |
| index = cpg.addMethodref(NODE_COUNTER, |
| "setValue", |
| "(D)" + NODE_COUNTER_SIG); |
| il.append(new INVOKEVIRTUAL(index)); |
| } |
| else if (isDefault()) { |
| compileDefault(classGen, methodGen); |
| } |
| else { |
| compilePatterns(classGen, methodGen); |
| } |
| |
| // Call setStartNode() |
| if (!hasValue()) { |
| il.append(methodGen.loadContextNode()); |
| index = cpg.addMethodref(NODE_COUNTER, |
| SET_START_NODE, |
| "(I)" + NODE_COUNTER_SIG); |
| il.append(new INVOKEVIRTUAL(index)); |
| } |
| |
| // Call getCounter() with or without args |
| if (_formatNeeded) { |
| if (_format != null) { |
| _format.translate(classGen, methodGen); |
| } |
| else { |
| il.append(new PUSH(cpg, "1")); |
| } |
| |
| if (_lang != null) { |
| _lang.translate(classGen, methodGen); |
| } |
| else { |
| il.append(new PUSH(cpg, "en")); // TODO ?? |
| } |
| |
| if (_letterValue != null) { |
| _letterValue.translate(classGen, methodGen); |
| } |
| else { |
| il.append(new PUSH(cpg, Constants.EMPTYSTRING)); |
| } |
| |
| if (_groupingSeparator != null) { |
| _groupingSeparator.translate(classGen, methodGen); |
| } |
| else { |
| il.append(new PUSH(cpg, Constants.EMPTYSTRING)); |
| } |
| |
| if (_groupingSize != null) { |
| _groupingSize.translate(classGen, methodGen); |
| } |
| else { |
| il.append(new PUSH(cpg, "0")); |
| } |
| |
| index = cpg.addMethodref(NODE_COUNTER, "getCounter", |
| "(" + STRING_SIG + STRING_SIG |
| + STRING_SIG + STRING_SIG |
| + STRING_SIG + ")" + STRING_SIG); |
| il.append(new INVOKEVIRTUAL(index)); |
| } |
| else { |
| index = cpg.addMethodref(NODE_COUNTER, "setDefaultFormatting", |
| "()" + NODE_COUNTER_SIG); |
| il.append(new INVOKEVIRTUAL(index)); |
| |
| index = cpg.addMethodref(NODE_COUNTER, "getCounter", |
| "()" + STRING_SIG); |
| il.append(new INVOKEVIRTUAL(index)); |
| } |
| |
| // Output the resulting string to the handler |
| il.append(methodGen.loadHandler()); |
| index = cpg.addMethodref(TRANSLET_CLASS, |
| CHARACTERSW, |
| CHARACTERSW_SIG); |
| il.append(new INVOKEVIRTUAL(index)); |
| } |
| } |