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 }