/*
* 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: Number.java,v 1.14 2004/02/24 02:58:42 zongaro Exp $
*/
package com.sun.org.apache.xalan.internal.xsltc.compiler;
import java.util.ArrayList;
import com.sun.org.apache.bcel.internal.classfile.Field;
import com.sun.org.apache.bcel.internal.generic.ALOAD;
import com.sun.org.apache.bcel.internal.generic.ASTORE;
import com.sun.org.apache.bcel.internal.generic.BranchHandle;
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.GOTO;
import com.sun.org.apache.bcel.internal.generic.IFNONNULL;
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.InstructionList;
import com.sun.org.apache.bcel.internal.generic.L2I;
import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.bcel.internal.generic.PUSH;
import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MatchGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeCounterGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.RealType;
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;
/**
* @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 = {
"com.sun.org.apache.xalan.internal.xsltc.dom.SingleNodeCounter", // LEVEL_SINGLE
"com.sun.org.apache.xalan.internal.xsltc.dom.MultipleNodeCounter", // LEVEL_MULTIPLE
"com.sun.org.apache.xalan.internal.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 true 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 _className 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,
com.sun.org.apache.bcel.internal.generic.Type.VOID,
new com.sun.org.apache.bcel.internal.generic.Type[] {
Util.getJCRefType(TRANSLET_INTF_SIG),
Util.getJCRefType(DOM_INTF_SIG),
Util.getJCRefType(NODE_ITERATOR_SIG)
},
new String[] {
"dom",
"translet",
"iterator"
},
"", _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],
"",
"(" + TRANSLET_INTF_SIG
+ DOM_INTF_SIG
+ NODE_ITERATOR_SIG
+ ")V");
il.append(new INVOKESPECIAL(index));
il.append(RETURN);
cons.stripAttributes(true);
cons.setMaxLocals();
cons.setMaxStack();
classGen.addMethod(cons.getMethod());
}
/**
* 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));
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",
"Lcom/sun/org/apache/xalan/internal/xsltc/Translet;");
il.append(ALOAD_0); // 'this' pointer on stack
il.append(new GETFIELD(field));
il.append(new CHECKCAST(cpg.addClass(TRANSLET_CLASS)));
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!!!
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,
com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
new com.sun.org.apache.bcel.internal.generic.Type[] {
com.sun.org.apache.bcel.internal.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);
matchGen.stripAttributes(true);
matchGen.setMaxLocals();
matchGen.setMaxStack();
matchGen.removeNOPs();
nodeCounterGen.addMethod(matchGen.getMethod());
}
/*
* Compile method matchesCount()
*/
if (_count != null) {
il = new InstructionList();
matchGen = new MatchGenerator(ACC_PUBLIC | ACC_FINAL,
com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
new com.sun.org.apache.bcel.internal.generic.Type[] {
com.sun.org.apache.bcel.internal.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);
matchGen.stripAttributes(true);
matchGen.setMaxLocals();
matchGen.setMaxStack();
matchGen.removeNOPs();
nodeCounterGen.addMethod(matchGen.getMethod());
}
getXSLTC().dumpClass(nodeCounterGen.getJavaClass());
// Push an instance of the newly created class
cpg = classGen.getConstantPool();
il = methodGen.getInstructionList();
final int index = cpg.addMethodref(_className, "",
"(" + 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);
// Round the number to the nearest integer
index = cpg.addMethodref(MATH_CLASS, "round", "(D)J");
il.append(new INVOKESTATIC(index));
il.append(new L2I());
// Call setValue on the node counter
index = cpg.addMethodref(NODE_COUNTER,
"setValue",
"(I)" + 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));
}
}