Updated TypeOracleBuilder to allow initializing annotation values from constant fields such as Long.MAX_VALUE and from constant conditional expressions.  I renamed evaluateExpression to evaluateConstantExpression to more closely reflect its purpose.  The annotation test cases were updated to check for these new cases.

Patch by: mmendez
Review by: scottb (TBR)

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1654 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java b/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
index 30ace23..25ae687 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
@@ -55,25 +55,21 @@
 import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.Argument;
 import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
-import org.eclipse.jdt.internal.compiler.ast.CharLiteral;
 import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
 import org.eclipse.jdt.internal.compiler.ast.Clinit;
 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.DoubleLiteral;
+import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
 import org.eclipse.jdt.internal.compiler.ast.Expression;
 import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.FloatLiteral;
 import org.eclipse.jdt.internal.compiler.ast.Initializer;
-import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
 import org.eclipse.jdt.internal.compiler.ast.Javadoc;
-import org.eclipse.jdt.internal.compiler.ast.LongLiteral;
 import org.eclipse.jdt.internal.compiler.ast.MagicLiteral;
 import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NameReference;
 import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
 import org.eclipse.jdt.internal.compiler.ast.NumberLiteral;
-import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
 import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
 import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
 import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
@@ -83,13 +79,13 @@
 import org.eclipse.jdt.internal.compiler.ast.Wildcard;
 import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
 import org.eclipse.jdt.internal.compiler.env.IGenericType;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
 import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
 import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
 import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
 import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
-import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
 import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
@@ -261,6 +257,39 @@
     tagValues.clear();
   }
 
+  /**
+   * Returns the value associated with a JDT constant.
+   */
+  private static Object getConstantValue(Constant constant) {
+    switch (constant.typeID()) {
+      case TypeIds.T_char:
+        return constant.charValue();
+      case TypeIds.T_byte:
+        return constant.byteValue();
+      case TypeIds.T_short:
+        return constant.shortValue();
+      case TypeIds.T_boolean:
+        return constant.booleanValue();
+      case TypeIds.T_long:
+        return constant.longValue();
+      case TypeIds.T_double:
+        return constant.doubleValue();
+      case TypeIds.T_float:
+        return constant.floatValue();
+      case TypeIds.T_int:
+        return constant.intValue();
+      case TypeIds.T_JavaLangString:
+        return constant.stringValue();
+      case TypeIds.T_null:
+        return null;
+      default:
+        break;
+    }
+
+    assert false : "Unknown constant type";
+    return null;
+  }
+
   private static String getMethodName(JClassType enclosingType,
       AbstractMethodDeclaration jmethod) {
     if (jmethod.isConstructor()) {
@@ -270,6 +299,13 @@
     }
   }
 
+  /**
+   * Returns the number associated with a JDT numeric literal.
+   */
+  private static Object getNumericLiteralValue(NumberLiteral expression) {
+    return getConstantValue(expression.constant);
+  }
+
   private static boolean isAnnotation(TypeDeclaration typeDecl) {
     if (typeDecl.kind() == IGenericType.ANNOTATION_TYPE_DECL) {
       return true;
@@ -612,40 +648,7 @@
     return oracle;
   }
 
-  private JUpperBound createTypeVariableBounds(TreeLogger logger,
-      TypeVariableBinding tvBinding) {
-    TypeBinding jfirstBound = tvBinding.firstBound;
-    if (jfirstBound == null) {
-      // No bounds were specified, so we default to Object
-      JClassType upperBound = (JClassType) resolveType(logger,
-          tvBinding.superclass);
-      /*
-       * Can't test for equality with TypeOracle.getJavaLangObject() since it
-       * may not be initialized at this point
-       */
-      assert (Object.class.getName().equals(upperBound.getQualifiedSourceName()));
-      return new JUpperBound(upperBound);
-    }
-
-    List<JClassType> bounds = new ArrayList<JClassType>();
-    JClassType firstBound = (JClassType) resolveType(logger, jfirstBound);
-    if (firstBound.isClass() != null) {
-      bounds.add(firstBound);
-    }
-
-    ReferenceBinding[] jsuperInterfaces = tvBinding.superInterfaces();
-    for (ReferenceBinding jsuperInterface : jsuperInterfaces) {
-      JClassType superInterface = (JClassType) resolveType(logger,
-          jsuperInterface);
-      assert (superInterface != null);
-      assert (superInterface.isInterface() != null);
-      bounds.add(superInterface);
-    }
-
-    return new JUpperBound(bounds.toArray(NO_JCLASSES));
-  }
-
-  private Object evaluateAnnotationExpression(TreeLogger logger,
+  private Object createAnnotationInstance(TreeLogger logger,
       Expression expression) {
     Annotation annotation = (Annotation) expression;
 
@@ -665,7 +668,7 @@
 
       // Value
       Expression expressionValue = mvp.value;
-      Object value = evaluateExpression(logger, expressionValue);
+      Object value = evaluateConstantExpression(logger, expressionValue);
       if (value == null) {
         return null;
       }
@@ -703,11 +706,14 @@
         identifierToValue);
   }
 
-  private Object evaluateArrayInitializerExpression(TreeLogger logger,
-      Expression expression) {
-    ArrayInitializer arrayInitializer = (ArrayInitializer) expression;
+  private Object createArrayConstant(TreeLogger logger,
+      ArrayInitializer arrayInitializer) {
     Class<?> leafComponentClass = getClassLiteral(logger,
         arrayInitializer.binding.leafComponentType);
+    if (leafComponentClass == null) {
+      return null;
+    }
+
     int[] dimensions = new int[arrayInitializer.binding.dimensions];
 
     Expression[] initExpressions = arrayInitializer.expressions;
@@ -720,7 +726,7 @@
     if (initExpressions != null) {
       for (int i = 0; i < initExpressions.length; ++i) {
         Expression arrayInitExp = initExpressions[i];
-        Object value = evaluateExpression(logger, arrayInitExp);
+        Object value = evaluateConstantExpression(logger, arrayInitExp);
         if (value != null) {
           Array.set(array, i, value);
         } else {
@@ -737,7 +743,41 @@
     return null;
   }
 
-  private Object evaluateExpression(TreeLogger logger, Expression expression) {
+  private JUpperBound createTypeVariableBounds(TreeLogger logger,
+      TypeVariableBinding tvBinding) {
+    TypeBinding jfirstBound = tvBinding.firstBound;
+    if (jfirstBound == null) {
+      // No bounds were specified, so we default to Object
+      JClassType upperBound = (JClassType) resolveType(logger,
+          tvBinding.superclass);
+      /*
+       * Can't test for equality with TypeOracle.getJavaLangObject() since it
+       * may not be initialized at this point
+       */
+      assert (Object.class.getName().equals(upperBound.getQualifiedSourceName()));
+      return new JUpperBound(upperBound);
+    }
+
+    List<JClassType> bounds = new ArrayList<JClassType>();
+    JClassType firstBound = (JClassType) resolveType(logger, jfirstBound);
+    if (firstBound.isClass() != null) {
+      bounds.add(firstBound);
+    }
+
+    ReferenceBinding[] jsuperInterfaces = tvBinding.superInterfaces();
+    for (ReferenceBinding jsuperInterface : jsuperInterfaces) {
+      JClassType superInterface = (JClassType) resolveType(logger,
+          jsuperInterface);
+      assert (superInterface != null);
+      assert (superInterface.isInterface() != null);
+      bounds.add(superInterface);
+    }
+
+    return new JUpperBound(bounds.toArray(NO_JCLASSES));
+  }
+
+  private Object evaluateConstantExpression(TreeLogger logger, 
+      Expression expression) {
     if (expression instanceof MagicLiteral) {
       if (expression instanceof FalseLiteral) {
         return Boolean.FALSE;
@@ -747,7 +787,7 @@
         return Boolean.TRUE;
       }
     } else if (expression instanceof NumberLiteral) {
-      Object value = evaluateNumericExpression(expression);
+      Object value = getNumericLiteralValue((NumberLiteral) expression);
       if (value != null) {
         return value;
       }
@@ -761,67 +801,90 @@
         return clazz;
       }
     } else if (expression instanceof ArrayInitializer) {
-      Object value = evaluateArrayInitializerExpression(logger, expression);
+      Object value = createArrayConstant(logger, (ArrayInitializer) expression);
       if (value != null) {
         return value;
       }
-    } else if (expression instanceof QualifiedNameReference) {
-      QualifiedNameReference qualifiedNameRef = (QualifiedNameReference) expression;
-      Class clazz = getClassLiteral(logger, qualifiedNameRef.actualReceiverType);
-      assert (clazz.isEnum());
-      if (clazz != null) {
-        FieldBinding fieldBinding = qualifiedNameRef.fieldBinding();
-        String enumName = String.valueOf(fieldBinding.name);
-        return Enum.valueOf(clazz, enumName);
+    } else if (expression instanceof NameReference) {
+      /*
+       * This name reference can only be to something that is a constant value,
+       * or an enumerated type since annotation values must be constants.
+       */
+      NameReference nameRef = (NameReference) expression;
+      
+      if (nameRef.constant != Constant.NotAConstant) {
+        return getConstantValue(nameRef.constant);
+      } else {
+        Class clazz = getClassLiteral(logger,
+            nameRef.actualReceiverType);
+        if (clazz.isEnum()) {
+          String enumName = String.valueOf(nameRef.fieldBinding().name);
+          return Enum.valueOf(clazz, enumName);
+        } else {
+          /* 
+           * If this is not an enumeration, then fall through to failure case 
+           * below.
+           */
+        }
       }
     } else if (expression instanceof Annotation) {
-      Object annotationInstance = evaluateAnnotationExpression(logger,
+      Object annotationInstance = createAnnotationInstance(logger,
           expression);
       if (annotationInstance != null) {
         return annotationInstance;
       }
+    } else if (expression instanceof ConditionalExpression) {
+      ConditionalExpression condExpression = (ConditionalExpression) expression;
+      assert (condExpression.constant != Constant.NotAConstant);
+
+      return getConstantValue(condExpression.constant);
     }
 
     assert (false);
     return null;
   }
-
-  private Object evaluateNumericExpression(Expression expression) {
-    if (expression instanceof CharLiteral) {
-      CharLiteral charLiteral = (CharLiteral) expression;
-      return Character.valueOf(charLiteral.constant.charValue());
-    } else if (expression instanceof DoubleLiteral) {
-      DoubleLiteral doubleLiteral = (DoubleLiteral) expression;
-      return Double.valueOf(doubleLiteral.constant.doubleValue());
-    } else if (expression instanceof FloatLiteral) {
-      FloatLiteral floatLiteral = (FloatLiteral) expression;
-      return Float.valueOf(floatLiteral.constant.floatValue());
-    } else if (expression instanceof IntLiteral) {
-      IntLiteral intLiteral = (IntLiteral) expression;
-      return Integer.valueOf(intLiteral.constant.intValue());
-    } else if (expression instanceof LongLiteral) {
-      LongLiteral longLiteral = (LongLiteral) expression;
-      return Long.valueOf(longLiteral.constant.longValue());
+ 
+  private Class<?> getClassLiteral(TreeLogger logger, TypeBinding resolvedType) {
+    JType type = resolveType(logger, resolvedType);
+    if (type == null) {
+      return null;
     }
 
-    return null;
+    if (type.isPrimitive() != null) {
+      return getClassLiteralForPrimitive(type.isPrimitive());
+    } else {
+      try {
+        String className = computeBinaryClassName(type);
+        Class<?> clazz = Class.forName(className);
+        return clazz;
+      } catch (ClassNotFoundException e) {
+        logger.log(TreeLogger.ERROR, "", e);
+        return null;
+      }
+    }
   }
 
-  private Class<?> getClassLiteral(TreeLogger logger, TypeBinding resolvedType) {
-    JClassType annotationType = (JClassType) resolveType(logger, resolvedType);
-    if (annotationType == null) {
-      return null;
+  private Class<?> getClassLiteralForPrimitive(JPrimitiveType type) {
+    if (type == JPrimitiveType.BOOLEAN) {
+      return boolean.class;
+    } else if (type == JPrimitiveType.BYTE) {
+      return byte.class;
+    } else if (type == JPrimitiveType.CHAR) {
+      return char.class;
+    } else if (type == JPrimitiveType.DOUBLE) {
+      return double.class;
+    } else if (type == JPrimitiveType.FLOAT) {
+      return float.class;
+    } else if (type == JPrimitiveType.INT) {
+      return int.class;
+    } else if (type == JPrimitiveType.LONG) {
+      return long.class;
+    } else if (type == JPrimitiveType.SHORT) {
+      return short.class;
     }
 
-    String className = computeBinaryClassName(annotationType);
-    try {
-      Class<?> clazz = Class.forName(className);
-      return clazz;
-    } catch (ClassNotFoundException e) {
-      logger.log(TreeLogger.ERROR, "", e);
-      // TODO(mmendez): how should we deal with this error?
-      return null;
-    }
+    assert (false);
+    return null;
   }
 
   private CompilationUnitProvider getCup(TypeDeclaration typeDecl) {
@@ -835,14 +898,14 @@
     return String.valueOf(CharOperation.concatWith(pkgParts, '.'));
   }
 
-  /** 
+  /**
    * Returns the qualified name of the binding, excluding any type parameter
    * information.
    */
   private String getQualifiedName(ReferenceBinding binding) {
     String qualifiedName = CharOperation.toString(binding.compoundName);
     if (binding instanceof LocalTypeBinding) {
-       // The real name of a local type is its constant pool name.
+      // The real name of a local type is its constant pool name.
       qualifiedName = CharOperation.charToString(binding.constantPoolName());
       qualifiedName = qualifiedName.replace('/', '.');
     } else {
@@ -942,8 +1005,12 @@
       return false;
     }
 
-    java.lang.annotation.Annotation annotation = (java.lang.annotation.Annotation) evaluateExpression(
+    java.lang.annotation.Annotation annotation = (java.lang.annotation.Annotation) createAnnotationInstance(
         logger, jannotation);
+    if (annotation == null) {
+      return false;
+    }
+
     declaredAnnotations.put(clazz, annotation);
     return (annotation != null) ? true : false;
   }
@@ -1067,7 +1134,7 @@
         AnnotationMethodDeclaration annotationMethod = (AnnotationMethodDeclaration) jmethod;
         Object defaultValue = null;
         if (annotationMethod.defaultValue != null) {
-          defaultValue = evaluateExpression(logger,
+          defaultValue = evaluateConstantExpression(logger,
               annotationMethod.defaultValue);
         }
         method = new JAnnotationMethod(enclosingType, name, declStart, declEnd,
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java
index 3db837a..9b86591 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java
@@ -59,6 +59,19 @@
     assertEquals(testAnnotationValue, testAnnotation.value());
     assertEquals(nestedAnnotationValue,
         testAnnotation.nestedAnnotation().value());
+
+    // Tests default values using conditional statements.
+    assertEquals(realAnnotation.longValue(), testAnnotation.longValue());
+
+    // Tests default value of array type.
+    assertEquals(realAnnotation.intArrayValue().length, 
+        testAnnotation.intArrayValue().length);
+
+    // Tests default value which is a field reference.
+    assertEquals(realAnnotation.stringValue(), testAnnotation.stringValue());
+
+    // Tests default value that is a class literal.
+    assertEquals(realAnnotation.classLiteral(), testAnnotation.classLiteral());
   }
 
   private final TreeLogger logger = TreeLogger.NULL;
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/TestAnnotation.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/TestAnnotation.java
index 76fa48e..bb88bfc 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/TestAnnotation.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/TestAnnotation.java
@@ -42,5 +42,34 @@
    */
   NestedAnnotation nestedAnnotation() default @NestedAnnotation("Not assigned");
 
-  int x = 0;
+  /**
+   * Used to test initialization using conditional statements.
+   */
+  boolean useMinLong = true;
+
+  /**
+   * Used to test initialization using SingleNameReferences.
+   */
+  String defaultStringValue = "Hello There";
+  
+  /**
+   * Tests default values using conditional statements.
+   */
+  long longValue() default useMinLong ? Long.MIN_VALUE : Long.MAX_VALUE;
+
+  /**
+   * Tests array default values.
+   */
+  int[] intArrayValue() default {1,2,3};
+
+  /**
+   * Tests default value initialization via a QualifiedNameReference.
+   */
+  String stringValue() default TestAnnotation.defaultStringValue;
+  
+  /**
+   * Tests default value initialization of class literals. 
+   */
+  Class<?> classLiteral() default Object.class;
 }
+