blob: 67fc907be80b2ff67fc19eb7f05ad33092fbf055 [file] [log] [blame]
/*
* @(#)$Id$
*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 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 org.apache.xalan.xsltc.compiler.util.Type;
import org.apache.xalan.xsltc.compiler.util.ReferenceType;
import de.fub.bytecode.classfile.JavaClass;
import de.fub.bytecode.generic.*;
import de.fub.bytecode.classfile.Field;
import org.apache.xalan.xsltc.compiler.util.*;
final class Number extends Instruction {
private static final int LEVEL_SINGLE = 0;
private static final int LEVEL_MULTIPLE = 1;
private static final int LEVEL_ANY = 2;
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;
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
};
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);
_formatNeeded = true;
}
else if (name.equals("lang")) {
_lang = new AttributeValueTemplate(value, parser);
_formatNeeded = true;
}
else if (name.equals("letter-value")) {
_letterValue = new AttributeValueTemplate(value, parser);
_formatNeeded = true;
}
else if (name.equals("grouping-separator")) {
_groupingSeparator = new AttributeValueTemplate(value, parser);
_formatNeeded = true;
}
else if (name.equals("grouping-size")) {
_groupingSize = new AttributeValueTemplate(value, parser);
_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,
String className) {
MethodGenerator cons;
final InstructionList il = new InstructionList();
final ConstantPoolGen cpg = classGen.getConstantPool();
cons = new MethodGenerator(ACC_PUBLIC,
de.fub.bytecode.generic.Type.VOID,
new de.fub.bytecode.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) {
ConstantPoolGen cpg = nodeCounterGen.getConstantPool();
final String className = matchGen.getClassName();
LocalVariableGen local;
int field;
// 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("Lorg/apache/xalan/xsltc/Translet;"),
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 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) {
//!!! local variables?
int current;
int field;
LocalVariableGen local;
MatchGenerator matchGen;
NodeCounterGenerator nodeCounterGen;
final String 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 single constructor to the class
compileConstructor(nodeCounterGen, className);
/*
* Compile method matchesFrom()
*/
if (_from != null) {
il = new InstructionList();
matchGen =
new MatchGenerator(ACC_PUBLIC | ACC_FINAL,
de.fub.bytecode.generic.Type.BOOLEAN,
new de.fub.bytecode.generic.Type[] {
de.fub.bytecode.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,
de.fub.bytecode.generic.Type.BOOLEAN,
new de.fub.bytecode.generic.Type[] {
de.fub.bytecode.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));
}
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));
}
}