- /*
- * 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: Predicate.java,v 1.34 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.ASTORE;
- 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.INVOKESPECIAL;
- import com.sun.org.apache.bcel.internal.generic.InstructionList;
- 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.BooleanType;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.FilterGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NumberType;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TestGenerator;
- 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
- * @author Morten Jorgensen
- */
- final class Predicate extends Expression implements Closure {
-
- /**
- * The predicate's expression.
- */
- private Expression _exp = null;
-
- /**
- * This flag indicates if optimizations are turned on. The
- * method <code>dontOptimize()</code> can be called to turn
- * optimizations off.
- */
- private boolean _canOptimize = true;
-
- /**
- * Flag indicatig if the nth position optimization is on. It
- * is set in <code>typeCheck()</code>.
- */
- private boolean _nthPositionFilter = false;
-
- /**
- * Flag indicatig if the nth position descendant is on. It
- * is set in <code>typeCheck()</code>.
- */
- private boolean _nthDescendant = false;
-
- /**
- * Cached node type of the expression that owns this predicate.
- */
- int _ptype = -1;
-
- /**
- * Name of the inner class.
- */
- private String _className = null;
-
- /**
- * List of variables in closure.
- */
- private ArrayList _closureVars = null;
-
- /**
- * Reference to parent closure.
- */
- private Closure _parentClosure = null;
-
- /**
- * Cached value of method <code>getCompareValue()</code>.
- */
- private Expression _value = null;
-
- /**
- * Cached value of method <code>getCompareValue()</code>.
- */
- private Step _step = null;
-
- /**
- * Initializes a predicate.
- */
- public Predicate(Expression exp) {
- _exp = exp;
- _exp.setParent(this);
-
- }
-
- /**
- * Set the parser for this expression.
- */
- public void setParser(Parser parser) {
- super.setParser(parser);
- _exp.setParser(parser);
- }
-
- /**
- * Returns a boolean value indicating if the nth position optimization
- * is on. Must be call after type checking!
- */
- public boolean isNthPositionFilter() {
- return _nthPositionFilter;
- }
-
- /**
- * Returns a boolean value indicating if the nth descendant optimization
- * is on. Must be call after type checking!
- */
- public boolean isNthDescendant() {
- return _nthDescendant;
- }
-
- /**
- * Turns off all optimizations for this predicate.
- */
- public void dontOptimize() {
- _canOptimize = false;
- }
-
- /**
- * Returns true if the expression in this predicate contains a call
- * to position().
- */
- public boolean hasPositionCall() {
- return _exp.hasPositionCall();
- }
-
- /**
- * Returns true if the expression in this predicate contains a call
- * to last().
- */
- public boolean hasLastCall() {
- return _exp.hasLastCall();
- }
-
- // -- 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() {
- if (_parentClosure == null) {
- SyntaxTreeNode node = getParent();
- do {
- if (node instanceof Closure) {
- _parentClosure = (Closure) node;
- break;
- }
- if (node instanceof TopLevelElement) {
- break; // way up in the tree
- }
- node = node.getParent();
- } while (node != null);
- }
- return _parentClosure;
- }
-
- /**
- * 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);
-
- // Add variable to parent closure as well
- Closure parentClosure = getParentClosure();
- if (parentClosure != null) {
- parentClosure.addVariable(variableRef);
- }
- }
- }
-
- // -- End Closure interface ----------------------
-
- /**
- * Returns the node type of the expression owning this predicate. The
- * return value is cached in <code>_ptype</code>.
- */
- 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() {
- 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.
- *
- * This method may be called twice, before and after calling
- * <code>dontOptimize()</code>. If so, the second time it should honor
- * the new value of <code>_canOptimize</code>.
- */
- 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);
- }
-
- if (_canOptimize) {
- // Nth position optimization. Expression must not depend on context
- _nthPositionFilter =
- !_exp.hasLastCall() && !_exp.hasPositionCall();
-
- // _nthDescendant optimization - only if _nthPositionFilter is on
- if (_nthPositionFilter) {
- SyntaxTreeNode parent = getParent();
- _nthDescendant = (parent instanceof Step) &&
- (parent.getParent() instanceof AbsoluteLocationPath);
- return _type = Type.NodeSet;
- }
- }
-
- // Reset optimization flags
- _nthPositionFilter = _nthDescendant = false;
-
- // Otherwise, expand [e] to [position() = e]
- final QName position =
- getParser().getQNameIgnoreDefaultNs("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);
- }
- return _type = Type.Boolean;
- }
- else {
- // All other types will be handled as boolean values
- if (texp instanceof BooleanType == false) {
- _exp = new CastExpr(_exp, Type.Boolean);
- }
- 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 ConstantPoolGen cpg = filterGen.getConstantPool();
- final int length = (_closureVars == null) ? 0 : _closureVars.size();
-
- // Add a new instance variable for each var in closure
- for (int i = 0; i < length; i++) {
- VariableBase var = ((VariableRefBase) _closureVars.get(i)).getVariable();
-
- filterGen.addField(new Field(ACC_PUBLIC,
- cpg.addUtf8(var.getEscapedName()),
- cpg.addUtf8(var.getType().toSignature()),
- null, cpg.getConstantPool()));
- }
-
- final InstructionList il = new InstructionList();
- testGen = new TestGenerator(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,
- com.sun.org.apache.bcel.internal.generic.Type.INT,
- com.sun.org.apache.bcel.internal.generic.Type.INT,
- com.sun.org.apache.bcel.internal.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;
- return (getStep() != null && getCompareValue() != null);
- }
-
- /**
- * Returns the step in an expression of the form 'step = value'.
- * Null is returned if the expression is not of the right form.
- * Optimization if off if null is returned.
- */
- public Step getStep() {
- // Returned cached value if called more than once
- if (_step != null) {
- return _step;
- }
-
- // Nothing to do if _exp is null
- if (_exp == null) {
- return null;
- }
-
- // Ignore if not equality expression
- if (_exp instanceof EqualityExpr) {
- EqualityExpr exp = (EqualityExpr)_exp;
- Expression left = exp.getLeft();
- Expression right = exp.getRight();
-
- // Unwrap and set _step if appropriate
- if (left instanceof CastExpr) {
- left = ((CastExpr) left).getExpr();
- }
- if (left instanceof Step) {
- _step = (Step) left;
- }
-
- // Unwrap and set _step if appropriate
- if (right instanceof CastExpr) {
- right = ((CastExpr)right).getExpr();
- }
- if (right instanceof Step) {
- _step = (Step)right;
- }
- }
- return _step;
- }
-
- /**
- * Returns the value in an expression of the form 'step = value'.
- * A value may be either a literal string or a variable whose
- * type is string. Optimization if off if null is returned.
- */
- public Expression getCompareValue() {
- // Returned cached value if called more than once
- if (_value != null) {
- return _value;
- }
-
- // Nothing to to do if _exp is null
- if (_exp == null) {
- return null;
- }
-
- // Ignore if not an equality expression
- if (_exp instanceof EqualityExpr) {
- EqualityExpr exp = (EqualityExpr) _exp;
- Expression left = exp.getLeft();
- Expression right = exp.getRight();
-
- // Return if left is literal string
- if (left instanceof LiteralExpr) {
- _value = left;
- return _value;
- }
- // Return if left is a variable reference of type string
- if (left instanceof VariableRefBase &&
- left.getType() == Type.String)
- {
- _value = left;
- return _value;
- }
-
- // Return if right is literal string
- if (right instanceof LiteralExpr) {
- _value = right;
- return _value;
- }
- // Return if left is a variable reference whose type is string
- if (right instanceof VariableRefBase &&
- right.getType() == Type.String)
- {
- _value = right;
- return _value;
- }
- }
- return null;
- }
-
- /**
- * 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();
-
- // Compile auxiliary class for filter
- compileFilter(classGen, methodGen);
-
- // Create new instance of filter
- il.append(new NEW(cpg.addClass(_className)));
- il.append(DUP);
- il.append(new INVOKESPECIAL(cpg.addMethodref(_className,
- "<init>", "()V")));
-
- // Initialize closure variables
- final int length = (_closureVars == null) ? 0 : _closureVars.size();
-
- for (int i = 0; i < length; i++) {
- VariableRefBase varRef = (VariableRefBase) _closureVars.get(i);
- VariableBase var = varRef.getVariable();
- Type varType = var.getType();
-
- il.append(DUP);
-
- // Find nearest closure implemented as an inner class
- Closure variableClosure = _parentClosure;
- while (variableClosure != null) {
- if (variableClosure.inInnerClass()) break;
- variableClosure = variableClosure.getParentClosure();
- }
-
- // Use getfield if in an inner class
- if (variableClosure != null) {
- il.append(ALOAD_0);
- il.append(new GETFIELD(
- cpg.addFieldref(variableClosure.getInnerClassName(),
- var.getEscapedName(), varType.toSignature())));
- }
- else {
- // Use a load of instruction if in translet class
- il.append(var.loadInstruction());
- }
-
- // Store variable in new closure
- il.append(new PUTFIELD(
- cpg.addFieldref(_className, var.getEscapedName(),
- varType.toSignature())));
- }
- }
-
- /**
- * Translate a predicate expression. If non of the optimizations apply
- * then this translation pushes two references on the stack: a reference
- * to a newly created filter object and a reference to the predicate's
- * closure. See class <code>Step</code> for further details.
- */
- 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);
- }
- }
- }