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 }