001 /*
002 $Id: BinaryExpression.java,v 1.8 2005/02/23 17:06:37 phk Exp $
003
004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005
006 Redistribution and use of this software and associated documentation
007 ("Software"), with or without modification, are permitted provided
008 that the following conditions are met:
009
010 1. Redistributions of source code must retain copyright
011 statements and notices. Redistributions must also contain a
012 copy of this document.
013
014 2. Redistributions in binary form must reproduce the
015 above copyright notice, this list of conditions and the
016 following disclaimer in the documentation and/or other
017 materials provided with the distribution.
018
019 3. The name "groovy" must not be used to endorse or promote
020 products derived from this Software without prior written
021 permission of The Codehaus. For written permission,
022 please contact info@codehaus.org.
023
024 4. Products derived from this Software may not be called "groovy"
025 nor may "groovy" appear in their names without prior written
026 permission of The Codehaus. "groovy" is a registered
027 trademark of The Codehaus.
028
029 5. Due credit should be given to The Codehaus -
030 http://groovy.codehaus.org/
031
032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043 OF THE POSSIBILITY OF SUCH DAMAGE.
044
045 */
046 package org.codehaus.groovy.ast.expr;
047
048 import java.io.OutputStream;
049 import java.io.Writer;
050 import java.math.BigDecimal;
051 import java.math.BigInteger;
052 import java.util.Collection;
053 import java.util.Date;
054 import java.util.List;
055 import java.util.Map;
056 import java.util.regex.Matcher;
057
058 import org.codehaus.groovy.ast.GroovyCodeVisitor;
059 import org.codehaus.groovy.ast.Type;
060 import org.codehaus.groovy.classgen.AsmClassGenerator;
061 import org.codehaus.groovy.syntax.Token;
062 import org.codehaus.groovy.syntax.Types;
063 import groovy.lang.GString;
064
065 /**
066 * Represents two expressions and an operation
067 *
068 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
069 * @version $Revision: 1.8 $
070 */
071 public class BinaryExpression extends Expression {
072
073 private Expression leftExpression;
074 private Expression rightExpression;
075 private Token operation;
076
077 public BinaryExpression(Expression leftExpression,
078 Token operation,
079 Expression rightExpression) {
080 this.leftExpression = leftExpression;
081 this.operation = operation;
082 this.rightExpression = rightExpression;
083
084 }
085
086 public Class getTypeClass() {
087 typeClass = resolveThisType(operation);
088 return typeClass;
089 }
090
091 public boolean isDynamic() {
092 return false; //To change body of implemented methods use File | Settings | File Templates.
093 }
094
095 private Class resolveThisType(Token operation) {
096 switch (operation.getType()) {
097 case Types.EQUAL : // = assignment
098 if (!leftExpression.isDynamic())
099 return leftExpression.getTypeClass();
100 else
101 return rightExpression.getTypeClass();
102 case Types.COMPARE_IDENTICAL :
103 case Types.COMPARE_EQUAL :
104 case Types.COMPARE_NOT_EQUAL :
105 case Types.COMPARE_GREATER_THAN :
106 case Types.COMPARE_GREATER_THAN_EQUAL :
107 case Types.COMPARE_LESS_THAN :
108 case Types.COMPARE_LESS_THAN_EQUAL :
109 case Types.KEYWORD_INSTANCEOF :
110 case Types.MATCH_REGEX :
111 return boolean.class;
112 case Types.LOGICAL_AND :
113 case Types.LOGICAL_OR :
114 return Boolean.class;
115 case Types.COMPARE_TO :
116 return Integer.class;
117 case Types.PLUS :
118 case Types.PLUS_EQUAL :{
119 if (leftExpression.getTypeClass() == String.class && rightExpression.getTypeClass() == String.class) {
120 return String.class;
121 }
122 else if (leftExpression.getTypeClass() == GString.class &&
123 (rightExpression.getTypeClass() == GString.class || rightExpression.getTypeClass() == String.class)) {
124 return GString.class;
125 }
126 else if (isNumber(leftExpression.getType()) && isNumber(rightExpression.getType())) {
127 return chooseWiderNumberType(leftExpression.getType(), rightExpression.getType());
128 }
129 else if (leftExpression.getTypeClass() == Date.class && Number.class.isAssignableFrom(rightExpression.getTypeClass()) ) {
130 return Date.class;
131 }
132 else if (leftExpression.getTypeClass() != null && Collection.class.isAssignableFrom(leftExpression.getTypeClass() )) {
133 return List.class;
134 }
135 else {
136 return null;
137 }
138 }
139 case Types.MINUS :
140 case Types.MINUS_EQUAL :{
141 if (leftExpression.getTypeClass() == String.class) {
142 return String.class;
143 } else if (leftExpression instanceof GStringExpression && isNumber(rightExpression.getType())) {
144 return String.class;
145 } else if (isNumber(leftExpression.getType()) && isNumber(rightExpression.getType())) {
146 return chooseWiderNumberType(leftExpression.getType(), rightExpression.getType());
147 }
148 else if (leftExpression.getTypeClass() != null && List.class.isAssignableFrom(leftExpression.getTypeClass() )) {
149 return List.class;
150 }
151 else if (leftExpression.getTypeClass() == Date.class && Number.class.isAssignableFrom(rightExpression.getTypeClass()) ) {
152 return Date.class;
153 }
154 else {
155 return null;
156 }
157 }
158 case Types.MULTIPLY :
159 case Types.MULTIPLY_EQUAL : {
160 if (leftExpression.getTypeClass() == String.class && isNumber(rightExpression.getType())) {
161 return String.class;
162 } else if (leftExpression instanceof GStringExpression && isNumber(rightExpression.getType())) {
163 return String.class;
164 } else if (isNumber(leftExpression.getType()) && isNumber(rightExpression.getType())) {
165 return chooseWiderNumberType(leftExpression.getType(), rightExpression.getType());
166 }
167 else if (leftExpression.getTypeClass() != null && Collection.class.isAssignableFrom(leftExpression.getTypeClass() )) {
168 return List.class;
169 }
170 else {
171 return null;
172 }
173 }
174
175 case Types.DIVIDE :
176 case Types.DIVIDE_EQUAL :
177 case Types.MOD :
178 case Types.MOD_EQUAL :
179 if (isNumber(leftExpression.getType()) && isNumber(rightExpression.getType())) {
180 return chooseWiderNumberType(leftExpression.getType(), rightExpression.getType());
181 }
182 return null;
183
184 case Types.POWER :
185 case Types.POWER_EQUAL :
186 if (isNumber(leftExpression.getType()) && isNumber(rightExpression.getType())) {
187 return chooseWiderNumberType(leftExpression.getType(), rightExpression.getType());
188 }
189 return null;
190
191 case Types.LEFT_SHIFT :
192 if (isNumber(leftExpression.getType()) && isNumber(rightExpression.getType())) {
193 return leftExpression.getTypeClass();
194 }
195 else if (leftExpression.getTypeClass() != null && Collection.class.isAssignableFrom(leftExpression.getTypeClass() )) {
196 return Collection.class;
197 }
198 else if (leftExpression.getTypeClass() != null && OutputStream.class.isAssignableFrom(leftExpression.getTypeClass())) {
199 return Writer.class;
200 }
201 else if (leftExpression.getTypeClass() != null && StringBuffer.class.isAssignableFrom(leftExpression.getTypeClass())) {
202 return Writer.class;
203 }
204 return null;
205 case Types.RIGHT_SHIFT :
206 case Types.RIGHT_SHIFT_UNSIGNED :
207 if (isNumber(leftExpression.getType()) && isNumber(rightExpression.getType())) {
208 return leftExpression.getTypeClass();
209 }
210 return null;
211 case Types.FIND_REGEX :
212 return Matcher.class;
213 case Types.LEFT_SQUARE_BRACKET :
214 Class cls = leftExpression.getTypeClass();
215 if (cls != null) {
216 if (cls.isArray()) {
217 Class elemType = cls.getComponentType();
218 //setTypeClass(elemType);
219 return elemType;
220 }
221 else if (leftExpression instanceof ListExpression) {
222 Class elemType = ((ListExpression)leftExpression).getComponentTypeClass();
223 //setTypeClass(elemType);
224 return elemType;
225 }
226 else if (leftExpression instanceof MapExpression) {
227 return Object.class;
228 }
229 else if (List.class.isAssignableFrom(cls)) {
230 return (Object.class);
231 }
232 else if (Map.class.isAssignableFrom(cls)) {
233 return (Object.class);
234 }
235 }
236 break;
237 }
238 return null;
239 }
240
241 private static boolean isNumber(String type) {
242 if (type!= null) {
243 if ( type.equals("int") ||
244 type.equals("short") ||
245 type.equals("byte") ||
246 type.equals("char") ||
247 type.equals("float") ||
248 type.equals("long") ||
249 type.equals("double") ||
250 type.equals("java.lang.Short") ||
251 type.equals("java.lang.Byte") ||
252 type.equals("java.lang.Character") ||
253 type.equals("java.lang.Integer") ||
254 type.equals("java.lang.Float") ||
255 type.equals("java.lang.Long") ||
256 type.equals("java.lang.Double") ||
257 type.equals("java.math.BigInteger") ||
258 type.equals("java.math.BigDecimal"))
259 {
260 return true;
261 }
262 }
263 return false;
264 }
265
266 private static Class getObjectClassForNumber(String type) {
267 if (type.equals("boolean") || type.equals("java.lang.Boolean")) {
268 return Boolean.class;
269 }
270 else if (type.equals("short") || type.equals("java.lang.Short")) {
271 return Short.class;
272 }
273 else if (type.equals("int") || type.equals("java.lang.Integer")) {
274 return Integer.class;
275 }
276 else if (type.equals("char") || type.equals("java.lang.Character")) {
277 return Integer.class;
278 }
279 else if (type.equals("long") || type.equals("java.lang.Long")) {
280 return Long.class;
281 }
282 else if (type.equals("float") || type.equals("java.lang.Float")) {
283 return Float.class;
284 }
285 else if (type.equals("double") || type.equals("java.lang.Double")) {
286 return Double.class;
287 }
288 else if (type.equals("java.math.BigInteger")) {
289 return BigInteger.class;
290 }
291 else if (type.equals("java.math.BigDecimal")) {
292 return BigDecimal.class;
293 }
294 else {
295 return null;
296 }
297 }
298
299 private static boolean isFloatingPoint(Class cls) {
300 return cls == Double.class || cls == Float.class;
301 }
302
303 private static boolean isInteger(Class cls) {
304 return cls == Integer.class || cls == Byte.class || cls == Short.class || cls == Character.class;
305 }
306
307 private static boolean isLong(Class cls) {
308 return cls == Long.class;
309 }
310
311 private static boolean isBigDecimal(Class cls) {
312 return cls == BigDecimal.class;
313 }
314
315 private static boolean isBigInteger(Class cls) {
316 return cls == BigInteger.class;
317 }
318
319 private static Class chooseWiderNumberType(String lefts, String rights) {
320 Class left = getObjectClassForNumber(lefts);
321 Class right = getObjectClassForNumber(rights);
322 if (isFloatingPoint(left) || isFloatingPoint(right)) {
323 return Double.class;
324 }
325 else if (isBigDecimal(left) || isBigDecimal(right)) {
326 return BigDecimal.class;
327 }
328 else if (isBigInteger(left) || isBigInteger(right)) {
329 return BigInteger.class;
330 }
331 else if (isLong(left) || isLong(right)){
332 return Long.class;
333 }
334 return Integer.class;
335
336 // see NumberMath for full Groovy math promotion
337 }
338 public String toString() {
339 return super.toString() +"[" + leftExpression + operation + rightExpression + "]";
340 }
341
342 public void visit(GroovyCodeVisitor visitor) {
343 visitor.visitBinaryExpression(this);
344 }
345
346 public Expression transformExpression(ExpressionTransformer transformer) {
347 return new BinaryExpression(transformer.transform(leftExpression), operation, transformer.transform(rightExpression));
348 }
349
350 public Expression getLeftExpression() {
351 return leftExpression;
352 }
353
354 public void setLeftExpression(Expression leftExpression) {
355 this.leftExpression = leftExpression;
356 }
357
358 public void setRightExpression(Expression rightExpression) {
359 this.rightExpression = rightExpression;
360 }
361
362 public Token getOperation() {
363 return operation;
364 }
365
366 public Expression getRightExpression() {
367 return rightExpression;
368 }
369
370 public String getText() {
371 if (operation.getType() == Types.LEFT_SQUARE_BRACKET) {
372 return leftExpression.getText() + "[" + rightExpression.getText() + "]";
373 }
374 return "(" + leftExpression.getText() + " " + operation.getText() + " " + rightExpression.getText() + ")";
375 }
376
377
378 /**
379 * Creates an assignment expression in which the specified expression
380 * is written into the specified variable name.
381 */
382
383 public static BinaryExpression newAssignmentExpression( String variable, Expression rhs ) {
384 VariableExpression lhs = new VariableExpression( variable );
385 Token operator = Token.newPlaceholder( Types.ASSIGN );
386
387 return new BinaryExpression( lhs, operator, rhs );
388 }
389
390
391 /**
392 * Creates variable initialization expression in which the specified expression
393 * is written into the specified variable name.
394 */
395
396 public static BinaryExpression newInitializationExpression( String variable, Type type, Expression rhs ) {
397 VariableExpression lhs = new VariableExpression( variable );
398
399 if( type != null ) {
400 lhs.setType( type.getName() );
401 }
402
403 Token operator = Token.newPlaceholder( Types.ASSIGN );
404
405 return new BinaryExpression( lhs, operator, rhs );
406 }
407
408 protected void resolveType(AsmClassGenerator resolver) {
409 leftExpression.resolve(resolver);
410 rightExpression.resolve(resolver);
411 Class cls = resolveThisType(operation);
412 if (cls != null) {
413 setTypeClass(cls);
414 }
415 else {
416 setResolveFailed(true);
417 setFailure("unknown. the right expression may have not been resolved");
418 }
419 }
420 }