1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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;
93 }
94
95 private Class resolveThisType(Token operation) {
96 switch (operation.getType()) {
97 case Types.EQUAL :
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
219 return elemType;
220 }
221 else if (leftExpression instanceof ListExpression) {
222 Class elemType = ((ListExpression)leftExpression).getComponentTypeClass();
223
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
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 }