| /* |
| * @(#)$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"))); |
| } |
| } |