1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22package com.sun.org.apache.xalan.internal.xsltc.compiler;
23
24import com.sun.org.apache.bcel.internal.generic.GOTO;
25import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
26import com.sun.org.apache.bcel.internal.generic.InstructionList;
27import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
28import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
29import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
30import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
31import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
32
33/**
34 * @author Jacek Ambroziak
35 * @author Santiago Pericas-Geertsen
36 * @author Morten Jorgensen
37 */
38final class LogicalExpr extends Expression {
39
40    public static final int OR  = 0;
41    public static final int AND = 1;
42
43    private final int  _op;     // operator
44    private Expression _left;   // first operand
45    private Expression _right;  // second operand
46
47    private static final String[] Ops = { "or", "and" };
48
49    /**
50     * Creates a new logical expression - either OR or AND. Note that the
51     * left- and right-hand side expressions can also be logical expressions,
52     * thus creating logical trees representing structures such as
53     * (a and (b or c) and d), etc...
54     */
55    public LogicalExpr(int op, Expression left, Expression right) {
56        _op = op;
57        (_left = left).setParent(this);
58        (_right = right).setParent(this);
59    }
60
61    /**
62     * Returns true if this expressions contains a call to position(). This is
63     * needed for context changes in node steps containing multiple predicates.
64     */
65    public boolean hasPositionCall() {
66        return (_left.hasPositionCall() || _right.hasPositionCall());
67    }
68
69    /**
70     * Returns true if this expressions contains a call to last()
71     */
72    public boolean hasLastCall() {
73            return (_left.hasLastCall() || _right.hasLastCall());
74    }
75
76    /**
77     * Returns an object representing the compile-time evaluation
78     * of an expression. We are only using this for function-available
79     * and element-available at this time.
80     */
81    public Object evaluateAtCompileTime() {
82        final Object leftb = _left.evaluateAtCompileTime();
83        final Object rightb = _right.evaluateAtCompileTime();
84
85        // Return null if we can't evaluate at compile time
86        if (leftb == null || rightb == null) {
87            return null;
88        }
89
90        if (_op == AND) {
91            return (leftb == Boolean.TRUE && rightb == Boolean.TRUE) ?
92                Boolean.TRUE : Boolean.FALSE;
93        }
94        else {
95            return (leftb == Boolean.TRUE || rightb == Boolean.TRUE) ?
96                Boolean.TRUE : Boolean.FALSE;
97        }
98    }
99
100    /**
101     * Returns this logical expression's operator - OR or AND represented
102     * by 0 and 1 respectively.
103     */
104    public int getOp() {
105        return(_op);
106    }
107
108    /**
109     * Override the SyntaxTreeNode.setParser() method to make sure that the
110     * parser is set for sub-expressions
111     */
112    public void setParser(Parser parser) {
113        super.setParser(parser);
114        _left.setParser(parser);
115        _right.setParser(parser);
116    }
117
118    /**
119     * Returns a string describing this expression
120     */
121    public String toString() {
122        return Ops[_op] + '(' + _left + ", " + _right + ')';
123    }
124
125    /**
126     * Type-check this expression, and possibly child expressions.
127     */
128    public Type typeCheck(SymbolTable stable) throws TypeCheckError {
129        // Get the left and right operand types
130        Type tleft = _left.typeCheck(stable);
131        Type tright = _right.typeCheck(stable);
132
133        // Check if the operator supports the two operand types
134        MethodType wantType = new MethodType(Type.Void, tleft, tright);
135        MethodType haveType = lookupPrimop(stable, Ops[_op], wantType);
136
137        // Yes, the operation is supported
138        if (haveType != null) {
139            // Check if left-hand side operand must be type casted
140            Type arg1 = (Type)haveType.argsType().elementAt(0);
141            if (!arg1.identicalTo(tleft))
142                _left = new CastExpr(_left, arg1);
143            // Check if right-hand side operand must be type casted
144            Type arg2 = (Type) haveType.argsType().elementAt(1);
145            if (!arg2.identicalTo(tright))
146                _right = new CastExpr(_right, arg1);
147            // Return the result type for the operator we will use
148            return _type = haveType.resultType();
149        }
150        throw new TypeCheckError(this);
151    }
152
153    /**
154     * Compile the expression - leave boolean expression on stack
155     */
156    public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
157        translateDesynthesized(classGen, methodGen);
158        synthesize(classGen, methodGen);
159    }
160
161    /**
162     * Compile expression and update true/false-lists
163     */
164    public void translateDesynthesized(ClassGenerator classGen,
165                                       MethodGenerator methodGen) {
166
167        final InstructionList il = methodGen.getInstructionList();
168        final SyntaxTreeNode parent = getParent();
169
170        // Compile AND-expression
171        if (_op == AND) {
172
173            // Translate left hand side - must be true
174            _left.translateDesynthesized(classGen, methodGen);
175
176            // Need this for chaining any OR-expression children
177            InstructionHandle middle = il.append(NOP);
178
179            // Translate left right side - must be true
180            _right.translateDesynthesized(classGen, methodGen);
181
182            // Need this for chaining any OR-expression children
183            InstructionHandle after = il.append(NOP);
184
185            // Append child expression false-lists to our false-list
186            _falseList.append(_right._falseList.append(_left._falseList));
187
188            // Special case for OR-expression as a left child of AND.
189            // The true-list of OR must point to second clause of AND.
190            if ((_left instanceof LogicalExpr) &&
191                (((LogicalExpr)_left).getOp() == OR)) {
192                _left.backPatchTrueList(middle);
193            }
194            else if (_left instanceof NotCall) {
195                _left.backPatchTrueList(middle);
196            }
197            else {
198                _trueList.append(_left._trueList);
199            }
200
201            // Special case for OR-expression as a right child of AND
202            // The true-list of OR must point to true-list of AND.
203            if ((_right instanceof LogicalExpr) &&
204                (((LogicalExpr)_right).getOp() == OR)) {
205                _right.backPatchTrueList(after);
206            }
207            else if (_right instanceof NotCall) {
208                _right.backPatchTrueList(after);
209            }
210            else {
211                _trueList.append(_right._trueList);
212            }
213        }
214        // Compile OR-expression
215        else {
216            // Translate left-hand side expression and produce true/false list
217            _left.translateDesynthesized(classGen, methodGen);
218
219            // This GOTO is used to skip over the code for the last test
220            // in the case where the the first test succeeds
221            InstructionHandle ih = il.append(new GOTO(null));
222
223            // Translate right-hand side expression and produce true/false list
224            _right.translateDesynthesized(classGen, methodGen);
225
226            _left._trueList.backPatch(ih);
227            _left._falseList.backPatch(ih.getNext());
228
229            _falseList.append(_right._falseList);
230            _trueList.add(ih).append(_right._trueList);
231        }
232    }
233}
234