View Javadoc

1   /*
2    $Id: BinaryExpression.java,v 1.8 2005/02/23 17:06:37 phk 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.AsmClassGenerator;
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.8 $
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 
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 }