This patch adds annotation support to the TypeOracle used by generators.  It is a departure from what we have done previously because it models annotations as actual instances that you invoke methods on instead of metadata which is merely inspected.  In this regard it is more like APT and java.lang.annotation.Annotation than our current MetaData API.  Because of this, generators will now be able to use solutions of the form:

// Special annotation
public @interface MyAnnotation {
   String value();
}
 
// Generator implementation
TypeOracle typeOracle = getTypeOracle();
JClassType annotatedClass = typeOracle.getType("com...");
MyAnnotation myAnnotation = annotatedClass.getAnnotation(MyAnnotation.class);
if (myAnnotation.value().equals("SpecialValue")) {
   ...
} 

This seems like it would be more useful to a developer than a set of name value pairs. 

Possible future enhancements:
Ability to ask the TypeOracle for all elements that have a particular annotation.
Homoginize with the existing metadata API, i.e. gwt.typeArgs, et al. 

Patch by: mmendez, tobyr, scottb
Review by: scottb, mmendez, tobyr (some pair prog + desk checking)


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1423 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/Annotations.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/Annotations.java
new file mode 100644
index 0000000..2d7631c
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/Annotations.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Default implementation of the {@link HasAnnotations} interface.
+ */
+class Annotations implements HasAnnotations {
+  /**
+   * All annotations declared on the annotated element.
+   */
+  private final Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+
+  /**
+   * Lazily initialized collection of annotations declared on or inherited by
+   * the annotated element.
+   */
+  private Map<Class<? extends Annotation>, Annotation> lazyAnnotations = null;
+
+  /**
+   * If not <code>null</code> the parent to inherit annotations from.
+   */
+  private Annotations parent;
+
+  public void addAnnotations(
+      Map<Class<? extends Annotation>, Annotation> annotations) {
+    if (annotations != null) {
+      assert (!annotations.containsValue(null));
+      declaredAnnotations.putAll(annotations);
+    }
+  }
+
+  public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+    initializeAnnotations();
+    return (T) lazyAnnotations.get(annotationClass);
+  }
+
+  public Annotation[] getAnnotations() {
+    initializeAnnotations();
+    Collection<Annotation> values = lazyAnnotations.values();
+    return values.toArray(new Annotation[values.size()]);
+  }
+
+  public Annotation[] getDeclaredAnnotations() {
+    Collection<Annotation> values = declaredAnnotations.values();
+    return values.toArray(new Annotation[values.size()]);
+  }
+
+  public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
+    return getAnnotation(annotationClass) != null;
+  }
+
+  void addAnnotation(Class<? extends Annotation> annotationClass,
+      Annotation annotationInstance) {
+    assert (annotationClass != null);
+    assert (annotationInstance != null);
+    assert (!declaredAnnotations.containsKey(annotationClass));
+
+    declaredAnnotations.put(annotationClass, annotationInstance);
+  }
+
+  void setParent(Annotations parent) {
+    this.parent = parent;
+  }
+
+  private void initializeAnnotations() {
+    if (lazyAnnotations != null) {
+      return;
+    }
+
+    if (parent != null) {
+      lazyAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+      parent.initializeAnnotations();
+      for (Entry<Class<? extends Annotation>, Annotation> entry : parent.lazyAnnotations.entrySet()) {
+        if (entry.getValue().annotationType().isAnnotationPresent(Inherited.class)) {
+          lazyAnnotations.put(entry.getKey(), entry.getValue());
+        }
+      }
+      
+      lazyAnnotations.putAll(declaredAnnotations);
+    } else {
+      lazyAnnotations = declaredAnnotations;
+    }
+  }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/HasAnnotations.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/HasAnnotations.java
new file mode 100644
index 0000000..37d1cc1
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/HasAnnotations.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Interface implemented by elements that can have annotations. This interface
+ * is a departure for GWT in that it used types declared in the
+ * java.lang.annotation package instead of types declared as part of this
+ * typeinfo package. This reflects a compromise between a pure
+ * {@link TypeOracle} model and one that is more useful to developers.
+ */
+public interface HasAnnotations {
+  /**
+   * Returns an instance of the specified annotation type if it is present on
+   * this element or <code>null</code> if it is not.
+   * 
+   * @param annotationClass annotation type to search for
+   * @return instance of the specified annotation type if it is present on this
+   *         element or <code>null</code> if it is not
+   */
+  <T extends Annotation> T getAnnotation(Class<T> annotationClass);
+
+  /**
+   * Returns all annotations that are declared or inherited by this element.
+   * 
+   * @return annotations that are declared or inherited by this element
+   */
+  Annotation[] getAnnotations();
+
+  /**
+   * Returns annotations that are declared on this element.
+   * 
+   * @return annotations that are declared on this element
+   */
+  Annotation[] getDeclaredAnnotations();
+
+  /**
+   * Returns <code>true</code> if this item has an annotation of the specified
+   * type.
+   * 
+   * @param annotationClass
+   * 
+   * @return <code>true</code> if this item has an annotation of the specified
+   *         type
+   */
+  boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
index 2969d5b..096cffd 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
@@ -15,13 +15,17 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Common superclass for {@link JMethod} and {@link JConstructor}.
  */
-public abstract class JAbstractMethod implements HasMetaData {
+public abstract class JAbstractMethod implements HasAnnotations, HasMetaData {
+
+  private final Annotations annotations = new Annotations();
 
   private int bodyEnd;
 
@@ -43,12 +47,13 @@
 
   // Only the builder can construct
   JAbstractMethod(String name, int declStart, int declEnd, int bodyStart,
-      int bodyEnd) {
+      int bodyEnd, Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
     this.name = name;
     this.declStart = declStart;
     this.declEnd = declEnd;
     this.bodyStart = bodyStart;
     this.bodyEnd = bodyEnd;
+    annotations.addAnnotations(declaredAnnotations);
   }
 
   public void addMetaData(String tagName, String[] values) {
@@ -72,6 +77,14 @@
     return null;
   }
 
+  public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+    return annotations.getAnnotation(annotationClass);
+  }
+
+  public Annotation[] getAnnotations() {
+    return annotations.getAnnotations();
+  }
+
   public int getBodyEnd() {
     return bodyEnd;
   }
@@ -80,6 +93,10 @@
     return bodyStart;
   }
 
+  public Annotation[] getDeclaredAnnotations() {
+    return annotations.getDeclaredAnnotations();
+  }
+
   public int getDeclEnd() {
     return declEnd;
   }
@@ -115,6 +132,14 @@
     return thrownTypes.toArray(TypeOracle.NO_JTYPES);
   }
 
+  public JAnnotationMethod isAnnotationMethod() {
+    return null;
+  }
+
+  public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
+    return annotations.isAnnotationPresent(annotationClass);
+  }
+
   public abstract JConstructor isConstructor();
 
   public boolean isDefaultAccess() {
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationMethod.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationMethod.java
new file mode 100644
index 0000000..4ba1607
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationMethod.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
+/**
+ * Method declared on an annotation type.
+ */
+public class JAnnotationMethod extends JMethod {
+  /**
+   * Default value for this annotation element. <code>null</code> is not a
+   * valid default value for an annotation element.
+   */
+  private final Object defaultValue;
+
+  public JAnnotationMethod(JClassType enclosingType, String name,
+      int declStart, int declEnd, int bodyStart, int bodyEnd,
+      Object defaultValue,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+    super(enclosingType, name, declStart, declEnd, bodyStart, bodyEnd,
+        declaredAnnotations);
+    this.defaultValue = defaultValue;
+  }
+
+  /**
+   * Returns the default value for this annotation method or <code>null</code>
+   * if there is not one.
+   * 
+   * @return default value for this annotation method or <code>null</code> if
+   *         there is not one
+   */
+  public Object getDefaultValue() {
+    return defaultValue;
+  }
+
+  @Override
+  public JAnnotationMethod isAnnotationMethod() {
+    return this;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java
new file mode 100644
index 0000000..19ae577
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo;
+
+import java.util.Arrays;
+
+/**
+ * Type representing an annotation type.
+ */
+public class JAnnotationType extends JClassType {
+
+  public JAnnotationType(TypeOracle oracle, CompilationUnitProvider cup,
+      JPackage declaringPackage, JClassType enclosingType, boolean isLocalType,
+      String name, int declStart, int declEnd, int bodyStart, int bodyEnd,
+      boolean isInterface) {
+    super(oracle, cup, declaringPackage, enclosingType, isLocalType, name,
+        declStart, declEnd, bodyStart, bodyEnd, isInterface);
+  }
+
+  @Override
+  public JAnnotationMethod getMethod(String name, JType[] paramTypes)
+      throws NotFoundException {
+    return (JAnnotationMethod) super.getMethod(name, paramTypes);
+  }
+
+  @Override
+  public JAnnotationMethod[] getMethods() {
+    JMethod[] methodArray = super.getMethods();
+    return Arrays.asList(methodArray).toArray(new JAnnotationMethod[0]);
+  }
+
+  @Override
+  public JAnnotationMethod[] getOverridableMethods() {
+    JMethod[] methodArray = super.getOverridableMethods();
+    return Arrays.asList(methodArray).toArray(new JAnnotationMethod[0]);
+  }
+
+  @Override
+  public JAnnotationType isAnnotation() {
+    return this;
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
index 9b85d55..483cc3b 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
@@ -19,6 +19,7 @@
 import com.google.gwt.dev.util.Util;
 
 import java.io.UnsupportedEncodingException;
+import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -31,9 +32,12 @@
 /**
  * Type representing a Java class or interface type.
  */
-public class JClassType extends JType implements HasMetaData {
+public class JClassType extends JType implements HasAnnotations, HasMetaData {
+
   private final Set<JClassType> allSubtypes = new HashSet<JClassType>();
 
+  private final Annotations annotations = new Annotations();
+
   private final int bodyEnd;
 
   private final int bodyStart;
@@ -119,6 +123,11 @@
     }
   }
 
+  public void addAnnotations(
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+    annotations.addAnnotations(declaredAnnotations);
+  }
+
   public void addImplementedInterface(JClassType intf) {
     assert (intf != null);
     interfaces.add(intf);
@@ -163,6 +172,14 @@
     return findNestedTypeImpl(parts, 0);
   }
 
+  public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+    return annotations.getAnnotation(annotationClass);
+  }
+
+  public Annotation[] getAnnotations() {
+    return annotations.getAnnotations();
+  }
+
   public int getBodyEnd() {
     return bodyEnd;
   }
@@ -188,6 +205,10 @@
     return constructors.toArray(TypeOracle.NO_JCTORS);
   }
 
+  public Annotation[] getDeclaredAnnotations() {
+    return annotations.getDeclaredAnnotations();
+  }
+
   public JClassType getEnclosingType() {
     return enclosingType;
   }
@@ -354,6 +375,10 @@
     return 0 != (modifierBits & TypeOracle.MOD_ABSTRACT);
   }
 
+  public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
+    return annotations.isAnnotationPresent(annotationClass);
+  }
+
   @Override
   public JArrayType isArray() {
     // intentional null
@@ -468,6 +493,7 @@
     assert (type != null);
     assert (isInterface() == null);
     this.superclass = type;
+    annotations.setParent(type.annotations);
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JConstructor.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JConstructor.java
index a3e2a25..f3ae15c 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JConstructor.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JConstructor.java
@@ -15,6 +15,9 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
 /**
  * Represents a constructor declaration.
  */
@@ -23,9 +26,15 @@
 
   public JConstructor(JClassType enclosingType, String name, int declStart,
       int declEnd, int bodyStart, int bodyEnd) {
-    super(name, declStart, declEnd, bodyStart, bodyEnd);
+    this(enclosingType, name, declStart, declEnd, bodyStart, bodyEnd, null);
+  }
+
+  public JConstructor(JClassType enclosingType, String name, int declStart,
+      int declEnd, int bodyStart, int bodyEnd,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+    super(name, declStart, declEnd, bodyStart, bodyEnd, declaredAnnotations);
     this.enclosingType = enclosingType;
-    enclosingType.addConstructor(this);
+    enclosingType.addConstructor(this); 
   }
 
   public JClassType getEnclosingType() {
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JField.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JField.java
index a594477..d8d920e 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JField.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JField.java
@@ -15,10 +15,15 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
 /**
  * Represents a field declaration.
  */
-public class JField implements HasMetaData {
+public class JField implements HasAnnotations, HasMetaData {
+
+  private final Annotations annotations = new Annotations();
 
   private final JClassType enclosingType;
 
@@ -31,11 +36,17 @@
   private JType type;
 
   public JField(JClassType enclosingType, String name) {
+    this(enclosingType, name, null);
+  }
+
+  public JField(JClassType enclosingType, String name,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
     this.enclosingType = enclosingType;
     this.name = name;
 
     assert (enclosingType != null);
     enclosingType.addField(this);
+    annotations.addAnnotations(declaredAnnotations);
   }
 
   public void addMetaData(String tagName, String[] values) {
@@ -46,6 +57,18 @@
     this.modifierBits |= modifierBits;
   }
 
+  public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+    return annotations.getAnnotation(annotationClass);
+  }
+
+  public Annotation[] getAnnotations() {
+    return annotations.getAnnotations();
+  }
+
+  public Annotation[] getDeclaredAnnotations() {
+    return annotations.getDeclaredAnnotations();
+  }
+
   public JClassType getEnclosingType() {
     return enclosingType;
   }
@@ -68,6 +91,10 @@
     return type;
   }
 
+  public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
+    return annotations.isAnnotationPresent(annotationClass);
+  }
+
   public boolean isDefaultAccess() {
     return 0 == (modifierBits & (TypeOracle.MOD_PUBLIC | TypeOracle.MOD_PRIVATE | TypeOracle.MOD_PROTECTED));
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JMethod.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JMethod.java
index a51be1e..2eaeaa2 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JMethod.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JMethod.java
@@ -15,6 +15,9 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
 /**
  * Represents a method declaration.
  */
@@ -26,7 +29,13 @@
 
   public JMethod(JClassType enclosingType, String name, int declStart,
       int declEnd, int bodyStart, int bodyEnd) {
-    super(name, declStart, declEnd, bodyStart, bodyEnd);
+    this(enclosingType, name, declStart, declEnd, bodyStart, bodyEnd, null);
+  }
+
+  public JMethod(JClassType enclosingType, String name, int declStart,
+      int declEnd, int bodyStart, int bodyEnd,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+    super(name, declStart, declEnd, bodyStart, bodyEnd, declaredAnnotations);
     this.enclosingType = enclosingType;
     enclosingType.addMethod(this);
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JPackage.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JPackage.java
index 30d06ea..f3c6592 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JPackage.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JPackage.java
@@ -15,22 +15,30 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import java.lang.annotation.Annotation;
 import java.util.HashMap;
 import java.util.Map;
 
 /**
  * Represents a logical package.
  */
-public class JPackage {
+public class JPackage implements HasAnnotations {
 
   private final String name;
 
+  private final Annotations annotations = new Annotations();
+
   private final Map<String, JClassType> types = new HashMap<String, JClassType>();
 
   JPackage(String name) {
     this.name = name;
   }
 
+  public void addAnnotations(
+      Map<Class<? extends Annotation>, Annotation> annotations) {
+    this.annotations.addAnnotations(annotations);
+  }
+
   public JClassType findType(String typeName) {
     String[] parts = typeName.split("\\.");
     return findType(parts);
@@ -40,6 +48,18 @@
     return findTypeImpl(typeName, 0);
   }
 
+  public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+    return annotations.getAnnotation(annotationClass);
+  }
+
+  public Annotation[] getAnnotations() {
+    return annotations.getAnnotations();
+  }
+
+  public Annotation[] getDeclaredAnnotations() {
+    return annotations.getDeclaredAnnotations();
+  }
+
   public String getName() {
     return name;
   }
@@ -56,6 +76,10 @@
     return types.values().toArray(TypeOracle.NO_JCLASSES);
   }
 
+  public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
+    return annotations.isAnnotationPresent(annotationClass);
+  }
+
   public boolean isDefault() {
     return "".equals(name);
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
index c1a897b..07aaaf1 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,10 +15,15 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
 /**
  * Represents a parameter in a declaration.
  */
-public class JParameter implements HasMetaData {
+public class JParameter implements HasAnnotations, HasMetaData {
+
+  private final Annotations annotations = new Annotations();
 
   private final HasMetaData metaData = new MetaData();
 
@@ -29,17 +34,36 @@
   private final JAbstractMethod enclosingMethod;
 
   public JParameter(JAbstractMethod enclosingMethod, JType type, String name) {
+    this(enclosingMethod, type, name, null);
+  }
+
+  public JParameter(JAbstractMethod enclosingMethod, JType type, String name,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
     this.enclosingMethod = enclosingMethod;
     this.type = type;
     this.name = name;
 
     enclosingMethod.addParameter(this);
+
+    annotations.addAnnotations(declaredAnnotations);
   }
 
   public void addMetaData(String tagName, String[] values) {
     metaData.addMetaData(tagName, values);
   }
 
+  public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+    return annotations.getAnnotation(annotationClass);
+  }
+
+  public Annotation[] getAnnotations() {
+    return annotations.getAnnotations();
+  }
+
+  public Annotation[] getDeclaredAnnotations() {
+    return annotations.getDeclaredAnnotations();
+  }
+
   public JAbstractMethod getEnclosingMethod() {
     return enclosingMethod;
   }
@@ -60,6 +84,10 @@
     return type;
   }
 
+  public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
+    return annotations.isAnnotationPresent(annotationClass);
+  }
+
   public String toString() {
     StringBuffer sb = new StringBuffer();
     sb.append(type.getQualifiedSourceName());
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java
index fd7806a..24ab78b 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java
@@ -33,6 +33,17 @@
 
   public abstract String getSimpleSourceName();
 
+  /**
+   * Returns this instance if it is a annotation or <code>null</code> if it is
+   * not.
+   * 
+   * @return this instance if it is a annotation or <code>null</code> if it is
+   *         not
+   */
+  public JAnnotationType isAnnotation() {
+    return null;
+  }
+
   public abstract JArrayType isArray();
 
   public abstract JClassType isClass();
diff --git a/dev/core/src/com/google/gwt/dev/jdt/AnnotationProxyFactory.java b/dev/core/src/com/google/gwt/dev/jdt/AnnotationProxyFactory.java
new file mode 100644
index 0000000..79b2f4e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jdt/AnnotationProxyFactory.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jdt;
+
+import com.google.gwt.core.ext.typeinfo.JAnnotationMethod;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JType;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Creates proxies for annotation objects that...
+ */
+class AnnotationProxyFactory {
+  /**
+   * {@link InvocationHandler} implementation used by all
+   * {@link java.lang.annotation.Annotation Annotation} proxies created by the
+   * {@link TypeOracle}.
+   */
+  private static class AnnotationProxyInvocationHandler implements
+      InvocationHandler {
+    /**
+     * The resolved class of this annotation.
+     */
+    private Class<? extends Annotation> annotationClass;
+
+    private final JClassType annotationType;
+
+    /**
+     * Maps method names onto values. Note that methods on annotation types
+     * cannot be overloaded because they have zero arguments.
+     */
+    private final Map<String, Object> identifierToValue;
+
+    /**
+     * A reference to the enclosing proxy object.
+     */
+    private Annotation proxy;
+
+    public AnnotationProxyInvocationHandler(JClassType annotationType,
+        Map<String, Object> identifierToValue,
+        Class<? extends Annotation> annotationClass) {
+      this.annotationType = annotationType;
+      this.identifierToValue = identifierToValue;
+      this.annotationClass = annotationClass;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (proxy == other) {
+        return true;
+      }
+      if (!annotationClass.isInstance(other)) {
+        return false;
+      }
+      try {
+        for (Method method : annotationClass.getDeclaredMethods()) {
+          Object myVal = method.invoke(proxy);
+          Object otherVal = method.invoke(other);
+
+          if (myVal instanceof Object[]) {
+            if (!Arrays.equals((Object[]) myVal, (Object[]) otherVal)) {
+              return false;
+            }
+          } else if (myVal instanceof boolean[]) {
+            if (!Arrays.equals((boolean[]) myVal, (boolean[]) otherVal)) {
+              return false;
+            }
+          } else if (myVal instanceof byte[]) {
+            if (!Arrays.equals((byte[]) myVal, (byte[]) otherVal)) {
+              return false;
+            }
+          } else if (myVal instanceof char[]) {
+            if (!Arrays.equals((char[]) myVal, (char[]) otherVal)) {
+              return false;
+            }
+          } else if (myVal instanceof short[]) {
+            if (!Arrays.equals((short[]) myVal, (short[]) otherVal)) {
+              return false;
+            }
+          } else if (myVal instanceof int[]) {
+            if (!Arrays.equals((int[]) myVal, (int[]) otherVal)) {
+              return false;
+            }
+          } else if (myVal instanceof long[]) {
+            if (!Arrays.equals((long[]) myVal, (long[]) otherVal)) {
+              return false;
+            }
+          } else if (myVal instanceof float[]) {
+            if (!Arrays.equals((float[]) myVal, (float[]) otherVal)) {
+              return false;
+            }
+          } else if (myVal instanceof double[]) {
+            if (!Arrays.equals((double[]) myVal, (double[]) otherVal)) {
+              return false;
+            }
+          } else {
+            if (!myVal.equals(otherVal)) {
+              return false;
+            }
+          }
+        }
+      } catch (IllegalArgumentException e) {
+        throw new RuntimeException(e);
+      } catch (IllegalAccessException e) {
+        throw new RuntimeException(e);
+      } catch (InvocationTargetException e) {
+        throw new RuntimeException(e.getTargetException());
+      }
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      int sum = 0;
+      try {
+        for (Method method : annotationClass.getDeclaredMethods()) {
+          Object myVal = method.invoke(proxy);
+          int memberHash;
+          if (myVal instanceof Object[]) {
+            memberHash = Arrays.hashCode((Object[]) myVal);
+          } else if (myVal instanceof boolean[]) {
+            memberHash = Arrays.hashCode((boolean[]) myVal);
+          } else if (myVal instanceof byte[]) {
+            memberHash = Arrays.hashCode((byte[]) myVal);
+          } else if (myVal instanceof char[]) {
+            memberHash = Arrays.hashCode((char[]) myVal);
+          } else if (myVal instanceof short[]) {
+            memberHash = Arrays.hashCode((short[]) myVal);
+          } else if (myVal instanceof int[]) {
+            memberHash = Arrays.hashCode((int[]) myVal);
+          } else if (myVal instanceof long[]) {
+            memberHash = Arrays.hashCode((long[]) myVal);
+          } else if (myVal instanceof float[]) {
+            memberHash = Arrays.hashCode((float[]) myVal);
+          } else if (myVal instanceof double[]) {
+            memberHash = Arrays.hashCode((double[]) myVal);
+          } else {
+            memberHash = myVal.hashCode();
+          }
+          // See doc for Annotation.hashCode.
+          memberHash ^= 127 * method.getName().hashCode();
+          sum += memberHash;
+        }
+      } catch (IllegalArgumentException e) {
+        throw new RuntimeException(e);
+      } catch (IllegalAccessException e) {
+        throw new RuntimeException(e);
+      } catch (InvocationTargetException e) {
+        throw new RuntimeException(e.getTargetException());
+      }
+      return sum;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
+     *      java.lang.reflect.Method, java.lang.Object[])
+     */
+    public Object invoke(Object proxy, Method method, Object[] args)
+        throws Throwable {
+
+      String name = method.getName();
+
+      // See if the value was explicitly declared
+      Object value = identifierToValue.get(name);
+      if (value != null) {
+        return value;
+      }
+
+      // Try to find a method on the interface.
+      JMethod jMethod = annotationType.findMethod(name, new JType[0]);
+      if (jMethod != null) {
+        JAnnotationMethod annotationMethod = jMethod.isAnnotationMethod();
+        assert (annotationMethod != null);
+        return annotationMethod.getDefaultValue();
+      }
+
+      /*
+       * Maybe it's an Object method, just delegate to myself.
+       */
+      return method.invoke(this, args);
+    }
+
+    public void setProxy(Annotation proxy) {
+      this.proxy = proxy;
+    }
+
+    @Override
+    public String toString() {
+      final StringBuilder msg = new StringBuilder();
+      msg.append('@').append(annotationType.getQualifiedSourceName()).append(
+          '(');
+      boolean first = true;
+      try {
+        for (Method method : annotationClass.getDeclaredMethods()) {
+          if (first) {
+            first = false;
+          } else {
+            msg.append(", ");
+          }
+          msg.append(method.getName()).append('=');
+          Object myVal = method.invoke(proxy);
+          if (myVal.getClass().isArray()) {
+            msg.append(java.util.Arrays.deepToString((Object[]) myVal));
+          } else {
+            msg.append(myVal);
+          }
+        }
+      } catch (IllegalArgumentException e) {
+        throw new RuntimeException(e);
+      } catch (IllegalAccessException e) {
+        throw new RuntimeException(e);
+      } catch (InvocationTargetException e) {
+        throw new RuntimeException(e.getTargetException());
+      }
+      msg.append(')');
+      return msg.toString();
+    }
+  }
+
+  public static Annotation create(Class<? extends Annotation> annotationClass,
+      JClassType annotationType, Map<String, Object> identifierToValue) {
+    AnnotationProxyInvocationHandler annotationInvocationHandler = new AnnotationProxyInvocationHandler(
+        annotationType, identifierToValue, annotationClass);
+    Annotation proxy = (Annotation) Proxy.newProxyInstance(
+        AnnotationProxyFactory.class.getClassLoader(), new Class<?>[] {
+            java.lang.annotation.Annotation.class, annotationClass},
+        annotationInvocationHandler);
+    annotationInvocationHandler.setProxy(proxy);
+    return proxy;
+  }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jdt/ICompilationUnitAdapter.java b/dev/core/src/com/google/gwt/dev/jdt/ICompilationUnitAdapter.java
index bf0ba50..5121882 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/ICompilationUnitAdapter.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/ICompilationUnitAdapter.java
@@ -21,6 +21,8 @@
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
 
+import java.io.File;
+
 /**
  * Implements <code>ICompilationUnit</code> in terms of a
  * {@link CompilationUnitProvider}.
@@ -51,6 +53,20 @@
   }
 
   public char[] getMainTypeName() {
+    String mainTypeName = cup.getLocation();
+    int nameStart = mainTypeName.lastIndexOf(File.separatorChar);
+    if (nameStart != -1) {
+      mainTypeName = mainTypeName.substring(nameStart + 1);
+    }
+
+    /*
+     * This is required to resolve the package-info class.
+     */
+    int ext = mainTypeName.lastIndexOf(".java");
+    if (ext != -1) {
+      return mainTypeName.substring(0, ext).toCharArray();
+    }
+
     // seems to work just returning null
     return null;
   }
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 b1d2d6e..9c80e86 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
@@ -20,12 +20,16 @@
 import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider;
 import com.google.gwt.core.ext.typeinfo.HasMetaData;
 import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
+import com.google.gwt.core.ext.typeinfo.JAnnotationMethod;
+import com.google.gwt.core.ext.typeinfo.JAnnotationType;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JConstructor;
 import com.google.gwt.core.ext.typeinfo.JField;
 import com.google.gwt.core.ext.typeinfo.JMethod;
 import com.google.gwt.core.ext.typeinfo.JPackage;
 import com.google.gwt.core.ext.typeinfo.JParameter;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
 import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
@@ -37,13 +41,31 @@
 import org.eclipse.jdt.internal.compiler.ASTVisitor;
 import org.eclipse.jdt.internal.compiler.CompilationResult;
 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+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.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.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.NumberLiteral;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
+import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
+import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.TypeReference;
 import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
@@ -54,6 +76,7 @@
 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.ParameterizedTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
@@ -67,6 +90,7 @@
 import java.io.CharArrayReader;
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -97,6 +121,40 @@
 
   private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s");
 
+  public static String computeBinaryClassName(JType type) {
+    JPrimitiveType primitiveType = type.isPrimitive();
+    if (primitiveType != null) {
+      return primitiveType.getJNISignature();
+    }
+
+    JArrayType arrayType = type.isArray();
+    if (arrayType != null) {
+      JType component = arrayType.getComponentType();
+      if (component.isClassOrInterface() != null) {
+        return "[L" + computeBinaryClassName(arrayType.getComponentType())
+            + ";";
+      } else {
+        return "[" + computeBinaryClassName(arrayType.getComponentType());
+      }
+    }
+
+    JParameterizedType parameterizedType = type.isParameterized();
+    if (parameterizedType != null) {
+      return computeBinaryClassName(parameterizedType.getRawType());
+    }
+
+    JClassType classType = type.isClassOrInterface();
+    assert (classType != null);
+
+    JClassType enclosingType = classType.getEnclosingType();
+    if (enclosingType != null) {
+      return computeBinaryClassName(enclosingType) + "$"
+          + classType.getSimpleSourceName();
+    }
+
+    return classType.getQualifiedSourceName();
+  }
+
   static boolean parseMetaDataTags(char[] unitSource, HasMetaData hasMetaData,
       Javadoc javadoc) {
 
@@ -187,6 +245,38 @@
     tagValues.clear();
   }
 
+  private static String getMethodName(JClassType enclosingType,
+      AbstractMethodDeclaration jmethod) {
+    if (jmethod.isConstructor()) {
+      return String.valueOf(enclosingType.getSimpleSourceName());
+    } else {
+      return String.valueOf(jmethod.binding.selector);
+    }
+  }
+
+  private static boolean isAnnotation(TypeDeclaration typeDecl) {
+    if (typeDecl.kind() == IGenericType.ANNOTATION_TYPE_DECL) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * Returns <code>true</code> if this name is the special package-info type
+   * name.
+   * 
+   * @return <code>true</code> if this name is the special package-info type
+   *         name
+   */
+  private static boolean isPackageInfoTypeName(String qname) {
+    return "package-info".equals(qname);
+  }
+
+  private static HashMap<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> newAnnotationMap() {
+    return new HashMap<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation>();
+  }
+
   private static void removeInfectedUnits(final TreeLogger logger,
       final Map<String, CompilationUnitDeclaration> cudsByFileName) {
 
@@ -468,6 +558,165 @@
     return oracle;
   }
 
+  private Object evaluateAnnotationExpression(TreeLogger logger,
+      Expression expression) {
+    Annotation annotation = (Annotation) expression;
+
+    // Determine the annotation class
+    TypeBinding resolvedType = annotation.resolvedType;
+    Class<? extends java.lang.annotation.Annotation> clazz = (Class<? extends java.lang.annotation.Annotation>) getClassLiteral(
+        logger, resolvedType);
+    if (clazz == null) {
+      return null;
+    }
+
+    // Build the map of identifiers to values.
+    Map<String, Object> identifierToValue = new HashMap<String, Object>();
+    for (MemberValuePair mvp : annotation.memberValuePairs()) {
+      // Method name
+      String identifier = String.valueOf(mvp.name);
+
+      // Value
+      Expression expressionValue = mvp.value;
+      Object value = evaluateExpression(logger, expressionValue);
+      if (value == null) {
+        return null;
+      }
+
+      identifierToValue.put(identifier, value);
+    }
+
+    // Create the Annotation proxy
+    JClassType annotationType = (JClassType) resolveType(logger, resolvedType);
+    if (annotationType == null) {
+      return null;
+    }
+
+    return AnnotationProxyFactory.create(clazz, annotationType,
+        identifierToValue);
+  }
+
+  private Object evaluateArrayInitializerExpression(TreeLogger logger,
+      Expression expression) {
+    ArrayInitializer arrayInitializer = (ArrayInitializer) expression;
+    Class<?> leafComponentClass = getClassLiteral(logger,
+        arrayInitializer.binding.leafComponentType);
+    int[] dimensions = new int[arrayInitializer.binding.dimensions];
+
+    Expression[] initExpressions = arrayInitializer.expressions;
+    if (initExpressions != null) {
+      dimensions[0] = initExpressions.length;
+    }
+
+    Object array = Array.newInstance(leafComponentClass, dimensions);
+    boolean failed = false;
+    if (initExpressions != null) {
+      for (int i = 0; i < initExpressions.length; ++i) {
+        Expression arrayInitExp = initExpressions[i];
+        Object value = evaluateExpression(logger, arrayInitExp);
+        if (value != null) {
+          Array.set(array, i, value);
+        } else {
+          failed = true;
+          break;
+        }
+      }
+    }
+
+    if (!failed) {
+      return array;
+    }
+
+    return null;
+  }
+
+  private Object evaluateExpression(TreeLogger logger, Expression expression) {
+    if (expression instanceof MagicLiteral) {
+      if (expression instanceof FalseLiteral) {
+        return Boolean.FALSE;
+      } else if (expression instanceof NullLiteral) {
+        // null is not a valid annotation value; JLS Third Ed. Section 9.7
+      } else if (expression instanceof TrueLiteral) {
+        return Boolean.TRUE;
+      }
+    } else if (expression instanceof NumberLiteral) {
+      Object value = evaluateNumericExpression(expression);
+      if (value != null) {
+        return value;
+      }
+    } else if (expression instanceof StringLiteral) {
+      StringLiteral stringLiteral = (StringLiteral) expression;
+      return stringLiteral.constant.stringValue();
+    } else if (expression instanceof ClassLiteralAccess) {
+      ClassLiteralAccess classLiteral = (ClassLiteralAccess) expression;
+      Class<?> clazz = getClassLiteral(logger, classLiteral.resolvedType);
+      if (clazz != null) {
+        return clazz;
+      }
+    } else if (expression instanceof ArrayInitializer) {
+      Object value = evaluateArrayInitializerExpression(logger, 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 Annotation) {
+      Object annotationInstance = evaluateAnnotationExpression(logger,
+          expression);
+      if (annotationInstance != null) {
+        return annotationInstance;
+      }
+    }
+
+    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());
+    }
+
+    return null;
+  }
+
+  private Class<?> getClassLiteral(TreeLogger logger, TypeBinding resolvedType) {
+    JClassType annotationType = (JClassType) resolveType(logger, resolvedType);
+    if (annotationType == null) {
+      return null;
+    }
+
+    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;
+    }
+  }
+
   private CompilationUnitProvider getCup(TypeDeclaration typeDecl) {
     ICompilationUnit icu = typeDecl.compilationResult.compilationUnit;
     ICompilationUnitAdapter icua = (ICompilationUnitAdapter) icu;
@@ -539,6 +788,7 @@
     String jpkgName = getPackage(typeDecl);
     JPackage pkg = oracle.getOrCreatePackage(jpkgName);
     final boolean jclassIsIntf = isInterface(typeDecl);
+    boolean jclassIsAnnonation = isAnnotation(typeDecl);
     CompilationUnitProvider cup = getCup(typeDecl);
 
     int declStart = typeDecl.declarationSourceStart;
@@ -546,13 +796,52 @@
     int bodyStart = typeDecl.bodyStart;
     int bodyEnd = typeDecl.bodyEnd;
 
-    JClassType type = new JClassType(oracle, cup, pkg, enclosingType,
+    JClassType type;
+    if (jclassIsAnnonation) {
+      type = new JAnnotationType(oracle, cup, pkg, enclosingType,
+          isLocalType, jclassName, declStart, declEnd, bodyStart, bodyEnd,
+          jclassIsIntf);
+    } else {
+    type = new JClassType(oracle, cup, pkg, enclosingType,
         isLocalType, jclassName, declStart, declEnd, bodyStart, bodyEnd,
         jclassIsIntf);
+    }
 
     cacheManager.setTypeForBinding(binding, type);
   }
 
+  private boolean resolveAnnotation(
+      TreeLogger logger,
+      Annotation jannotation,
+      Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations) {
+    // Determine the annotation class
+    TypeBinding resolvedType = jannotation.resolvedType;
+    Class<? extends java.lang.annotation.Annotation> clazz = (Class<? extends java.lang.annotation.Annotation>) getClassLiteral(
+        logger, resolvedType);
+    if (clazz == null) {
+      return false;
+    }
+
+    java.lang.annotation.Annotation annotation = (java.lang.annotation.Annotation) evaluateExpression(
+        logger, jannotation);
+    declaredAnnotations.put(clazz, annotation);
+    return (annotation != null) ? true : false;
+  }
+
+  private boolean resolveAnnotations(
+      TreeLogger logger,
+      Annotation[] annotations,
+      Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations) {
+    if (annotations != null) {
+      for (Annotation annotation : annotations) {
+        if (!resolveAnnotation(logger, annotation, declaredAnnotations)) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
   private boolean resolveField(TreeLogger logger, char[] unitSource,
       JClassType enclosingType, FieldDeclaration jfield) {
 
@@ -562,8 +851,16 @@
       return true;
     }
 
+    // Resolve annotations
+    Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
+    if (!resolveAnnotations(logger, jfield.annotations, declaredAnnotations)) {
+      // Failed to resolve.
+      //
+      return false;
+    }
+
     String name = String.valueOf(jfield.name);
-    JField field = new JField(enclosingType, name);
+    JField field = new JField(enclosingType, name, declaredAnnotations);
 
     // Get modifiers.
     //
@@ -607,7 +904,6 @@
 
   private boolean resolveMethod(TreeLogger logger, char[] unitSource,
       JClassType enclosingType, AbstractMethodDeclaration jmethod) {
-    JAbstractMethod method;
 
     if (jmethod instanceof Clinit) {
       // Pretend we didn't see this.
@@ -615,23 +911,40 @@
       return true;
     }
 
-    String name = null;
     int declStart = jmethod.declarationSourceStart;
     int declEnd = jmethod.declarationSourceEnd;
     int bodyStart = jmethod.bodyStart;
     int bodyEnd = jmethod.bodyEnd;
+    String name = getMethodName(enclosingType, jmethod);
 
-    if (jmethod.isConstructor()) {
-      name = String.valueOf(enclosingType.getSimpleSourceName());
-      method = new JConstructor(enclosingType, name, declStart, declEnd,
-          bodyStart, bodyEnd);
-    } else {
-      name = String.valueOf(jmethod.binding.selector);
-      method = new JMethod(enclosingType, name, declStart, declEnd, bodyStart,
-          bodyEnd);
-
-      // Set the return type.
+    // Resolve annotations
+    Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
+    if (!resolveAnnotations(logger, jmethod.annotations, declaredAnnotations)) {
+      // Failed to resolve.
       //
+      return false;
+    }
+
+    JAbstractMethod method;
+    if (jmethod.isConstructor()) {
+      method = new JConstructor(enclosingType, name, declStart, declEnd,
+          bodyStart, bodyEnd, declaredAnnotations);
+    } else {
+      if (jmethod.isAnnotationMethod()) {
+        AnnotationMethodDeclaration annotationMethod = (AnnotationMethodDeclaration) jmethod;
+        Object defaultValue = null;
+        if (annotationMethod.defaultValue != null) {
+          defaultValue = evaluateExpression(logger,
+              annotationMethod.defaultValue);
+        }
+        method = new JAnnotationMethod(enclosingType, name, declStart, declEnd,
+            bodyStart, bodyEnd, defaultValue, declaredAnnotations);
+      } else {
+        method = new JMethod(enclosingType, name, declStart, declEnd,
+            bodyStart, bodyEnd, declaredAnnotations);
+      }
+
+      // Add the return type if necessary.
       TypeBinding jreturnType = ((MethodDeclaration) jmethod).returnType.resolvedType;
       JType returnType = resolveType(logger, jreturnType);
       if (returnType == null) {
@@ -689,6 +1002,32 @@
     return true;
   }
 
+  private boolean resolvePackage(TreeLogger logger,
+      TypeDeclaration jclass) {
+    SourceTypeBinding binding = jclass.binding;
+
+    TypeOracle oracle = cacheManager.getTypeOracle();
+    String packageName = String.valueOf(binding.fPackage.readableName());
+    JPackage pkg = oracle.getOrCreatePackage(packageName);
+    assert (pkg != null);
+
+    CompilationUnitScope cus = (CompilationUnitScope) jclass.scope.parent;
+    assert (cus != null);
+
+    // Resolve annotations
+    Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
+    if (!resolveAnnotations(logger,
+        cus.referenceContext.currentPackage.annotations, declaredAnnotations)) {
+      // Failed to resolve.
+      //
+      return false;
+    }
+
+    pkg.addAnnotations(declaredAnnotations);
+
+    return true;
+  }
+
   private boolean resolveParameter(TreeLogger logger, JAbstractMethod method,
       Argument jparam) {
     TypeBinding jtype = jparam.binding.type;
@@ -699,8 +1038,17 @@
       return false;
     }
 
+    // Resolve annotations
+    Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
+    if (!resolveAnnotations(logger, jparam.annotations, declaredAnnotations)) {
+      // Failed to resolve.
+      //
+      return false;
+    }
+
     String name = String.valueOf(jparam.name);
-    new JParameter(method, type, name);
+    new JParameter(method, type, name, declaredAnnotations);
+
     return true;
   }
 
@@ -846,7 +1194,8 @@
     }
 
     String name = String.valueOf(binding.readableName());
-    logger.log(TreeLogger.WARN, "Unable to resolve type: " + name, null);
+    logger.log(TreeLogger.WARN, "Unable to resolve type: " + name
+        + " binding: " + binding.getClass().getCanonicalName(), null);
     return null;
   }
 
@@ -864,6 +1213,12 @@
     String qname = String.valueOf(binding.qualifiedSourceName());
     logger.log(TreeLogger.SPAM, "Found type '" + qname + "'", null);
 
+    // Handle package-info classes.
+    if (isPackageInfoTypeName(qname)) {
+      return resolvePackage(logger, jclass);
+    }
+
+    // Just resolve the type.
     JClassType type = (JClassType) resolveType(logger, binding);
     if (type == null) {
       // Failed to resolve.
@@ -875,6 +1230,15 @@
     //
     type.addModifierBits(Shared.bindingToModifierBits(jclass.binding));
 
+    // Resolve annotations
+    Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
+    if (!resolveAnnotations(logger, jclass.annotations, declaredAnnotations)) {
+      // Failed to resolve.
+      //
+      return false;
+    }
+    type.addAnnotations(declaredAnnotations);
+
     // Resolve superclass (for classes only).
     //
     if (type.isInterface() == null) {
@@ -926,5 +1290,4 @@
 
     return true;
   }
-
 }
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/AnnotationsTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/AnnotationsTest.java
new file mode 100644
index 0000000..14669e4
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/AnnotationsTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Test cases for the {@link Annotations} class.
+ */
+public class AnnotationsTest extends TestCase {
+
+  @TestAnnotation1("1")
+  private static class AnnotatedClass1 {
+  }
+
+  @TestAnnotation2("2")
+  private static class AnnotatedClass2 extends AnnotatedClass1 {
+  }
+
+  @Inherited
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target(ElementType.TYPE)
+  private @interface TestAnnotation1 {
+    String value();
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target(ElementType.TYPE)
+  private @interface TestAnnotation2 {
+    String value();
+  }
+
+  
+  @Inherited
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target(ElementType.TYPE)
+  private @interface UnusedAnnotation {
+    String value();
+  }
+
+  /**
+   * Initializes an {@link Annotations} instance from a given {@link Class}.
+   */
+  private static Annotations initializeAnnotationsFromClass(
+      Class<?> annotatedClass, Annotations parent) {
+    Annotation[] jAnnotations = annotatedClass.getDeclaredAnnotations();
+    Annotations annotations = new Annotations();
+    for (Annotation annotation : jAnnotations) {
+      annotations.addAnnotation(annotation.annotationType(), annotation);
+    }
+
+    if (parent != null) {
+      annotations.setParent(parent);
+    }
+
+    return annotations;
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.Annotations#addAnnotations(java.util.Map)}.
+   */
+  public void testAddAnnotations() {
+    Annotations annotations = new Annotations();
+    Map<Class<? extends Annotation>, Annotation> entries = new HashMap<Class<? extends Annotation>, Annotation>();
+    entries.put(TestAnnotation1.class, AnnotatedClass1.class.getAnnotation(TestAnnotation1.class));
+    annotations.addAnnotations(entries);
+    assertNotNull(annotations.getAnnotation(TestAnnotation1.class));
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.Annotations#getAnnotation(java.lang.Class)}.
+   * 
+   * case 1: annotation is a declared case 2: annotation is inherited case 3:
+   * annotation is not found
+   */
+  public void testGetAnnotationDeclared() {
+    Annotations annotations = initializeAnnotationsFromClass(
+        AnnotatedClass1.class, null);
+    assertNotNull(annotations.getAnnotation(TestAnnotation1.class));
+    assertNull(annotations.getAnnotation(UnusedAnnotation.class));
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.Annotations#getAnnotation(java.lang.Class)}.
+   * 
+   * case 1: annotation is a declared case 2: annotation is inherited case 3:
+   * annotation is not found
+   */
+  public void testGetAnnotationInherited() {
+    Annotations annotations1 = initializeAnnotationsFromClass(
+        AnnotatedClass1.class, null);
+    Annotations annotations2 = initializeAnnotationsFromClass(
+        AnnotatedClass2.class, annotations1);
+
+    assertNotNull(annotations2.getAnnotation(TestAnnotation1.class));
+    assertNull(annotations2.getAnnotation(UnusedAnnotation.class));
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.Annotations#getAnnotations()}.
+   */
+  public void testGetAnnotations() {
+    Annotations annotations1 = initializeAnnotationsFromClass(
+        AnnotatedClass1.class, null);
+    Annotations annotations2 = initializeAnnotationsFromClass(
+        AnnotatedClass2.class, annotations1);
+
+    assertEquals(2, annotations2.getAnnotations().length);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.Annotations#getDeclaredAnnotations()}.
+   */
+  public void testGetDeclaredAnnotations() {
+    Annotations annotations1 = initializeAnnotationsFromClass(
+        AnnotatedClass1.class, null);
+    Annotations annotations2 = initializeAnnotationsFromClass(
+        AnnotatedClass2.class, annotations1);
+
+    assertEquals(1, annotations2.getDeclaredAnnotations().length);
+    assertEquals(1, annotations1.getDeclaredAnnotations().length);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.Annotations#isAnnotationPresent(java.lang.Class)}.
+   */
+  public void testIsAnnotationPresent() {
+    Annotations annotations1 = initializeAnnotationsFromClass(
+        AnnotatedClass1.class, null);
+    Annotations annotations2 = initializeAnnotationsFromClass(
+        AnnotatedClass2.class, annotations1);
+
+    assertTrue(annotations2.isAnnotationPresent(TestAnnotation1.class));
+    assertTrue(annotations2.isAnnotationPresent(TestAnnotation2.class));
+    assertFalse(annotations2.isAnnotationPresent(UnusedAnnotation.class));
+  }
+}
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
new file mode 100644
index 0000000..9400983
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.test.AnnotatedClass;
+import com.google.gwt.core.ext.typeinfo.test.TestAnnotation;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * Test cases for the {@link TypeOracle}'s {@link Annotation} support.
+ * 
+ * Array annotations Enum annotations String from field annotations
+ */
+public class TypeOracleAnnotationSupportTest extends TestCase {
+  static {
+    ModuleDefLoader.setEnableCachingModules(true);
+  }
+
+  private static void validateAnnotation(HasAnnotations annotatedElement,
+      String testAnnotationValue, String nestedAnnotationValue,
+      TestAnnotation realAnnotation) {
+    assertNotNull(annotatedElement);
+
+    TestAnnotation testAnnotation = annotatedElement.getAnnotation(TestAnnotation.class);
+    assertNotNull(testAnnotation);
+
+    // Check our proxy objects against the real thing.
+    assertEquals(realAnnotation, testAnnotation);
+    assertEquals(realAnnotation.hashCode(), testAnnotation.hashCode());
+
+    // tobyr doesn't like this.
+    // assertEquals(realAnnotation.toString(), testAnnotation.toString());
+
+    // checks default value
+    assertEquals(testAnnotationValue, testAnnotation.value());
+    assertEquals(nestedAnnotationValue,
+        testAnnotation.nestedAnnotation().value());
+  }
+
+  private final TreeLogger logger = TreeLogger.NULL;
+  private ModuleDef moduleDef;
+
+  private final TypeOracle typeOracle;
+
+  public TypeOracleAnnotationSupportTest() throws UnableToCompleteException {
+    moduleDef = ModuleDefLoader.loadFromClassPath(logger,
+        "com.google.gwt.core.ext.typeinfo.TypeOracleTest");
+    typeOracle = moduleDef.getTypeOracle(logger);
+  }
+
+  /**
+   * Test that a class can be annotated.
+   */
+  public void testAnnotatedClass() throws NotFoundException {
+    JClassType annotatedClass = typeOracle.getType(AnnotatedClass.class.getName());
+
+    TestAnnotation realAnnotation = AnnotatedClass.class.getAnnotation(TestAnnotation.class);
+    validateAnnotation(annotatedClass, "Class", "Foo", realAnnotation);
+
+    assertEquals(1, annotatedClass.getAnnotations().length);
+  }
+
+  /**
+   * Test that a constructor can be annotated.
+   */
+  public void testAnnotatedConstructor() throws NotFoundException,
+      SecurityException, NoSuchMethodException {
+    JClassType annotatedClass = typeOracle.getType(AnnotatedClass.class.getName());
+    JConstructor ctor = annotatedClass.getConstructor(new JType[0]);
+
+    Constructor<AnnotatedClass> constructor = AnnotatedClass.class.getConstructor();
+    TestAnnotation realAnnotation = constructor.getAnnotation(TestAnnotation.class);
+
+    validateAnnotation(ctor, "Constructor", "Not assigned", realAnnotation);
+  }
+
+  /**
+   * Test that a field can be annotated.
+   */
+  public void testAnnotatedField() throws NotFoundException, SecurityException,
+      NoSuchFieldException {
+    JClassType annotatedClass = typeOracle.getType(AnnotatedClass.class.getName());
+    JField annotatedField = annotatedClass.getField("annotatedField");
+
+    Field field = AnnotatedClass.class.getDeclaredField("annotatedField");
+    TestAnnotation realAnnotation = field.getAnnotation(TestAnnotation.class);
+
+    validateAnnotation(annotatedField, "Field", "Not assigned", realAnnotation);
+  }
+
+  /**
+   * Tests that methods can be annotated.
+   */
+  public void testAnnotatedMethod() throws NotFoundException,
+      SecurityException, NoSuchMethodException {
+    JClassType annotatedClass = typeOracle.getType(AnnotatedClass.class.getName());
+    JMethod annotatedMethod = annotatedClass.getMethod("annotatedMethod",
+        new JType[0]);
+
+    Method method = AnnotatedClass.class.getDeclaredMethod("annotatedMethod");
+    TestAnnotation realAnnotation = method.getAnnotation(TestAnnotation.class);
+
+    validateAnnotation(annotatedMethod, "Method", "Not assigned",
+        realAnnotation);
+  }
+
+  /**
+   * Tests that packages can be annotated. This necessitates the existence of a
+   * package-info.java file in the package that you wish to annotate.
+   */
+  public void testAnnotatedPackage() throws NotFoundException,
+      ClassNotFoundException {
+    JPackage annotatedPackage = typeOracle.getPackage("com.google.gwt.core.ext.typeinfo.test");
+    assertNotNull(annotatedPackage);
+
+    TestAnnotation realAnnotation = Class.forName(
+        "com.google.gwt.core.ext.typeinfo.test.package-info").getAnnotation(
+        TestAnnotation.class);
+
+    validateAnnotation(annotatedPackage, "Package", "Not assigned",
+        realAnnotation);
+  }
+
+  /**
+   * Tests that parameters can be annotated.
+   */
+  public void testAnnotatedParameter() throws NotFoundException,
+      SecurityException, NoSuchMethodException {
+    JClassType annotatedClass = typeOracle.getType(AnnotatedClass.class.getName());
+    JMethod jmethod = annotatedClass.getMethod("methodWithAnnotatedParameter",
+        new JType[] {JPrimitiveType.INT});
+    JParameter parameter = jmethod.getParameters()[0];
+
+    Method method = AnnotatedClass.class.getDeclaredMethod(
+        "methodWithAnnotatedParameter", int.class);
+    Annotation[][] paramAnnotations = method.getParameterAnnotations();
+    TestAnnotation realAnnotation = (TestAnnotation) paramAnnotations[0][0];
+
+    validateAnnotation(parameter, "Parameter", "Not assigned", realAnnotation);
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleTest.gwt.xml b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleTest.gwt.xml
new file mode 100644
index 0000000..b417c6a
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleTest.gwt.xml
@@ -0,0 +1,18 @@
+<!--                                                                        -->
+<!-- Copyright 2007 Google Inc.                                             -->
+<!-- Licensed under the Apache License, Version 2.0 (the "License"); you    -->
+<!-- may not use this file except in compliance with the License. You may   -->
+<!-- may obtain a copy of the License at                                    -->
+<!--                                                                        -->
+<!-- http://www.apache.org/licenses/LICENSE-2.0                             -->
+<!--                                                                        -->
+<!-- Unless required by applicable law or agreed to in writing, software    -->
+<!-- distributed under the License is distributed on an "AS IS" BASIS,      -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        -->
+<!-- implied. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+
+<module>
+  <inherits name="com.google.gwt.core.Core"/>
+  <source path="test"/>
+</module>
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/AnnotatedAnnotation.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/AnnotatedAnnotation.java
new file mode 100644
index 0000000..77e666c
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/AnnotatedAnnotation.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo.test;
+
+/**
+ * Tests that an annotation can be annotated.
+ */
+@TestAnnotation("Annotated annotation")
+public @interface AnnotatedAnnotation {
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/AnnotatedClass.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/AnnotatedClass.java
new file mode 100644
index 0000000..e7f7022
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/AnnotatedClass.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo.test;
+
+/**
+ * Used to test that a
+ * {@link com.google.gwt.core.ext.typeinfo.TypeOracle TypeOracle} will correctly
+ * report the presence of annotations on the different annotatable elements.
+ */
+@TestAnnotation(value = "Class", nestedAnnotation = @NestedAnnotation("Foo"))
+public class AnnotatedClass {
+  @TestAnnotation("Field")
+  private int annotatedField;
+
+  @TestAnnotation("Constructor")
+  public AnnotatedClass() {
+  }
+
+  @TestAnnotation("Method")
+  public void annotatedMethod() {
+  }
+
+  public void methodWithAnnotatedParameter(@TestAnnotation("Parameter")
+  int annotatedParameter) {
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/NestedAnnotation.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/NestedAnnotation.java
new file mode 100644
index 0000000..a4ceaf4
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/NestedAnnotation.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo.test;
+
+import java.lang.annotation.Target;
+
+/**
+ * Declare an annotation that cannot be applied to anything.  This is used to
+ * test nested annotations (annotations used when applying an annotation).
+ */
+@Target({})
+public @interface NestedAnnotation {
+  String value();
+}
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
new file mode 100644
index 0000000..76fa48e
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/TestAnnotation.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo.test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This type is used to test how the
+ * {@link com.google.gwt.core.ext.typeinfo.TypeOracle TypeOracle} deals with
+ * annotations.
+ */
+//tests depend on this being available
+@Retention(RetentionPolicy.RUNTIME)
+@Target( {
+    ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD,
+    ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER,
+    ElementType.TYPE})
+public @interface TestAnnotation {
+  /**
+   * Default value for the annotation.
+   */
+  String value();
+
+  /**
+   * Tests element default values that are themselves annotations.
+   */
+  NestedAnnotation nestedAnnotation() default @NestedAnnotation("Not assigned");
+
+  int x = 0;
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/package-info.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/package-info.java
new file mode 100644
index 0000000..ce64c8d
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/**
+ * Used to test the annotation of packages. 
+ */
+@TestAnnotation("Package")
+package com.google.gwt.core.ext.typeinfo.test;
+