View Javadoc

1   /*
2    $Id: BinaryExpression.java,v 1.6 2004/07/10 03:31:37 bran Exp $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  package org.codehaus.groovy.ast.expr;
47  
48  import java.io.OutputStream;
49  import java.io.Writer;
50  import java.math.BigDecimal;
51  import java.math.BigInteger;
52  import java.util.Collection;
53  import java.util.Date;
54  import java.util.List;
55  import java.util.Map;
56  import java.util.regex.Matcher;
57  
58  import org.codehaus.groovy.ast.GroovyCodeVisitor;
59  import org.codehaus.groovy.ast.Type;
60  import org.codehaus.groovy.classgen.AsmClassGenerator2;
61  import org.codehaus.groovy.syntax.Token;
62  import org.codehaus.groovy.syntax.Types;
63  import groovy.lang.GString;
64  
65  /***
66   * Represents two expressions and an operation
67   * 
68   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
69   * @version $Revision: 1.6 $
70   */
71  public class BinaryExpression extends Expression {
72      
73      private Expression leftExpression;
74      private Expression rightExpression;
75      private Token operation;
76      
77      public BinaryExpression(Expression leftExpression,
78                              Token operation,
79                              Expression rightExpression) {
80          this.leftExpression = leftExpression;
81          this.operation = operation;
82          this.rightExpression = rightExpression;
83  
84      }
85  
86      public Class getTypeClass() {
87          typeClass = resolveThisType(operation);
88          return typeClass;
89      }
90  
91      public boolean isDynamic() {
92          return false;  //To change body of implemented methods use File | Settings | File Templates.
93      }
94  
95      private Class resolveThisType(Token operation) {
96          switch (operation.getType()) {
97              case Types.EQUAL : // = assignment
98                  if (!leftExpression.isDynamic())
99                      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             case Types.LEFT_SHIFT :
184                 if (isNumber(leftExpression.getType()) && isNumber(rightExpression.getType())) {
185                     return leftExpression.getTypeClass();
186                 }
187                 else if (leftExpression.getTypeClass() != null && Collection.class.isAssignableFrom(leftExpression.getTypeClass() )) {
188                     return Collection.class;
189                 }
190                 else if (leftExpression.getTypeClass() != null && OutputStream.class.isAssignableFrom(leftExpression.getTypeClass())) {
191                     return Writer.class;
192                 }
193                 else if (leftExpression.getTypeClass() != null && StringBuffer.class.isAssignableFrom(leftExpression.getTypeClass())) {
194                     return Writer.class;
195                 }
196                 return null;
197             case Types.RIGHT_SHIFT :
198             case Types.RIGHT_SHIFT_UNSIGNED :
199                 if (isNumber(leftExpression.getType()) && isNumber(rightExpression.getType())) {
200                     return leftExpression.getTypeClass();
201                 }
202                 return null;
203             case Types.FIND_REGEX :
204                 return Matcher.class;
205             case Types.LEFT_SQUARE_BRACKET :
206                 Class cls = leftExpression.getTypeClass();
207                 if (cls != null) {
208                     if (cls.isArray()) {
209                         Class elemType = cls.getComponentType();
210                         //setTypeClass(elemType);
211                         return elemType;
212                     }
213                     else if (leftExpression instanceof ListExpression) {
214                         Class elemType = ((ListExpression)leftExpression).getComponentTypeClass();
215                         //setTypeClass(elemType);
216                         return elemType;
217                     }
218                     else if (leftExpression instanceof MapExpression) {
219                         return Object.class;
220                     }
221                     else if (List.class.isAssignableFrom(cls)) {
222                         return (Object.class);
223                     }
224                     else if (Map.class.isAssignableFrom(cls)) {
225                         return (Object.class);
226                     }
227                 }
228                 break;
229         }
230         return null;
231     }
232 
233     private static boolean isNumber(String type) {
234         if (type!= null) {
235             if (    type.equals("int") ||
236                     type.equals("short") ||
237                     type.equals("byte") ||
238                     type.equals("char") ||
239                     type.equals("float") ||
240                     type.equals("long") ||
241                     type.equals("double") ||
242                     type.equals("java.lang.Short") ||
243                     type.equals("java.lang.Byte") ||
244                     type.equals("java.lang.Character") ||
245                     type.equals("java.lang.Integer") ||
246                     type.equals("java.lang.Float") ||
247                     type.equals("java.lang.Long") ||
248                     type.equals("java.lang.Double") ||
249                     type.equals("java.math.BigInteger") ||
250                     type.equals("java.math.BigDecimal"))
251             {
252                 return true;
253             }
254         }
255         return false;
256     }
257 
258     private static Class getObjectClassForNumber(String type) {
259         if (type.equals("boolean") || type.equals("java.lang.Boolean")) {
260             return Boolean.class;
261         }
262         else if (type.equals("short") || type.equals("java.lang.Short")) {
263             return Short.class;
264         }
265         else if (type.equals("int") || type.equals("java.lang.Integer")) {
266                     return Integer.class;
267         }
268         else if (type.equals("char") || type.equals("java.lang.Character")) {
269                     return Integer.class;
270         }
271         else if (type.equals("long") || type.equals("java.lang.Long")) {
272             return Long.class;
273         }
274         else if (type.equals("float") || type.equals("java.lang.Float")) {
275             return Float.class;
276         }
277         else if (type.equals("double") || type.equals("java.lang.Double")) {
278             return Double.class;
279         }
280         else if (type.equals("java.math.BigInteger")) {
281             return BigInteger.class;
282         }
283         else if (type.equals("java.math.BigDecimal")) {
284             return BigDecimal.class;
285         }
286         else {
287             return null;
288         }
289     }
290 
291     private static boolean isFloatingPoint(Class cls) {
292 		return cls == Double.class || cls == Float.class;
293 	}
294 
295 	private static boolean isInteger(Class cls) {
296 		return cls == Integer.class || cls == Byte.class || cls == Short.class || cls == Character.class;
297 	}
298 
299 	private static boolean isLong(Class cls) {
300 		return cls == Long.class;
301 	}
302 
303 	private static boolean isBigDecimal(Class cls) {
304 		return cls == BigDecimal.class;
305 	}
306 
307 	private static boolean isBigInteger(Class cls) {
308 		return cls == BigInteger.class;
309 	}
310 
311     private static Class chooseWiderNumberType(String lefts, String rights) {
312         Class left = getObjectClassForNumber(lefts);
313         Class right = getObjectClassForNumber(rights);
314         if (isFloatingPoint(left) || isFloatingPoint(right)) {
315             return Double.class;
316         }
317         else if (isBigDecimal(left) || isBigDecimal(right)) {
318             return BigDecimal.class;
319         }
320         else if (isBigInteger(left) || isBigInteger(right)) {
321             return BigInteger.class;
322         }
323         else if (isLong(left) || isLong(right)){
324             return Long.class;
325         }
326         return Integer.class;
327 
328         // see NumberMath for full Groovy math promotion
329     }
330     public String toString() {
331         return super.toString() +"[" + leftExpression + operation + rightExpression + "]";
332     }
333 
334     public void visit(GroovyCodeVisitor visitor) {
335         visitor.visitBinaryExpression(this);
336     }
337 
338     public Expression transformExpression(ExpressionTransformer transformer) {
339         return new BinaryExpression(transformer.transform(leftExpression), operation, transformer.transform(rightExpression));
340     }
341 
342     public Expression getLeftExpression() {
343         return leftExpression;
344     }
345 
346     public void setLeftExpression(Expression leftExpression) {
347         this.leftExpression = leftExpression;
348     }
349 
350     public void setRightExpression(Expression rightExpression) {
351         this.rightExpression = rightExpression;
352     }
353 
354     public Token getOperation() {
355         return operation;
356     }
357 
358     public Expression getRightExpression() {
359         return rightExpression;
360     }
361 
362     public String getText() {
363         if (operation.getType() == Types.LEFT_SQUARE_BRACKET) {
364             return leftExpression.getText() + "[" + rightExpression.getText() + "]";
365         }
366         return "(" + leftExpression.getText() + " " + operation.getText() + " " + rightExpression.getText() + ")";
367     }
368     
369     
370    /***
371     *  Creates an assignment expression in which the specified expression
372     *  is written into the specified variable name.   
373     */
374     
375     public static BinaryExpression newAssignmentExpression( String variable, Expression rhs ) {
376     	VariableExpression lhs = new VariableExpression( variable );
377     	Token         operator = Token.newPlaceholder( Types.ASSIGN );
378     
379     	return new BinaryExpression( lhs, operator, rhs );
380     }
381 
382 
383     /***
384      *  Creates variable initialization expression in which the specified expression
385      *  is written into the specified variable name.   
386      */
387      
388      public static BinaryExpression newInitializationExpression( String variable, Type type, Expression rhs ) {
389      	VariableExpression lhs = new VariableExpression( variable );
390      
391      	if( type != null ) {
392      	    lhs.setType( type.getName() );
393      	}
394      
395      	Token operator = Token.newPlaceholder( Types.ASSIGN );
396      
397         return new BinaryExpression( lhs, operator, rhs );
398      }
399 
400     protected  void resolveType(AsmClassGenerator2 resolver) {
401         leftExpression.resolve(resolver);
402         rightExpression.resolve(resolver);
403         Class cls = resolveThisType(operation);
404         if (cls != null) {
405             setTypeClass(cls);
406         }
407         else {
408              setResolveFailed(true);
409             setFailure("unknown. the right expression may have not been resolved");
410         }
411     }
412 }