blob: f57b23c6ee54ae690a0d72ad3ae20b1026a964f0 [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 Morten Jorgensen
*
*/
package org.apache.xalan.xsltc.compiler;
import java.util.Vector;
import org.apache.bcel.classfile.JavaClass;
import org.apache.xalan.xsltc.compiler.util.Type;
import org.apache.xalan.xsltc.compiler.util.ReferenceType;
import org.apache.bcel.generic.*;
import org.apache.xalan.xsltc.compiler.util.*;
final class Predicate extends Expression {
private Expression _exp = null; // Expression to be compiled inside pred.
private String _className; // Name of filter to generate
private boolean _nthPositionFilter = false;
private boolean _nthDescendant = false;
private boolean _canOptimize = true;
private int _ptype = -1;
public Predicate(Expression exp) {
(_exp = exp).setParent(this);
}
public void setParser(Parser parser) {
super.setParser(parser);
_exp.setParser(parser);
}
public boolean isNthDescendant() {
return _nthDescendant;
}
public boolean isNthPositionFilter() {
return _nthPositionFilter;
}
public void dontOptimize() {
_canOptimize = false;
}
protected final boolean isClosureBoundary() {
return true;
}
public int getPosType() {
if (_ptype == -1) {
SyntaxTreeNode parent = getParent();
if (parent instanceof StepPattern) {
_ptype = ((StepPattern)parent).getNodeType();
}
else if (parent instanceof AbsoluteLocationPath) {
AbsoluteLocationPath path = (AbsoluteLocationPath)parent;
Expression exp = path.getPath();
if (exp instanceof Step) {
_ptype = ((Step)exp).getNodeType();
}
}
else if (parent instanceof VariableRefBase) {
final VariableRefBase ref = (VariableRefBase)parent;
final VariableBase var = ref.getVariable();
final Expression exp = var.getExpression();
if (exp instanceof Step) {
_ptype = ((Step)exp).getNodeType();
}
}
else if (parent instanceof Step) {
_ptype = ((Step)parent).getNodeType();
}
}
return _ptype;
}
public boolean parentIsPattern() {
return (getParent() instanceof Pattern);
}
public Expression getExpr() {
return _exp;
}
public String toString() {
if (isNthPositionFilter())
return "pred([" + _exp + "],"+getPosType()+")";
else
return "pred(" + _exp + ')';
}
/**
* Type check a predicate expression. If the type of the expression is
* number convert it to boolean by adding a comparison with position().
* Note that if the expression is a parameter, we cannot distinguish
* at compile time if its type is number or not. Hence, expressions of
* reference type are always converted to booleans.
*/
public Type typeCheck(SymbolTable stable) throws TypeCheckError {
Type texp = _exp.typeCheck(stable);
// We need explicit type information for reference types - no good!
if (texp instanceof ReferenceType) {
_exp = new CastExpr(_exp, texp = Type.Real);
}
// A result tree fragment should not be cast directly to a number type,
// but rather to a boolean value, and then to a numer (0 or 1).
// Ref. section 11.2 of the XSLT 1.0 spec
if (texp instanceof ResultTreeType) {
_exp = new CastExpr(_exp, Type.Boolean);
_exp = new CastExpr(_exp, Type.Real);
texp = _exp.typeCheck(stable);
}
// Numerical types will be converted to a position filter
if (texp instanceof NumberType) {
// Cast any numerical types to an integer
if (texp instanceof IntType == false) {
_exp = new CastExpr(_exp, Type.Int);
}
SyntaxTreeNode parent = getParent();
// Expand [last()] into [position() = last()]
if ((_exp instanceof LastCall) ||
(parent instanceof Pattern) ||
(parent instanceof FilterExpr)) {
final QName position = getParser().getQName("position");
final PositionCall positionCall = new PositionCall(position);
positionCall.setParser(getParser());
positionCall.setParent(this);
_exp = new EqualityExpr(EqualityExpr.EQ, positionCall, _exp);
if (_exp.typeCheck(stable) != Type.Boolean) {
_exp = new CastExpr(_exp, Type.Boolean);
}
if (parent instanceof Pattern) {
_nthPositionFilter = true;
}
else if (parent instanceof FilterExpr) {
FilterExpr filter = (FilterExpr)parent;
Expression fexp = filter.getExpr();
if (fexp instanceof KeyCall)
_canOptimize = false;
else if (fexp instanceof VariableRefBase)
_canOptimize = false;
else if (fexp instanceof ParentLocationPath)
_canOptimize = false;
else if (fexp instanceof UnionPathExpr)
_canOptimize = false;
else if (_exp.hasPositionCall() && _exp.hasLastCall())
_canOptimize = false;
else if (filter.getParent() instanceof FilterParentPath)
_canOptimize = false;
if (_canOptimize)
_nthPositionFilter = true;
}
return _type = Type.Boolean;
}
// Use NthPositionIterator to handle [position()] or [a]
else {
if ((parent != null) && (parent instanceof Step)) {
parent = parent.getParent();
if ((parent != null) &&
(parent instanceof AbsoluteLocationPath)) {
// TODO: Special case for "//*[n]" pattern....
_nthDescendant = true;
return _type = Type.NodeSet;
}
}
_nthPositionFilter = true;
return _type = Type.NodeSet;
}
}
else if (texp instanceof BooleanType) {
if (_exp.hasPositionCall())
_nthPositionFilter = true;
}
// All other types will be handled as boolean values
else {
_exp = new CastExpr(_exp, Type.Boolean);
}
_nthPositionFilter = false;
return _type = Type.Boolean;
}
/**
* Create a new "Filter" class implementing
* <code>CurrentNodeListFilter</code>. Allocate registers for local
* variables and local parameters passed in the closure to test().
* Notice that local variables need to be "unboxed".
*/
private void compileFilter(ClassGenerator classGen,
MethodGenerator methodGen) {
TestGenerator testGen;
LocalVariableGen local;
FilterGenerator filterGen;
_className = getXSLTC().getHelperClassName();
filterGen = new FilterGenerator(_className,
"java.lang.Object",
toString(),
ACC_PUBLIC | ACC_SUPER,
new String[] {
CURRENT_NODE_LIST_FILTER
},
classGen.getStylesheet());
final InstructionList il = new InstructionList();
final ConstantPoolGen cpg = filterGen.getConstantPool();
testGen = new TestGenerator(ACC_PUBLIC | ACC_FINAL,
org.apache.bcel.generic.Type.BOOLEAN,
new org.apache.bcel.generic.Type[] {
org.apache.bcel.generic.Type.INT,
org.apache.bcel.generic.Type.INT,
org.apache.bcel.generic.Type.INT,
org.apache.bcel.generic.Type.INT,
Util.getJCRefType(TRANSLET_SIG),
Util.getJCRefType(NODE_ITERATOR_SIG)
},
new String[] {
"node",
"position",
"last",
"current",
"translet",
"iterator"
},
"test", _className, il, cpg);
// Store the dom in a local variable
local = testGen.addLocalVariable("document",
Util.getJCRefType(DOM_INTF_SIG),
null, null);
final String className = classGen.getClassName();
il.append(filterGen.loadTranslet());
il.append(new CHECKCAST(cpg.addClass(className)));
il.append(new GETFIELD(cpg.addFieldref(className,
DOM_FIELD, DOM_INTF_SIG)));
il.append(new ASTORE(local.getIndex()));
// Store the dom index in the test generator
testGen.setDomIndex(local.getIndex());
_exp.translate(filterGen, testGen);
il.append(IRETURN);
testGen.stripAttributes(true);
testGen.setMaxLocals();
testGen.setMaxStack();
testGen.removeNOPs();
filterGen.addEmptyConstructor(ACC_PUBLIC);
filterGen.addMethod(testGen.getMethod());
getXSLTC().dumpClass(filterGen.getJavaClass());
}
/**
* Returns true if the predicate is a test for the existance of an
* element or attribute. All we have to do is to get the first node
* from the step, check if it is there, and then return true/false.
*/
public boolean isBooleanTest() {
return (_exp instanceof BooleanExpr);
}
/**
* Method to see if we can optimise the predicate by using a specialised
* iterator for expressions like '/foo/bar[@attr = $var]', which are
* very common in many stylesheets
*/
public boolean isNodeValueTest() {
if (!_canOptimize) return false;
if ((getStep() != null) && (getCompareValue() != null))
return true;
else
return false;
}
private Expression _value = null;
private Step _step = null;
/**
* Utility method for optimisation. See isNodeValueTest()
*/
public Expression getCompareValue() {
if (_value != null) return _value;
if (_exp == null) return null;
if (_exp instanceof EqualityExpr) {
EqualityExpr exp = (EqualityExpr)_exp;
Expression left = exp.getLeft();
Expression right = exp.getRight();
Type tleft = left.getType();
Type tright = right.getType();
if (left instanceof CastExpr) left = ((CastExpr)left).getExpr();
if (right instanceof CastExpr) right = ((CastExpr)right).getExpr();
try {
if ((tleft == Type.String) && (!(left instanceof Step)))
_value = exp.getLeft();
if (left instanceof VariableRefBase)
_value = new CastExpr(left, Type.String);
if (_value != null) return _value;
}
catch (TypeCheckError e) { }
try {
if ((tright == Type.String) && (!(right instanceof Step)))
_value = exp.getRight();
if (right instanceof VariableRefBase)
_value = new CastExpr(right, Type.String);
if (_value != null) return _value;
}
catch (TypeCheckError e) { }
}
return null;
}
/**
* Utility method for optimisation. See isNodeValueTest()
*/
public Step getStep() {
if (_step != null) return _step;
if (_exp == null) return null;
if (_exp instanceof EqualityExpr) {
EqualityExpr exp = (EqualityExpr)_exp;
Expression left = exp.getLeft();
Expression right = exp.getRight();
if (left instanceof CastExpr) left = ((CastExpr)left).getExpr();
if (left instanceof Step) _step = (Step)left;
if (right instanceof CastExpr) right = ((CastExpr)right).getExpr();
if (right instanceof Step) _step = (Step)right;
}
return _step;
}
/**
* Translate a predicate expression. This translation pushes
* two references on the stack: a reference to a newly created
* filter object and a reference to the predicate's closure.
*/
public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
if (_nthPositionFilter || _nthDescendant) {
_exp.translate(classGen, methodGen);
}
else if (isNodeValueTest() && (getParent() instanceof Step)) {
_value.translate(classGen, methodGen);
il.append(new CHECKCAST(cpg.addClass(STRING_CLASS)));
il.append(new PUSH(cpg, ((EqualityExpr)_exp).getOp()));
}
else {
translateFilter(classGen, methodGen);
}
}
/**
* Translate a predicate expression. This translation pushes
* two references on the stack: a reference to a newly created
* filter object and a reference to the predicate's closure.
*/
public void translateFilter(ClassGenerator classGen,
MethodGenerator methodGen) {
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
compileFilter(classGen, methodGen);
il.append(new NEW(cpg.addClass(_className)));
il.append(DUP);
il.append(new INVOKESPECIAL(cpg.addMethodref(_className,
"<init>", "()V")));
}
}