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.BranchInstruction;
25import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
26import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
27import com.sun.org.apache.bcel.internal.generic.InstructionList;
28import com.sun.org.apache.bcel.internal.generic.PUSH;
29import com.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType;
30import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
31import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
32import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
33import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
34import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
35import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType;
36import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeType;
37import com.sun.org.apache.xalan.internal.xsltc.compiler.util.RealType;
38import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
39import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
40import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
41import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
42import com.sun.org.apache.xalan.internal.xsltc.runtime.Operators;
43
44/**
45 * @author Jacek Ambroziak
46 * @author Santiago Pericas-Geertsen
47 */
48final class RelationalExpr extends Expression {
49
50    private int _op;
51    private Expression _left, _right;
52
53    public RelationalExpr(int op, Expression left, Expression right) {
54        _op = op;
55        (_left = left).setParent(this);
56        (_right = right).setParent(this);
57    }
58
59    public void setParser(Parser parser) {
60        super.setParser(parser);
61        _left.setParser(parser);
62        _right.setParser(parser);
63    }
64
65    /**
66     * Returns true if this expressions contains a call to position(). This is
67     * needed for context changes in node steps containing multiple predicates.
68     */
69    public boolean hasPositionCall() {
70        if (_left.hasPositionCall()) return true;
71        if (_right.hasPositionCall()) return true;
72        return false;
73    }
74
75    /**
76     * Returns true if this expressions contains a call to last()
77     */
78    public boolean hasLastCall() {
79            return (_left.hasLastCall() || _right.hasLastCall());
80    }
81
82    public boolean hasReferenceArgs() {
83        return _left.getType() instanceof ReferenceType ||
84            _right.getType() instanceof ReferenceType;
85    }
86
87    public boolean hasNodeArgs() {
88        return _left.getType() instanceof NodeType ||
89            _right.getType() instanceof NodeType;
90    }
91
92    public boolean hasNodeSetArgs() {
93        return _left.getType() instanceof NodeSetType ||
94            _right.getType() instanceof NodeSetType;
95    }
96
97    public Type typeCheck(SymbolTable stable) throws TypeCheckError {
98        Type tleft = _left.typeCheck(stable);
99        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}
282