blob: 085f1a350dd23a59c956c7f751296d09167c65f0 [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
* @author Erwin Bolwidt <ejb@klomp.org>
*
*/
package org.apache.xalan.xsltc.compiler;
import java.util.Vector;
import org.apache.xalan.xsltc.dom.Axis;
import org.apache.xalan.xsltc.compiler.util.Type;
import de.fub.bytecode.generic.*;
import de.fub.bytecode.classfile.Field;
import org.apache.xalan.xsltc.compiler.util.*;
final class StepPattern extends RelativePathPattern {
private static final int NO_CONTEXT = 0;
private static final int SIMPLE_CONTEXT = 1;
private static final int GENERAL_CONTEXT = 2;
private final int _axis;
private final Vector _predicates;
private final int _nodeType;
private Step _step = null;
private boolean _isEpsilon = false;
private int _contextCase;
public StepPattern(int axis, int nodeType, Vector predicates) {
_axis = axis;
_nodeType = nodeType;
_predicates = predicates;
}
public void setParser(Parser parser) {
super.setParser(parser);
if (_predicates != null) {
final int n = _predicates.size();
for (int i = 0; i < n; i++) {
final Predicate exp = (Predicate)_predicates.elementAt(i);
exp.setParser(parser);
exp.setParent(this);
}
}
}
public int getNodeType() {
return _nodeType;
}
public StepPattern getKernelPattern() {
return this;
}
public boolean isWildcard() {
return _isEpsilon && hasPredicates() == false;
}
public boolean hasPredicates() {
return _predicates != null && _predicates.size() > 0;
}
public double getDefaultPriority() {
if (hasPredicates()) {
return 0.5;
}
else {
switch(_nodeType) {
case -1:
return(-0.25);
case 0:
return(0.0);
default:
if (_nodeType >= NodeTest.GTYPE)
return(0.0);
else
return(-0.5);
}
}
}
public void reduceKernelPattern() {
_isEpsilon = true;
}
public String toString() {
final StringBuffer buffer = new StringBuffer("stepPattern(\"");
buffer.append(Axis.names[_axis])
.append("\", ")
.append(_isEpsilon ? "epsilon" : Integer.toString(_nodeType));
if (_predicates != null)
buffer.append(", ").append(_predicates.toString());
return buffer.append(')').toString();
}
private int analyzeCases() {
boolean noContext = true;
final int n = _predicates.size();
for (int i = 0; i < n && noContext; i++) {
final Predicate exp = (Predicate)_predicates.elementAt(i);
if (exp.isNthPositionFilter())
noContext = false;
}
if (noContext) {
return NO_CONTEXT;
}
else if (n == 1) {
return SIMPLE_CONTEXT;
}
return GENERAL_CONTEXT;
}
private String getNextFieldName() {
return "__step_pattern_iter_" + getXSLTC().nextStepPatternSerial();
}
public Type typeCheck(SymbolTable stable) throws TypeCheckError {
if (hasPredicates()) {
// Type check all the predicates (e -> position() = e)
final int n = _predicates.size();
for (int i = 0; i < n; i++) {
final Predicate pred = (Predicate)_predicates.elementAt(i);
pred.typeCheck(stable);
}
// Analyze context cases
_contextCase = analyzeCases();
// Create an instance of Step to do the translation
if (_contextCase == SIMPLE_CONTEXT) {
_step = new Step(_axis, _nodeType, null);
_step.typeCheck(stable);
}
else if (_contextCase == GENERAL_CONTEXT) {
_step = new Step(_axis, _nodeType, _predicates);
_step.typeCheck(stable);
}
}
return _axis == Axis.CHILD ? Type.Element : Type.Attribute;
}
private void translateKernel(ClassGenerator classGen,
MethodGenerator methodGen) {
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
// context node is on the stack
final int getType = cpg.addInterfaceMethodref(DOM_INTF,
"getType", "(I)I");
il.append(methodGen.loadDOM());
il.append(SWAP);
il.append(new INVOKEINTERFACE(getType, 2));
il.append(new PUSH(cpg, _nodeType));
// Need to allow for long jumps here - don't know if 100% correct
//_falseList.add(il.append(new IF_ICMPNE(null)));
final BranchHandle icmp = il.append(new IF_ICMPEQ(null));
_falseList.add(il.append(new GOTO_W(null)));
icmp.setTarget(il.append(NOP));
}
private void translateNoContext(ClassGenerator classGen,
MethodGenerator methodGen) {
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
// Push current node on the stack
il.append(methodGen.loadCurrentNode());
il.append(SWAP);
// Overwrite current node with matching node
il.append(methodGen.storeCurrentNode());
// If pattern not reduced then check kernel
if (!_isEpsilon) {
il.append(methodGen.loadCurrentNode());
translateKernel(classGen, methodGen);
}
// Compile the expressions within the predicates
final int n = _predicates.size();
for (int i = 0; i < n; i++) {
final Predicate pred = (Predicate)_predicates.elementAt(i);
final Expression exp = pred.getExpr();
exp.translateDesynthesized(classGen, methodGen);
_trueList.append(exp._trueList);
_falseList.append(exp._falseList);
}
// Backpatch true list and restore current iterator/node
InstructionHandle restore;
restore = il.append(methodGen.storeCurrentNode());
backPatchTrueList(restore);
BranchHandle skipFalse = il.append(new GOTO(null));
// Backpatch false list and restore current iterator/node
restore = il.append(methodGen.storeCurrentNode());
backPatchFalseList(restore);
_falseList.add(il.append(new GOTO(null)));
// True list falls through
skipFalse.setTarget(il.append(NOP));
}
private void translateSimpleContext(ClassGenerator classGen,
MethodGenerator methodGen) {
int index;
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
// Store matching node into a local variable
LocalVariableGen match;
match = methodGen.addLocalVariable("step_pattern_tmp1",
Util.getJCRefType(NODE_SIG),
il.getEnd(), null);
il.append(new ISTORE(match.getIndex()));
// If pattern not reduced then check kernel
if (!_isEpsilon) {
il.append(new ILOAD(match.getIndex()));
translateKernel(classGen, methodGen);
}
// Push current iterator and current node on the stack
il.append(methodGen.loadCurrentNode());
il.append(methodGen.loadIterator());
// Create a new matching iterator using the matching node
index = cpg.addMethodref(MATCHING_ITERATOR, "<init>",
"(I" + NODE_ITERATOR_SIG + ")V");
il.append(new NEW(cpg.addClass(MATCHING_ITERATOR)));
il.append(DUP);
il.append(new ILOAD(match.getIndex()));
_step.translate(classGen, methodGen);
il.append(new INVOKESPECIAL(index));
// Get the parent of the matching node
il.append(methodGen.loadDOM());
il.append(new ILOAD(match.getIndex()));
index = cpg.addInterfaceMethodref(DOM_INTF, GET_PARENT, GET_PARENT_SIG);
il.append(new INVOKEINTERFACE(index, 2));
// Start the iterator with the parent
il.append(methodGen.setStartNode());
// Overwrite current iterator and current node
il.append(methodGen.storeIterator());
il.append(new ILOAD(match.getIndex()));
il.append(methodGen.storeCurrentNode());
// Translate the expression of the predicate
final Predicate pred = (Predicate) _predicates.elementAt(0);
final Expression exp = pred.getExpr();
exp.translateDesynthesized(classGen, methodGen);
// Backpatch true list and restore current iterator/node
InstructionHandle restore;
restore = il.append(methodGen.storeIterator());
il.append(methodGen.storeCurrentNode());
exp.backPatchTrueList(restore);
BranchHandle skipFalse = il.append(new GOTO(null));
// Backpatch false list and restore current iterator/node
restore = il.append(methodGen.storeIterator());
il.append(methodGen.storeCurrentNode());
exp.backPatchFalseList(restore);
_falseList.add(il.append(new GOTO(null)));
// True list falls through
skipFalse.setTarget(il.append(NOP));
}
private void translateGeneralContext(ClassGenerator classGen,
MethodGenerator methodGen) {
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
int iteratorIndex = 0;
BranchHandle ifBlock = null;
LocalVariableGen iter, node, node2;
final String iteratorName = getNextFieldName();
// Store node on the stack into a local variable
node = methodGen.addLocalVariable("step_pattern_tmp1",
Util.getJCRefType(NODE_SIG),
il.getEnd(), null);
il.append(new ISTORE(node.getIndex()));
// Create a new local to store the iterator
iter = methodGen.addLocalVariable("step_pattern_tmp2",
Util.getJCRefType(NODE_ITERATOR_SIG),
il.getEnd(), null);
// Add a new private field if this is the main class
if (!classGen.isExternal()) {
final Field iterator =
new Field(ACC_PRIVATE,
cpg.addUtf8(iteratorName),
cpg.addUtf8(NODE_ITERATOR_SIG),
null, cpg.getConstantPool());
classGen.addField(iterator);
iteratorIndex = cpg.addFieldref(classGen.getClassName(),
iteratorName,
NODE_ITERATOR_SIG);
il.append(classGen.loadTranslet());
il.append(new GETFIELD(iteratorIndex));
il.append(DUP);
il.append(new ASTORE(iter.getIndex()));
ifBlock = il.append(new IFNONNULL(null));
il.append(classGen.loadTranslet());
}
// Compile the step created at type checking time
_step.translate(classGen, methodGen);
il.append(new ASTORE(iter.getIndex()));
// If in the main class update the field too
if (!classGen.isExternal()) {
il.append(new ALOAD(iter.getIndex()));
il.append(new PUTFIELD(iteratorIndex));
ifBlock.setTarget(il.append(NOP));
}
// Get the parent of the node on the stack
il.append(methodGen.loadDOM());
il.append(new ILOAD(node.getIndex()));
int index = cpg.addInterfaceMethodref(DOM_INTF,
GET_PARENT, GET_PARENT_SIG);
il.append(new INVOKEINTERFACE(index, 2));
// Initialize the iterator with the parent
il.append(new ALOAD(iter.getIndex()));
il.append(SWAP);
il.append(methodGen.setStartNode());
/*
* Inline loop:
*
* int node2;
* while ((node2 = iter.next()) != NodeIterator.END
* && node2 < node);
* return node2 == node;
*/
BranchHandle skipNext;
InstructionHandle begin, next;
node2 = methodGen.addLocalVariable("step_pattern_tmp3",
Util.getJCRefType(NODE_SIG),
il.getEnd(), null);
skipNext = il.append(new GOTO(null));
next = il.append(new ALOAD(iter.getIndex()));
begin = il.append(methodGen.nextNode());
il.append(DUP);
il.append(new ISTORE(node2.getIndex()));
_falseList.add(il.append(new IFEQ(null))); // NodeIterator.END
il.append(new ILOAD(node2.getIndex()));
il.append(new ILOAD(node.getIndex()));
il.append(new IF_ICMPLT(next));
il.append(new ILOAD(node2.getIndex()));
il.append(new ILOAD(node.getIndex()));
_falseList.add(il.append(new IF_ICMPNE(null)));
skipNext.setTarget(begin);
}
public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
if (hasPredicates()) {
switch (_contextCase) {
case NO_CONTEXT:
translateNoContext(classGen, methodGen);
break;
case SIMPLE_CONTEXT:
translateSimpleContext(classGen, methodGen);
break;
default:
translateGeneralContext(classGen, methodGen);
break;
}
}
else if (isWildcard()) {
il.append(POP); // true list falls through
}
else {
translateKernel(classGen, methodGen);
}
}
}