blob: 601e1560affe79fc9e8fb134f5a4571dfe16fa0a [file] [log] [blame]
/*
* @(#)$Id$
*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2001, Sun
* Microsystems., http://www.sun.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* @author Jacek Ambroziak
* @author Santiago Pericas-Geertsen
*
*/
package org.apache.xalan.xsltc.compiler;
import java.util.ArrayList;
import org.apache.xalan.xsltc.compiler.util.Type;
import org.apache.xalan.xsltc.compiler.util.ReferenceType;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.*;
import org.apache.bcel.classfile.Field;
import org.apache.xalan.xsltc.compiler.util.*;
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);
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",
"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)));
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.getVariable()),
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);
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,
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);
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, "<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.getVariable(),
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));
}
}