001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: LogicalExpr.java 468650 2006-10-28 07:03:30Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import org.apache.bcel.generic.GOTO;
025    import org.apache.bcel.generic.InstructionHandle;
026    import org.apache.bcel.generic.InstructionList;
027    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
028    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
029    import org.apache.xalan.xsltc.compiler.util.MethodType;
030    import org.apache.xalan.xsltc.compiler.util.Type;
031    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
032    
033    /**
034     * @author Jacek Ambroziak
035     * @author Santiago Pericas-Geertsen
036     * @author Morten Jorgensen
037     */
038    final class LogicalExpr extends Expression {
039    
040        public static final int OR  = 0;
041        public static final int AND = 1;
042            
043        private final int  _op;     // operator
044        private Expression _left;   // first operand
045        private Expression _right;  // second operand
046    
047        private static final String[] Ops = { "or", "and" };
048    
049        /**
050         * Creates a new logical expression - either OR or AND. Note that the
051         * left- and right-hand side expressions can also be logical expressions,
052         * thus creating logical trees representing structures such as
053         * (a and (b or c) and d), etc...
054         */
055        public LogicalExpr(int op, Expression left, Expression right) {
056            _op = op;
057            (_left = left).setParent(this);
058            (_right = right).setParent(this);
059        }
060    
061        /**
062         * Returns true if this expressions contains a call to position(). This is
063         * needed for context changes in node steps containing multiple predicates.
064         */
065        public boolean hasPositionCall() {
066            return (_left.hasPositionCall() || _right.hasPositionCall());
067        }
068    
069        /**
070         * Returns true if this expressions contains a call to last()
071         */
072        public boolean hasLastCall() {
073                return (_left.hasLastCall() || _right.hasLastCall());
074        }
075        
076        /**
077         * Returns an object representing the compile-time evaluation 
078         * of an expression. We are only using this for function-available
079         * and element-available at this time.
080         */
081        public Object evaluateAtCompileTime() {
082            final Object leftb = _left.evaluateAtCompileTime();
083            final Object rightb = _right.evaluateAtCompileTime();
084    
085            // Return null if we can't evaluate at compile time
086            if (leftb == null || rightb == null) {
087                return null;
088            }
089    
090            if (_op == AND) {
091                return (leftb == Boolean.TRUE && rightb == Boolean.TRUE) ?
092                    Boolean.TRUE : Boolean.FALSE;
093            }
094            else {
095                return (leftb == Boolean.TRUE || rightb == Boolean.TRUE) ?
096                    Boolean.TRUE : Boolean.FALSE;
097            }
098        }
099    
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    }