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: RelationalExpr.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.BranchInstruction;
025    import org.apache.bcel.generic.ConstantPoolGen;
026    import org.apache.bcel.generic.INVOKESTATIC;
027    import org.apache.bcel.generic.InstructionList;
028    import org.apache.bcel.generic.PUSH;
029    import org.apache.xalan.xsltc.compiler.util.BooleanType;
030    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
031    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
032    import org.apache.xalan.xsltc.compiler.util.IntType;
033    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
034    import org.apache.xalan.xsltc.compiler.util.MethodType;
035    import org.apache.xalan.xsltc.compiler.util.NodeSetType;
036    import org.apache.xalan.xsltc.compiler.util.NodeType;
037    import org.apache.xalan.xsltc.compiler.util.RealType;
038    import org.apache.xalan.xsltc.compiler.util.ReferenceType;
039    import org.apache.xalan.xsltc.compiler.util.ResultTreeType;
040    import org.apache.xalan.xsltc.compiler.util.Type;
041    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
042    import org.apache.xalan.xsltc.runtime.Operators;
043    
044    /**
045     * @author Jacek Ambroziak
046     * @author Santiago Pericas-Geertsen
047     */
048    final class RelationalExpr extends Expression {
049    
050        private int _op;
051        private Expression _left, _right;
052    
053        public RelationalExpr(int op, Expression left, Expression right) {
054            _op = op;
055            (_left = left).setParent(this);
056            (_right = right).setParent(this);
057        }
058    
059        public void setParser(Parser parser) {
060            super.setParser(parser);
061            _left.setParser(parser);
062            _right.setParser(parser);
063        }
064    
065        /**
066         * Returns true if this expressions contains a call to position(). This is
067         * needed for context changes in node steps containing multiple predicates.
068         */
069        public boolean hasPositionCall() {
070            if (_left.hasPositionCall()) return true;
071            if (_right.hasPositionCall()) return true;
072            return false;
073        }
074    
075        /**
076         * Returns true if this expressions contains a call to last()
077         */
078        public boolean hasLastCall() {
079                return (_left.hasLastCall() || _right.hasLastCall());
080        }
081        
082        public boolean hasReferenceArgs() {
083            return _left.getType() instanceof ReferenceType ||
084                _right.getType() instanceof ReferenceType;
085        }
086    
087        public boolean hasNodeArgs() {
088            return _left.getType() instanceof NodeType ||
089                _right.getType() instanceof NodeType;
090        }
091    
092        public boolean hasNodeSetArgs() {
093            return _left.getType() instanceof NodeSetType ||
094                _right.getType() instanceof NodeSetType;
095        }
096    
097        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
098            Type tleft = _left.typeCheck(stable); 
099            Type tright = _right.typeCheck(stable);
100    
101            //bug fix # 2838, cast to reals if both are result tree fragments
102            if (tleft instanceof ResultTreeType &&
103                tright instanceof ResultTreeType ) 
104            {
105                _right = new CastExpr(_right, Type.Real);
106                _left = new CastExpr(_left, Type.Real);
107                return _type = Type.Boolean; 
108            }
109    
110            // If one is of reference type, then convert the other too
111            if (hasReferenceArgs()) {
112                Type type = null;
113                Type typeL = null;
114                Type typeR = null;
115                if (tleft instanceof ReferenceType) {
116                    if (_left instanceof VariableRefBase) {
117                        VariableRefBase ref = (VariableRefBase)_left;
118                        VariableBase var = ref.getVariable();
119                        typeL = var.getType();
120                    }
121                }
122                if (tright instanceof ReferenceType) {
123                    if (_right instanceof VariableRefBase) {
124                        VariableRefBase ref = (VariableRefBase)_right;
125                        VariableBase var = ref.getVariable();
126                        typeR = var.getType();
127                    }
128                }
129                // bug fix # 2838 
130                if (typeL == null)
131                    type = typeR;
132                else if (typeR == null)
133                    type = typeL;
134                else {
135                    type = Type.Real;
136                }
137                if (type == null) type = Type.Real;
138    
139                _right = new CastExpr(_right, type);
140                _left = new CastExpr(_left, type);
141                return _type = Type.Boolean;
142            }
143    
144            if (hasNodeSetArgs()) {
145                // Ensure that the node-set is the left argument
146                if (tright instanceof NodeSetType) {
147                    final Expression temp = _right; _right = _left; _left = temp;
148            _op = (_op == Operators.GT) ? Operators.LT :
149                (_op == Operators.LT) ? Operators.GT :
150                (_op == Operators.GE) ? Operators.LE : Operators.GE;
151                    tright = _right.getType();
152                }
153    
154                // Promote nodes to node sets
155                if (tright instanceof NodeType) {
156                    _right = new CastExpr(_right, Type.NodeSet);
157                }
158                // Promote integer to doubles to have fewer compares
159                if (tright instanceof IntType) {
160                    _right = new CastExpr(_right, Type.Real);
161                }
162                // Promote result-trees to strings
163                if (tright instanceof ResultTreeType) {
164                    _right = new CastExpr(_right, Type.String);
165                }
166                return _type = Type.Boolean;
167            }
168    
169            // In the node-boolean case, convert node to boolean first
170            if (hasNodeArgs()) {
171                if (tleft instanceof BooleanType) {
172                    _right = new CastExpr(_right, Type.Boolean);
173                    tright = Type.Boolean;
174                }
175                if (tright instanceof BooleanType) {
176                    _left = new CastExpr(_left, Type.Boolean);
177                    tleft = Type.Boolean;
178                }
179            }
180    
181            // Lookup the table of primops to find the best match
182        MethodType ptype = lookupPrimop(stable, Operators.getOpNames(_op),
183                    new MethodType(Type.Void, tleft, tright)); 
184    
185            if (ptype != null) {
186                Type arg1 = (Type) ptype.argsType().elementAt(0);
187                if (!arg1.identicalTo(tleft)) {
188                    _left = new CastExpr(_left, arg1);
189                }
190                Type arg2 = (Type) ptype.argsType().elementAt(1);
191                if (!arg2.identicalTo(tright)) {
192                    _right = new CastExpr(_right, arg1);                            
193                }
194                return _type = ptype.resultType();
195            }
196            throw new TypeCheckError(this);
197        }
198    
199        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
200            if (hasNodeSetArgs() || hasReferenceArgs()) {
201                final ConstantPoolGen cpg = classGen.getConstantPool();
202                final InstructionList il = methodGen.getInstructionList();
203    
204                // Call compare() from the BasisLibrary
205                _left.translate(classGen, methodGen);
206                _left.startIterator(classGen, methodGen);
207                _right.translate(classGen, methodGen);
208                _right.startIterator(classGen, methodGen);
209    
210                il.append(new PUSH(cpg, _op));
211                il.append(methodGen.loadDOM());
212    
213                int index = cpg.addMethodref(BASIS_LIBRARY_CLASS, "compare",
214                                             "("
215                                             + _left.getType().toSignature() 
216                                             + _right.getType().toSignature()
217                                             + "I"
218                                             + DOM_INTF_SIG
219                                             + ")Z");
220                il.append(new INVOKESTATIC(index));
221            }
222            else {
223                translateDesynthesized(classGen, methodGen);
224                synthesize(classGen, methodGen);
225            }
226        }
227    
228        public void translateDesynthesized(ClassGenerator classGen,
229                                           MethodGenerator methodGen) {
230            if (hasNodeSetArgs() || hasReferenceArgs()) {
231                translate(classGen, methodGen);
232                desynthesize(classGen, methodGen);
233            }
234            else {
235                BranchInstruction bi = null;
236                final InstructionList il = methodGen.getInstructionList();
237    
238                _left.translate(classGen, methodGen);
239                _right.translate(classGen, methodGen);
240    
241                // TODO: optimize if one of the args is 0
242    
243                boolean tozero = false;
244                Type tleft = _left.getType(); 
245    
246                if (tleft instanceof RealType) {
247            il.append(tleft.CMP(_op == Operators.LT || _op == Operators.LE));
248                    tleft = Type.Int;
249                    tozero = true;
250                }
251    
252                switch (_op) {
253            case Operators.LT:
254                    bi = tleft.GE(tozero);  
255                    break;
256                    
257            case Operators.GT:
258                    bi = tleft.LE(tozero);
259                    break;
260                    
261            case Operators.LE:
262                    bi = tleft.GT(tozero);
263                    break;
264                    
265            case Operators.GE:
266                    bi = tleft.LT(tozero);
267                    break;
268                    
269                default:
270                    ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_RELAT_OP_ERR,this);
271                    getParser().reportError(Constants.FATAL, msg);
272                }
273    
274                _falseList.add(il.append(bi));              // must be backpatched
275            }
276        }
277    
278        public String toString() {
279            return Operators.getOpNames(_op) + '(' + _left + ", " + _right + ')';
280        }
281    }