The attached patch finishes up the Java 1.5 support in the TypeOracle.  The patch makes the following changes:

* Implemented the functionality missing from the JDelegatingClassType subtypes.
* Enabled erasure and type parameter substitution for JRawType and JParameterizedType respectively.  This means that List<Interger> is a full blown JClassType where every reference to its type parameter has been replaced with Integer.
* JParameterizedTypes and JWildcard types now have referential stability.
* Added test cases for the new functionality.
* Added support for enumerated types
* Implemented a wildcard capture type scheme that allows us to reasonably determine the set of subtypes of a parameterized type since this is technically an unbounded set.  For example, the subtypes List<Integer> could actually be parameterized types like  MyList<? extends Serializable, Integer> and so on.
* Fixes a bug in our Annotation proxy; we failed to implement the Annotation.annotationType() method.
* Fixed a problem with annotations.  Specifically, JLS3 9.7, single element array values do not need curly braces
* Other minor issues with our JDT mapping

Issues outstanding:
* Subtype and assignability checks may still not deal with all of the corner cases.
* The TypeOracle's refresh logic needs to be update to properly flush generics and parameterized types are out of date.

Patch by: mmendez
Review by: scottb (TBR)

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1523 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/AbstractMembers.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/AbstractMembers.java
new file mode 100644
index 0000000..47fd220
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/AbstractMembers.java
@@ -0,0 +1,269 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+abstract class AbstractMembers {
+
+  protected final JClassType classType;
+  private JMethod[] cachedOverridableMethods;
+  private final Map<String, JClassType> nestedTypes = new HashMap<String, JClassType>();
+
+  public AbstractMembers(JClassType classType) {
+    this.classType = classType;
+  }
+
+  public JConstructor findConstructor(JType[] paramTypes) {
+    JConstructor[] ctors = getConstructors();
+    for (int i = 0; i < ctors.length; i++) {
+      JConstructor candidate = ctors[i];
+      if (candidate.hasParamTypes(paramTypes)) {
+        return candidate;
+      }
+    }
+    return null;
+  }
+
+  public JField findField(String name) {
+    return doGetFields().get(name);
+  }
+
+  public JMethod findMethod(String name, JType[] paramTypes) {
+    JMethod[] overloads = getOverloads(name);
+    for (int i = 0; i < overloads.length; i++) {
+      JMethod candidate = overloads[i];
+      if (candidate.hasParamTypes(paramTypes)) {
+        return candidate;
+      }
+    }
+    return null;
+  }
+
+  public JClassType findNestedType(String typeName) {
+    String[] parts = typeName.split("\\.");
+    return findNestedTypeImpl(parts, 0);
+  }
+
+  public JConstructor getConstructor(JType[] paramTypes)
+      throws NotFoundException {
+    JConstructor result = findConstructor(paramTypes);
+    if (result == null) {
+      throw new NotFoundException();
+    }
+    return result;
+  }
+
+  public JConstructor[] getConstructors() {
+    return doGetConstructors().toArray(TypeOracle.NO_JCTORS);
+  }
+
+  public JField getField(String name) {
+    JField field = findField(name);
+    assert (field != null);
+    return field;
+  }
+
+  public JField[] getFields() {
+    return doGetFields().values().toArray(TypeOracle.NO_JFIELDS);
+  }
+
+  public JMethod getMethod(String name, JType[] paramTypes)
+      throws NotFoundException {
+    JMethod result = findMethod(name, paramTypes);
+    if (result == null) {
+      throw new NotFoundException();
+    }
+    return result;
+  }
+
+  public JMethod[] getMethods() {
+    List<JMethod> resultMethods = new ArrayList<JMethod>();
+    for (List<JMethod> overloads : doGetMethods().values()) {
+      resultMethods.addAll(overloads);
+    }
+    return resultMethods.toArray(TypeOracle.NO_JMETHODS);
+  }
+
+  public JClassType getNestedType(String typeName) throws NotFoundException {
+    JClassType result = findNestedType(typeName);
+    if (result == null) {
+      throw new NotFoundException();
+    }
+    return result;
+  }
+
+  public JClassType[] getNestedTypes() {
+    return nestedTypes.values().toArray(TypeOracle.NO_JCLASSES);
+  }
+
+  public JMethod[] getOverloads(String name) {
+    List<?> resultMethods = doGetMethods().get(name);
+    if (resultMethods != null) {
+      return resultMethods.toArray(TypeOracle.NO_JMETHODS);
+    } else {
+      return TypeOracle.NO_JMETHODS;
+    }
+  }
+
+  public JMethod[] getOverridableMethods() {
+    if (cachedOverridableMethods == null) {
+      Map<String, JMethod> methodsBySignature = new TreeMap<String, JMethod>();
+      getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
+      if (classType.isClass() != null) {
+        getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
+      }
+      int size = methodsBySignature.size();
+      Collection<JMethod> leafMethods = methodsBySignature.values();
+      cachedOverridableMethods = leafMethods.toArray(new JMethod[size]);
+    }
+    return cachedOverridableMethods;
+  }
+
+  protected void addConstructor(JConstructor ctor) {
+    assert (!doGetConstructors().contains(ctor));
+    doGetConstructors().add(ctor);
+  }
+
+  protected void addField(JField field) {
+    Object existing = doGetFields().put(field.getName(), field);
+    assert (existing == null);
+  }
+
+  protected void addMethod(JMethod method) {
+    String methodName = method.getName();
+    Map<String, List<JMethod>> methods = doGetMethods();
+    List<JMethod> overloads = methods.get(methodName);
+    if (overloads == null) {
+      overloads = new ArrayList<JMethod>();
+      methods.put(methodName, overloads);
+    }
+    overloads.add(method);
+  }
+
+  protected void addNestedType(JClassType type) {
+    nestedTypes.put(type.getSimpleSourceName(), type);
+  }
+
+  protected abstract List<JConstructor> doGetConstructors();
+
+  protected abstract Map<String, JField> doGetFields();
+
+  protected abstract Map<String, List<JMethod>> doGetMethods();
+
+  protected JClassType findNestedTypeImpl(String[] typeName, int index) {
+    JClassType found = nestedTypes.get(typeName[index]);
+    if (found == null) {
+      return null;
+    } else if (index < typeName.length - 1) {
+      return found.findNestedTypeImpl(typeName, index + 1);
+    } else {
+      return found;
+    }
+  }
+
+  protected void getOverridableMethodsOnSuperclassesAndThisClass(
+      Map<String, JMethod> methodsBySignature) {
+    assert (classType.isClass() != null);
+
+    // Recurse first so that more derived methods will clobber less derived
+    // methods.
+    JClassType superClass = classType.getSuperclass();
+    if (superClass != null) {
+      superClass.getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
+    }
+
+    JMethod[] declaredMethods = getMethods();
+    for (int i = 0; i < declaredMethods.length; i++) {
+      JMethod method = declaredMethods[i];
+
+      // Ensure that this method is overridable.
+      if (method.isFinal() || method.isPrivate() || method.isStatic()) {
+        // We cannot override this method, so skip it.
+        continue;
+      }
+
+      // We can override this method, so record it.
+      String sig = computeInternalSignature(method);
+      methodsBySignature.put(sig, method);
+    }
+  }
+
+  /**
+   * Gets the methods declared in interfaces that this type extends. If this
+   * type is a class, its own methods are not added. If this type is an
+   * interface, its own methods are added. Used internally by
+   * {@link #getOverridableMethods()}.
+   * 
+   * @param methodsBySignature
+   */
+  protected void getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(
+      Map<String, JMethod> methodsBySignature) {
+    // Recurse first so that more derived methods will clobber less derived
+    // methods.
+    JClassType[] superIntfs = classType.getImplementedInterfaces();
+    for (int i = 0; i < superIntfs.length; i++) {
+      JClassType superIntf = superIntfs[i];
+      superIntf.getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
+    }
+
+    if (classType.isInterface() == null) {
+      // This is not an interface, so we're done after having visited its
+      // implemented interfaces.
+      return;
+    }
+
+    JMethod[] declaredMethods = getMethods();
+    for (int i = 0; i < declaredMethods.length; i++) {
+      JMethod method = declaredMethods[i];
+
+      String sig = computeInternalSignature(method);
+      JMethod existing = methodsBySignature.get(sig);
+      if (existing != null) {
+        JClassType existingType = existing.getEnclosingType();
+        JClassType thisType = method.getEnclosingType();
+        if (thisType.isAssignableFrom(existingType)) {
+          // The existing method is in a more-derived type, so don't replace it.
+          continue;
+        }
+      }
+      methodsBySignature.put(sig, method);
+    }
+  }
+
+  protected JClassType getParentType() {
+    return classType;
+  }
+
+  private String computeInternalSignature(JMethod method) {
+    StringBuffer sb = new StringBuffer();
+    sb.setLength(0);
+    sb.append(method.getName());
+    JParameter[] params = method.getParameters();
+    for (int j = 0; j < params.length; j++) {
+      JParameter param = params[j];
+      sb.append("/");
+      sb.append(param.getType().getErasedType().getQualifiedSourceName());
+    }
+    return sb.toString();
+  }
+
+}
\ No newline at end of file
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
index 2d7631c..e90fa74 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/Annotations.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/Annotations.java
@@ -42,6 +42,23 @@
    */
   private Annotations parent;
 
+  Annotations() {
+  }
+  
+  Annotations(Annotations otherAnnotations) {
+    if (otherAnnotations != null) {
+      Annotation[] otherDeclaredAnnotations = otherAnnotations.getDeclaredAnnotations();
+      for (Annotation otherDeclaredAnnotation : otherDeclaredAnnotations) {
+        addAnnotation(otherDeclaredAnnotation.annotationType(),
+            otherDeclaredAnnotation);
+      }
+    }
+  }
+
+  Annotations(Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+    this.declaredAnnotations.putAll(declaredAnnotations);
+  }
+
   public void addAnnotations(
       Map<Class<? extends Annotation>, Annotation> annotations) {
     if (annotations != null) {
@@ -92,11 +109,12 @@
       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)) {
+        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/DelegateMembers.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/DelegateMembers.java
new file mode 100644
index 0000000..2502013
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/DelegateMembers.java
@@ -0,0 +1,115 @@
+/*
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class that initializes the different members of a
+ * {@link JDelegatingClassType} from its corresponding base type on demand.
+ */
+class DelegateMembers extends AbstractMembers {
+  private final JClassType baseType;
+  private List<JConstructor> lazyConstructors;
+  private Map<String, JField> lazyFields;
+  private Map<String, List<JMethod>> lazyMethods;
+  private final Substitution substitution;
+
+  /**
+   */
+  public DelegateMembers(JDelegatingClassType enclosingType,
+      JClassType baseType, Substitution substitution) {
+    super(enclosingType);
+    this.baseType = baseType;
+    this.substitution = substitution;
+  }
+
+  @Override
+  protected List<JConstructor> doGetConstructors() {
+    if (lazyConstructors != null) {
+      /*
+       * Return if the constructors are being initialized or have been
+       * initialized.
+       */
+      return lazyConstructors;
+    }
+    lazyConstructors = new ArrayList<JConstructor>();
+
+    JConstructor[] baseCtors = baseType.getConstructors();
+    for (JConstructor baseCtor : baseCtors) {
+      JConstructor newCtor = new JConstructor(getParentType(), baseCtor);
+      initializeParams(baseCtor, newCtor);
+      addConstructor(newCtor);
+    }
+
+    return lazyConstructors;
+  }
+
+  @Override
+  protected Map<String, JField> doGetFields() {
+    if (lazyFields != null) {
+      /*
+       * Return if the fields are being initialized or have been
+       * initialized.
+       */
+      return lazyFields;
+    }
+    lazyFields = new HashMap<String, JField>();
+
+    JField[] baseFields = baseType.getFields();
+    for (JField baseField : baseFields) {
+      JField newField = new JField(getParentType(), baseField);
+      newField.setType(substitution.getSubstitution(baseField.getType()));
+      addField(newField);
+    }
+
+    return lazyFields;
+  }
+
+  @Override
+  protected Map<String, List<JMethod>> doGetMethods() {
+    if (lazyMethods != null) {
+      /*
+       * Return if the methods are being initialized or have been
+       * initialized.
+       */
+      return lazyMethods;
+    }
+    lazyMethods = new HashMap<String, List<JMethod>>();
+
+    JMethod[] baseMethods = baseType.getMethods();
+    for (JMethod baseMethod : baseMethods) {
+      JMethod newMethod = new JMethod(getParentType(), baseMethod);
+      initializeParams(baseMethod, newMethod);
+      newMethod.setReturnType(substitution.getSubstitution(baseMethod.getReturnType()));
+      addMethod(newMethod);
+    }
+
+    return lazyMethods;
+  }
+
+  private void initializeParams(JAbstractMethod srcMethod,
+      JAbstractMethod newMethod) {
+    for (JParameter srcParam : srcMethod.getParameters()) {
+      JParameter newParam = new JParameter(newMethod, srcParam);
+      newParam.setType(substitution.getSubstitution(srcParam.getType()));
+      newMethod.addParameter(newParam);
+    }
+  }
+}
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 be5ebca..e7f8b98 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
@@ -26,7 +26,7 @@
 public abstract class JAbstractMethod implements HasAnnotations, HasMetaData,
     HasTypeParameters {
 
-  private final Annotations annotations = new Annotations();
+  private final Annotations annotations;
 
   private int bodyEnd;
 
@@ -59,9 +59,26 @@
     this.declEnd = declEnd;
     this.bodyStart = bodyStart;
     this.bodyEnd = bodyEnd;
+    annotations = new Annotations();
     annotations.addAnnotations(declaredAnnotations);
   }
 
+  /**
+   * @param enclosingType
+   * @param ctor
+   */
+  JAbstractMethod(JAbstractMethod srcMethod) {
+    this.annotations = new Annotations(srcMethod.annotations);
+    this.bodyEnd = srcMethod.bodyEnd;
+    this.bodyStart = srcMethod.bodyStart;
+    this.declEnd = srcMethod.declEnd;
+    this.declStart = srcMethod.declStart;
+    this.isVarArgs = srcMethod.isVarArgs;
+    MetaData.copy(this, srcMethod.metaData);
+    this.modifierBits = srcMethod.modifierBits;
+    this.name = srcMethod.name;
+  }
+
   public void addMetaData(String tagName, String[] values) {
     metaData.addMetaData(tagName, values);
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
index a206c4b..d2f3ba7 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
@@ -24,6 +24,7 @@
  * Type representing a Java array.
  */
 public class JArrayType extends JClassType {
+  private static final JArrayType[] NO_JARRAYS = new JArrayType[0];
 
   private JType componentType;
 
@@ -63,14 +64,12 @@
 
   @Override
   public JField findField(String name) {
-    // TODO length
     return null;
   }
 
   @Override
   public JMethod findMethod(String name, JType[] paramTypes) {
-    // TODO Object
-    return null;
+    return getOracle().getJavaLangObject().findMethod(name, paramTypes);
   }
 
   @Override
@@ -110,12 +109,12 @@
   @Override
   public JConstructor getConstructor(JType[] paramTypes)
       throws NotFoundException {
-    return null;
+    throw new NotFoundException();
   }
 
   @Override
   public JConstructor[] getConstructors() {
-    return null;
+    return TypeOracle.NO_JCTORS;
   }
 
   @Override
@@ -130,19 +129,16 @@
 
   @Override
   public JClassType getErasedType() {
-    // TODO array of component type
-    return this;
+    return getOracle().getArrayType(getComponentType().getErasedType());
   }
 
   @Override
   public JField getField(String name) {
-    // TODO length
     return null;
   }
 
   @Override
   public JField[] getFields() {
-    // TODO length
     return TypeOracle.NO_JFIELDS;
   }
 
@@ -151,10 +147,12 @@
     return TypeOracle.NO_JCLASSES;
   }
 
+  @Override
   public String getJNISignature() {
     return "[" + componentType.getJNISignature();
   }
 
+  @Override
   public JType getLeafType() {
     return componentType.getLeafType();
   }
@@ -172,20 +170,17 @@
   @Override
   public JMethod getMethod(String name, JType[] paramTypes)
       throws NotFoundException {
-    // TODO Object
-    return null;
+    return getOracle().getJavaLangObject().getMethod(name, paramTypes);
   }
 
   @Override
   public JMethod[] getMethods() {
-    // TODO Object
-    return null;
+    return getOracle().getJavaLangObject().getMethods();
   }
 
   @Override
   public String getName() {
-    // TODO Auto-generated method stub
-    return null;
+    return getSimpleSourceName();
   }
 
   @Override
@@ -205,26 +200,32 @@
 
   @Override
   public JMethod[] getOverloads(String name) {
-    // TODO Object
-    return null;
+    return getOracle().getJavaLangObject().getOverloads(name);
   }
 
   @Override
   public JMethod[] getOverridableMethods() {
-    // TODO Object
-    return null;
+    return getOracle().getJavaLangObject().getOverridableMethods();
   }
 
   @Override
   public JPackage getPackage() {
-    // TODO
-    return null;
+    JType leafType = getLeafType();
+    if (leafType.isPrimitive() != null) {
+      // TODO: is there a default package?
+      return null;
+    }
+
+    JClassType leafClass = (JClassType) leafType;
+    return leafClass.getPackage();
   }
 
+  @Override
   public String getParameterizedQualifiedSourceName() {
     return getComponentType().getParameterizedQualifiedSourceName() + "[]";
   }
 
+  @Override
   public String getQualifiedSourceName() {
     if (lazyQualifiedName == null) {
       lazyQualifiedName = getComponentType().getQualifiedSourceName() + "[]";
@@ -241,6 +242,7 @@
     return 1;
   }
 
+  @Override
   public String getSimpleSourceName() {
     if (lazySimpleName == null) {
       lazySimpleName = getComponentType().getSimpleSourceName() + "[]";
@@ -249,21 +251,64 @@
   }
 
   @Override
-  public JClassType[] getSubtypes() {
-    // TODO
-    return TypeOracle.NO_JCLASSES;
+  public JArrayType[] getSubtypes() {
+    if (getComponentType().isPrimitive() != null) {
+      return NO_JARRAYS;
+    }
+
+    JClassType componentClass = (JClassType) getComponentType();
+    JClassType[] componentSubtypes = componentClass.getSubtypes();
+    JArrayType[] arraySubtypes = new JArrayType[componentSubtypes.length];
+    for (int i = 0; i < componentSubtypes.length; ++i) {
+      arraySubtypes[i] = getOracle().getArrayType(componentSubtypes[i]);
+    }
+
+    return arraySubtypes;
   }
 
   @Override
   public JClassType getSuperclass() {
-    // TODO Object?
-    return null;
+    JType compType = getComponentType();
+    JClassType javaLangObject = getOracle().getJavaLangObject();
+    if (compType.isPrimitive() != null) {
+      // Super of primitive[] is object
+      return javaLangObject;
+    }
+
+    JArrayType arrayComponent = compType.isArray();
+    if (arrayComponent != null) {
+      // Superclass of multi-dimensional array is superclass of component
+      return getOracle().getArrayType(arrayComponent.getSuperclass());
+    }
+
+    JClassType type = compType.isClassOrInterface();
+    if (type == javaLangObject) {
+      // Superclass of Object[] is Object
+      return javaLangObject;
+    }
+
+    JClassType superType = type.getSuperclass();
+    if (superType == null) {
+      // Superclass of interface[] is Object[]
+      assert (type.isInterface() == null);
+      superType = javaLangObject;
+    }
+
+    return getOracle().getArrayType(superType);
   }
 
   @Override
   public String getTypeHash() throws UnableToCompleteException {
-    // TODO
-    return null;
+    JType leafType = getLeafType();
+    JClassType leafClassType = leafType.isClassOrInterface();
+    if (leafClassType != null) {
+      return leafClassType.getTypeHash();
+    }
+
+    // Arrays of primitive types should have a stable hash
+    assert (leafType.isPrimitive() != null);
+
+    return leafType.getQualifiedSourceName();
   }
 
   @Override
@@ -276,22 +321,51 @@
     return false;
   }
 
+  @Override
   public JArrayType isArray() {
     return this;
   }
 
   @Override
   public boolean isAssignableFrom(JClassType possibleSubtype) {
-    // TODO
-    return false;
+    if (this == possibleSubtype) {
+      // type is assignable to itself
+      return true;
+    }
+
+    JArrayType possibleSubtypeArray = possibleSubtype.isArray();
+    if (possibleSubtypeArray == null) {
+      // possible subtype must be an array to be assignable
+      return false;
+    }
+
+    JType thisComponentType = getComponentType();
+    JType otherComponentType = possibleSubtypeArray.getComponentType();
+
+    if (thisComponentType.isPrimitive() != null
+        || otherComponentType.isPrimitive() != null) {
+      /*
+       * Since this was not equal to the possible subtype, we know that either
+       * the dimensions are off or the component types are off
+       */
+      return false;
+    }
+
+    assert (thisComponentType instanceof JClassType);
+    assert (otherComponentType instanceof JClassType);
+
+    JClassType thisComponentClassType = (JClassType) thisComponentType;
+    JClassType otherComponentClassType = (JClassType) otherComponentType;
+
+    return thisComponentClassType.isAssignableFrom(otherComponentClassType);
   }
 
   @Override
   public boolean isAssignableTo(JClassType possibleSupertype) {
-    // TODO
-    return false;
+    return possibleSupertype.isAssignableFrom(this);
   }
 
+  @Override
   public JClassType isClass() {
     // intentional null
     return null;
@@ -303,10 +377,16 @@
   }
 
   @Override
+  public JEnumType isEnum() {
+    return null;
+  }
+
+  @Override
   public JGenericType isGenericType() {
     return null;
   }
 
+  @Override
   public JClassType isInterface() {
     // intentional null
     return null;
@@ -322,11 +402,13 @@
     return false;
   }
 
+  @Override
   public JParameterizedType isParameterized() {
     // intentional null
     return null;
   }
 
+  @Override
   public JPrimitiveType isPrimitive() {
     // intentional null
     return null;
@@ -357,6 +439,11 @@
     return true;
   }
 
+  @Override
+  public JWildcardType isWildcard() {
+    return null;
+  }
+
   public void setLeafType(JType type) {
     JArrayType componentTypeIsArray = componentType.isArray();
     if (componentTypeIsArray != null) {
@@ -370,6 +457,7 @@
   public void setSuperclass(JClassType type) {
   }
 
+  @Override
   public String toString() {
     return getQualifiedSourceName();
   }
@@ -388,13 +476,15 @@
   @Override
   protected void getOverridableMethodsOnSuperclassesAndThisClass(
       Map<String, JMethod> methodsBySignature) {
-    // TODO Object
+    getOracle().getJavaLangObject().getOverridableMethodsOnSuperclassesAndThisClass(
+        methodsBySignature);
   }
 
   @Override
   protected void getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(
       Map<String, JMethod> methodsBySignature) {
-    // TODO Object
+    getOracle().getJavaLangObject().getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(
+        methodsBySignature);
   }
 
   @Override
@@ -435,6 +525,12 @@
   }
 
   @Override
+  JArrayType getSubstitutedType(JParameterizedType parameterizedType) {
+    return oracle.getArrayType(getComponentType().getSubstitutedType(
+        parameterizedType));
+  }
+
+  @Override
   void notifySuperTypes() {
   }
 
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JBound.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JBound.java
index a59fc01..b9f6141 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JBound.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JBound.java
@@ -16,36 +16,81 @@
 package com.google.gwt.core.ext.typeinfo;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
- * 
+ * Represents a bound for a {@link JTypeParameter} or a {@link JWildcardType}.
  */
-public class JBound {
+public abstract class JBound {
+  /**
+   * Types which make up this bound.
+   */
+  private final List<JClassType> bounds = new ArrayList<JClassType>();
 
-  private final JClassType firstBound;
-  private final List<JClassType> lowerBounds = new ArrayList<JClassType>();
-  private final List<JClassType> upperBounds = new ArrayList<JClassType>();
-
-  public JBound(JClassType firstBound) {
-    this.firstBound = firstBound;
+  JBound(JClassType[] bounds) {
+    this.bounds.addAll(Arrays.asList(bounds));
   }
 
-  public void addLowerBound(JClassType bound) {
-    lowerBounds.add(bound);
-  }
-
-  public void addUpperBound(JClassType bound) {
-    upperBounds.add(bound);
+  public JClassType[] getBounds() {
+    return bounds.toArray(TypeOracle.NO_JCLASSES);
   }
 
   public JClassType getFirstBound() {
-    return firstBound;
+    assert (!bounds.isEmpty());
+
+    return bounds.get(0);
   }
 
+  public String getQualifiedSourceName() {
+    return toString(true);
+  }
+
+  public String getSimpleSourceName() {
+    return toString(false);
+  }
+
+  public abstract JLowerBound isLowerBound();
+
+  public abstract JUpperBound isUpperBound();
+
   @Override
   public String toString() {
-    return " extends " + firstBound.getParameterizedQualifiedSourceName();
+    return getQualifiedSourceName();
   }
-  
+
+  abstract JClassType[] getSubtypes();
+
+  abstract boolean isAssignableFrom(JBound possibleSubWildcard);
+
+  private String toString(boolean useQualifiedNames) {
+    StringBuffer sb = new StringBuffer();
+
+    if (isUpperBound() != null) {
+      sb.append(" extends ");
+    } else {
+      sb.append(" super ");
+    }
+
+    boolean needsAmpersand = false;
+    for (JClassType bound : bounds) {
+
+      if (needsAmpersand) {
+        sb.append(" & ");
+      } else {
+        needsAmpersand = true;
+      }
+
+      String name;
+      if (useQualifiedNames) {
+        name = bound.getParameterizedQualifiedSourceName();
+      } else {
+        name = bound.getSimpleSourceName();
+      }
+
+      sb.append(name);
+    }
+
+    return sb.toString();
+  }
 }
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 276a179..359bc04 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
@@ -222,11 +222,13 @@
 
   abstract JClassType findNestedTypeImpl(String[] typeName, int index);
 
+  @Override
+  abstract JClassType getSubstitutedType(JParameterizedType parameterizedType);
+
   abstract void notifySuperTypes();
 
   /**
    * Removes references to this instance from all of its super types.
    */
   abstract void removeFromSupertypes();
-
 }
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 feb2044..504eaa3 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
@@ -37,10 +37,17 @@
     enclosingType.addConstructor(this);
   }
 
+  JConstructor(JClassType enclosingType, JConstructor ctor) {
+    super(ctor);
+    this.enclosingType = enclosingType;
+  }
+
+  @Override
   public JClassType getEnclosingType() {
     return enclosingType;
   }
 
+  @Override
   public String getReadableDeclaration() {
     String[] names = TypeOracle.modifierBitsToNames(getModifierBits());
     StringBuilder sb = new StringBuilder();
@@ -57,14 +64,17 @@
     return sb.toString();
   }
 
+  @Override
   public JConstructor isConstructor() {
     return this;
   }
 
+  @Override
   public JMethod isMethod() {
     return null;
   }
 
+  @Override
   public String toString() {
     return getReadableDeclaration();
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JDelegatingClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JDelegatingClassType.java
index 29df9df..e421658 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JDelegatingClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JDelegatingClassType.java
@@ -26,7 +26,7 @@
  */
 abstract class JDelegatingClassType extends JClassType {
 
-  protected JClassType baseType;
+  private JClassType baseType;
 
   JDelegatingClassType() {
   }
@@ -84,6 +84,10 @@
     return baseType.getAnnotations();
   }
 
+  public JClassType getBaseType() {
+    return baseType;
+  }
+
   @Override
   public int getBodyEnd() {
     return baseType.getBodyEnd();
@@ -123,9 +127,12 @@
 
   @Override
   public JClassType getEnclosingType() {
+    // TODO this can be wrong if the enclosing type is a parameterized type. For
+    // example, if a generic class has a non-static generic inner class.
     return baseType.getEnclosingType();
   }
 
+  @Override
   public JClassType getErasedType() {
     return baseType.getErasedType();
   }
@@ -220,9 +227,6 @@
     return baseType.getSuperclass();
   }
 
-  /**
-   * TODO: What is this???
-   */
   @Override
   public String getTypeHash() throws UnableToCompleteException {
     return baseType.getTypeHash();
@@ -282,6 +286,11 @@
   }
 
   @Override
+  public final JEnumType isEnum() {
+    return null;
+  }
+
+  @Override
   public JClassType isInterface() {
     if (baseType.isInterface() != null) {
       return this;
@@ -410,5 +419,4 @@
   final void setBaseType(JClassType baseType) {
     this.baseType = baseType;
   }
-
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumConstant.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumConstant.java
new file mode 100644
index 0000000..cf35f88
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumConstant.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+/**
+ * An enumeration constant declared in an enumerated type. 
+ */
+public class JEnumConstant extends JField {
+  private final int ordinal;
+
+  public JEnumConstant(JClassType enclosingType, String name,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
+      int ordinal) {
+    super(enclosingType, name, declaredAnnotations);
+    this.ordinal = ordinal;
+  }
+
+  /**
+   * Returns the ordinal value for this enumeration constant.
+   * 
+   * @return ordinal value for this enumeration constant
+   */
+  public int getOrdinal() {
+    return ordinal;
+  }
+
+  @Override
+  public JEnumConstant isEnumConstant() {
+    return this;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java
new file mode 100644
index 0000000..c91f14b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java
@@ -0,0 +1,74 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+/**
+ * Type representing a Java enumerated type.
+ */
+public class JEnumType extends JRealClassType {
+  private JEnumConstant[] lazyEnumConstants;
+
+  /**
+   * @param oracle
+   * @param cup
+   * @param declaringPackage
+   * @param enclosingType
+   * @param isLocalType
+   * @param name
+   * @param declStart
+   * @param declEnd
+   * @param bodyStart
+   * @param bodyEnd
+   * @param isInterface
+   */
+  public JEnumType(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);
+  }
+
+  /**
+   * Returns the enumeration constants declared by this enumeration.
+   * 
+   * @return enumeration constants declared by this enumeration
+   */
+  public JEnumConstant[] getEnumConstants() {
+    if (lazyEnumConstants == null) {
+      List<JEnumConstant> enumConstants = new ArrayList<JEnumConstant>();
+      for (JField field : getFields()) {
+        if (field.isEnumConstant() != null) {
+          enumConstants.add(field.isEnumConstant());
+        }
+      }
+
+      assert (!enumConstants.isEmpty());
+
+      lazyEnumConstants = enumConstants.toArray(new JEnumConstant[0]);
+    }
+
+    return lazyEnumConstants;
+  }
+
+  @Override
+  public JEnumType isEnum() {
+    return this;
+  }
+}
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 d8d920e..f7ce1d7 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
@@ -23,7 +23,7 @@
  */
 public class JField implements HasAnnotations, HasMetaData {
 
-  private final Annotations annotations = new Annotations();
+  private final Annotations annotations;
 
   private final JClassType enclosingType;
 
@@ -41,14 +41,24 @@
 
   public JField(JClassType enclosingType, String name,
       Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+    assert (enclosingType != null);
     this.enclosingType = enclosingType;
     this.name = name;
-
-    assert (enclosingType != null);
-    enclosingType.addField(this);
+    this.enclosingType.addField(this);
+    annotations =  new Annotations();
     annotations.addAnnotations(declaredAnnotations);
   }
 
+  JField(JClassType enclosingType, JField srcField) {
+    this.annotations = new Annotations(srcField.annotations);
+    this.enclosingType = enclosingType;
+    this.modifierBits = srcField.modifierBits;
+    this.name = srcField.name;
+    this.type = srcField.type;
+
+    MetaData.copy(this, srcField);
+  }
+
   public void addMetaData(String tagName, String[] values) {
     metaData.addMetaData(tagName, values);
   }
@@ -99,6 +109,10 @@
     return 0 == (modifierBits & (TypeOracle.MOD_PUBLIC | TypeOracle.MOD_PRIVATE | TypeOracle.MOD_PROTECTED));
   }
 
+  public JEnumConstant isEnumConstant() {
+    return null;
+  }
+
   public boolean isFinal() {
     return 0 != (modifierBits & TypeOracle.MOD_FINAL);
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
index d5dda07..533f074 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
@@ -41,7 +41,14 @@
   @Override
   public String getParameterizedQualifiedSourceName() {
     StringBuffer sb = new StringBuffer();
-    sb.append(getQualifiedSourceName());
+
+    if (getEnclosingType() != null) {
+      sb.append(getEnclosingType().getParameterizedQualifiedSourceName());
+      sb.append(".");
+      sb.append(getSimpleSourceName());
+    } else {
+      sb.append(getQualifiedSourceName());
+    }    
 
     sb.append('<');
     boolean needComma = false;
@@ -59,8 +66,9 @@
 
   public JRawType getRawType() {
     if (lazyRawType == null) {
-      lazyRawType = new JRawType(this);
+      lazyRawType = new JRawType(this, getEnclosingType());
     }
+
     return lazyRawType;
   }
 
@@ -69,21 +77,17 @@
   }
 
   @Override
-  public boolean isDefaultInstantiable() {
-    /*
-     * By definition, you cannot instantiate a generic type, only a
-     * parameterized type or a raw type?
-     */
-    return false;
-  }
-
-  @Override
   public JGenericType isGenericType() {
     return this;
   }
 
-  protected boolean isDefaultInstantiableIfParameterized() {
-    return super.isDefaultInstantiable();
+  @Override
+  public String toString() {
+    if (isInterface() != null) {
+      return "interface " + getParameterizedQualifiedSourceName();
+    }
+
+    return "class " + getParameterizedQualifiedSourceName();
   }
 
   void addTypeParameter(JTypeParameter typeParameter) {
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JLowerBound.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JLowerBound.java
new file mode 100644
index 0000000..111bfa9
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JLowerBound.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+/**
+ * Represents the super bound used in {@link JWildcardType}s.
+ */
+public class JLowerBound extends JBound {
+  /**
+   * 
+   */
+  public JLowerBound(JClassType lowerBound) {
+    this(new JClassType[] {lowerBound});
+  }
+
+  /**
+   * 
+   */
+  public JLowerBound(JClassType[] lowerBounds) {
+    super(lowerBounds);
+  }
+
+  @Override
+  public JLowerBound isLowerBound() {
+    return this;
+  }
+
+  @Override
+  public JUpperBound isUpperBound() {
+    return null;
+  }
+
+  @Override
+  JClassType[] getSubtypes() {
+    return TypeOracle.NO_JCLASSES;
+  }
+
+  @Override
+  boolean isAssignableFrom(JBound possibleSubWildcard) {
+    JLowerBound lowerBound = possibleSubWildcard.isLowerBound();
+    if (lowerBound != null) {
+      return lowerBound.getFirstBound().isAssignableFrom(getFirstBound());
+    }
+
+    return false;
+  }
+}
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 6afd46f..bef2f11 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
@@ -40,6 +40,12 @@
     enclosingType.addMethod(this);
   }
 
+  JMethod(JClassType enclosingType, JMethod srcMethod) {
+    super(srcMethod);
+    this.enclosingType = enclosingType;
+    this.returnType = srcMethod.returnType;
+  }
+
   @Override
   public JClassType getEnclosingType() {
     return enclosingType;
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 07aaaf1..bf4b2b2 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
@@ -23,7 +23,7 @@
  */
 public class JParameter implements HasAnnotations, HasMetaData {
 
-  private final Annotations annotations = new Annotations();
+  private final Annotations annotations;
 
   private final HasMetaData metaData = new MetaData();
 
@@ -45,9 +45,18 @@
 
     enclosingMethod.addParameter(this);
 
+    annotations = new Annotations();
     annotations.addAnnotations(declaredAnnotations);
   }
 
+  JParameter(JAbstractMethod enclosingMethod, JParameter srcParam) {
+    this.enclosingMethod = enclosingMethod;
+    this.type = srcParam.type;
+    this.name = srcParam.name;
+    this.annotations = new Annotations(srcParam.annotations);
+    MetaData.copy(this, srcParam);
+  }
+
   public void addMetaData(String tagName, String[] values) {
     metaData.addMetaData(tagName, values);
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java
index 46f375a..ad0a18f 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java
@@ -16,21 +16,92 @@
 package com.google.gwt.core.ext.typeinfo;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Represents a parameterized type in a declaration.
  */
 public class JParameterizedType extends JDelegatingClassType {
+  /**
+   * Create a parameterized type along with any necessary enclosing
+   * parameterized types. Enclosing parameterized types are necessary when the
+   * base type is a non-static member and the enclosing type is also generic.
+   */
+  private static JParameterizedType createParameterizedTypeRecursive(
+      JGenericType baseType, Map<JClassType, JClassType> substitutionMap) {
+    JClassType enclosingType = null;
+    if (baseType.isMemberType() && !baseType.isStatic()) {
+      JGenericType isGenericEnclosingType = baseType.getEnclosingType().isGenericType();
+      if (isGenericEnclosingType != null) {
+        enclosingType = createParameterizedTypeRecursive(
+            isGenericEnclosingType, substitutionMap);
+      }
+    }
 
-  private final Members members = new Members(this);
+    JTypeParameter[] typeParameters = baseType.getTypeParameters();
+    JClassType[] newTypeArgs = new JClassType[typeParameters.length];
+    TypeOracle oracle = baseType.getOracle();
+    for (int i = 0; i < newTypeArgs.length; ++i) {
+      JClassType newTypeArg = substitutionMap.get(typeParameters[i]);
+      if (newTypeArg == null) {
+        JBound typeParamBounds = typeParameters[i].getBounds();
+        JUpperBound newTypeArgBounds = new JUpperBound(
+            typeParamBounds.getFirstBound());
+        newTypeArg = oracle.getWildcardType(newTypeArgBounds);
+      }
+
+      newTypeArgs[i] = newTypeArg;
+    }
+
+    // TODO: this is wrong if the generic type is a non-static inner class.
+    JParameterizedType parameterizedType = oracle.getParameterizedType(
+        baseType, enclosingType, newTypeArgs);
+    return parameterizedType;
+  }
+
+  private final JClassType enclosingType;
+
+  private List<JClassType> interfaces;
+
+  private JClassType lazySuperclass;
+
+  private final AbstractMembers members;
 
   private final List<JClassType> typeArgs = new ArrayList<JClassType>();
 
-  JParameterizedType(JGenericType baseType) {
+  /**
+   * This map records the JClassType that should be used in place of a given
+   * {@link JTypeParameter}.
+   */
+  private final Map<JTypeParameter, JClassType> substitutionMap = new IdentityHashMap<JTypeParameter, JClassType>();
+
+  public JParameterizedType(JGenericType baseType, JClassType enclosingType,
+      JClassType[] typeArgs) {
     super.setBaseType(baseType);
-    // TODO: type substitutions setting up fields/methods!
+
+    this.enclosingType = enclosingType;
+
+    // NOTE: this instance is not considered a nested type of the enclosing type
+
+    final JParameterizedType parameterizedType = this;
+    members = new DelegateMembers(this, baseType, new Substitution() {
+      public JType getSubstitution(JType type) {
+        return type.getSubstitutedType(parameterizedType);
+      }
+    });
+
+    List<JClassType> typeArgsList = Arrays.asList(typeArgs);
+    this.typeArgs.addAll(typeArgsList);
+    assert (typeArgsList.indexOf(null) == -1);
+
+    initializeTypeParameterSubstitutionMap();
+
+    // NOTE: Can't perform substitutions until we are done building
   }
 
   @Override
@@ -53,8 +124,9 @@
     return members.findNestedType(typeName);
   }
 
+  @Override
   public JGenericType getBaseType() {
-    return (JGenericType) baseType;
+    return (JGenericType) super.getBaseType();
   }
 
   @Override
@@ -69,6 +141,11 @@
   }
 
   @Override
+  public JClassType getEnclosingType() {
+    return enclosingType;
+  }
+
+  @Override
   public JField getField(String name) {
     return members.getField(name);
   }
@@ -79,6 +156,19 @@
   }
 
   @Override
+  public JClassType[] getImplementedInterfaces() {
+    if (interfaces == null) {
+      interfaces = new ArrayList<JClassType>();
+      JClassType[] intfs = getBaseType().getImplementedInterfaces();
+      for (JClassType intf : intfs) {
+        JClassType newIntf = intf.getSubstitutedType(this);
+        interfaces.add(newIntf);
+      }
+    }
+    return interfaces.toArray(TypeOracle.NO_JCLASSES);
+  }
+
+  @Override
   public JMethod getMethod(String name, JType[] paramTypes)
       throws NotFoundException {
     return members.getMethod(name, paramTypes);
@@ -120,19 +210,35 @@
   @Override
   public String getParameterizedQualifiedSourceName() {
     StringBuffer sb = new StringBuffer();
-    sb.append(getQualifiedSourceName());
 
-    sb.append('<');
-    boolean needComma = false;
-    for (JType typeArg : typeArgs) {
-      if (needComma) {
-        sb.append(", ");
-      } else {
-        needComma = true;
-      }
-      sb.append(typeArg.getParameterizedQualifiedSourceName());
+    if (getEnclosingType() != null) {
+      sb.append(getEnclosingType().getParameterizedQualifiedSourceName());
+      sb.append(".");
+      sb.append(getSimpleSourceName());
+    } else {
+      sb.append(getQualifiedSourceName());
     }
-    sb.append('>');
+
+    if (typeArgs.size() > 0) {
+      sb.append('<');
+      boolean needComma = false;
+      for (JType typeArg : typeArgs) {
+        if (needComma) {
+          sb.append(", ");
+        } else {
+          needComma = true;
+        }
+        sb.append(typeArg.getParameterizedQualifiedSourceName());
+      }
+      sb.append('>');
+    } else {
+      /*
+       * Non-static, inner classes of generic types are modeled as generic, even
+       * if they do not declare type parameters or reference the type parameters
+       * of their enclosing generic type.
+       */
+    }
+
     return sb.toString();
   }
 
@@ -140,6 +246,7 @@
    * Everything is fully qualified and includes the &lt; and &gt; in the
    * signature.
    */
+  @Override
   public String getQualifiedSourceName() {
     return getBaseType().getQualifiedSourceName();
   }
@@ -151,8 +258,60 @@
   /**
    * In this case, the raw type name.
    */
+  @Override
   public String getSimpleSourceName() {
-    return getRawType().getSimpleSourceName();
+    return getBaseType().getSimpleSourceName();
+  }
+
+  /*
+   * Goal: Return a list of possible subtypes of this parameterized type. In the
+   * event that we have generic subtypes and we cannot resolve the all of the
+   * type arguments, we need to wildcard types in place of the arguments that we
+   * cannot resolve.
+   * 
+   * Algorithm: - Ask generic type for its subtypes - Filter subtypes of the
+   * generic which cannot be our subtype.
+   */
+  @Override
+  public JClassType[] getSubtypes() {
+    List<JClassType> subtypeList = new ArrayList<JClassType>();
+
+    // Parameterized types are not tracked in the subtype hierarchy; ask base
+    // type
+    JClassType[] genericSubtypes = getBaseType().getSubtypes();
+    for (JClassType subtype : genericSubtypes) {
+      Set<JClassType> typeHierarchy = getFlattenedTypeHierarchy(subtype);
+
+      // Could be a subtype depending on how it is substituted
+      Map<JClassType, JClassType> substitutions = new IdentityHashMap<JClassType, JClassType>();
+      if (isSubtype(subtype, typeHierarchy, substitutions, true)) {
+        JGenericType genericType = subtype.isGenericType();
+        if (genericType != null) {
+          subtype = createParameterizedTypeRecursive(genericType, substitutions);
+        }
+
+        subtypeList.add(subtype);
+      }
+    }
+
+    return subtypeList.toArray(TypeOracle.NO_JCLASSES);
+  }
+
+  @Override
+  public JClassType getSuperclass() {
+    if (isInterface() != null) {
+      return null;
+    }
+
+    if (lazySuperclass == null) {
+      JGenericType baseType = getBaseType();
+      JClassType superclass = baseType.getSuperclass();
+      assert (superclass != null);
+
+      lazySuperclass = superclass.getSubstitutedType(this);
+    }
+
+    return lazySuperclass;
   }
 
   public JClassType[] getTypeArgs() {
@@ -160,8 +319,24 @@
   }
 
   @Override
-  public boolean isDefaultInstantiable() {
-    return getBaseType().isDefaultInstantiableIfParameterized();
+  public boolean isAssignableFrom(JClassType possibleSubtype) {
+    if (possibleSubtype == this) {
+      return true;
+    }
+
+    JRawType possibleRawSubtype = possibleSubtype.isRawType();
+    if (possibleRawSubtype != null) {
+      return getBaseType().isAssignableFrom(possibleRawSubtype.getBaseType());
+    }
+
+    Set<JClassType> typeHierarchy = getFlattenedTypeHierarchy(possibleSubtype);
+    return isSubtype(possibleSubtype, typeHierarchy,
+        new IdentityHashMap<JClassType, JClassType>(), false);
+  }
+
+  @Override
+  public boolean isAssignableTo(JClassType possibleSupertype) {
+    return possibleSupertype.isAssignableFrom(this);
   }
 
   @Override
@@ -180,10 +355,31 @@
   }
 
   @Override
+  public JWildcardType isWildcard() {
+    return null;
+  }
+
+  /**
+   */
+  public void setTypeArguments(JClassType[] typeArgs) {
+    this.typeArgs.addAll(Arrays.asList(typeArgs));
+  }
+
+  @Override
+  public String toString() {
+    if (isInterface() != null) {
+      return "interface " + getParameterizedQualifiedSourceName();
+    }
+
+    return "class " + getParameterizedQualifiedSourceName();
+  }
+
+  @Override
   protected JClassType findNestedTypeImpl(String[] typeName, int index) {
     return members.findNestedTypeImpl(typeName, index);
   }
 
+  @Override
   protected void getOverridableMethodsOnSuperclassesAndThisClass(
       Map<String, JMethod> methodsBySignature) {
     members.getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
@@ -197,14 +393,179 @@
    * 
    * @param methodsBySignature
    */
+  @Override
   protected void getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(
       Map<String, JMethod> methodsBySignature) {
     members.getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
   }
 
-  void addTypeArg(JClassType type) {
-    assert (type.isPrimitive() == null);
-    typeArgs.add(type);
+  @Override
+  JClassType getSubstitutedType(JParameterizedType parameterizedType) {
+    if (this == parameterizedType) {
+      return this;
+    }
+
+    JClassType[] newTypeArgs = new JClassType[typeArgs.size()];
+    for (int i = 0; i < newTypeArgs.length; ++i) {
+      newTypeArgs[i] = typeArgs.get(i).getSubstitutedType(parameterizedType);
+    }
+
+    return getOracle().getParameterizedType(getBaseType(), getEnclosingType(),
+        newTypeArgs);
   }
 
-}
+  /**
+   * Returns the {@link JClassType} that is a substitute for the given
+   * {@link JTypeParameter}. If there is no substitution, the original
+   * {@link JTypeParameter} is returned.
+   */
+  JClassType getTypeParameterSubstitution(JTypeParameter typeParameter) {
+    JClassType substitute = substitutionMap.get(typeParameter);
+    if (substitute != null) {
+      return substitute;
+    }
+
+    return typeParameter;
+  }
+
+  boolean hasTypeArgs(JClassType[] otherArgTypes) {
+    if (otherArgTypes.length != typeArgs.size()) {
+      return false;
+    }
+
+    for (int i = 0; i < otherArgTypes.length; ++i) {
+      // Identity tests are ok since identity is durable within an oracle.
+      //
+      if (otherArgTypes[i] != typeArgs.get(i)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Initialize a map of substitutions for {@link JTypeParameter}s to
+   * corresponding {@link JClassType}s.
+   */
+  void initializeTypeParameterSubstitutionMap() {
+    JParameterizedType currentParameterizedType = this;
+
+    while (currentParameterizedType != null) {
+      JGenericType genericType = currentParameterizedType.getBaseType();
+      JTypeParameter[] typeParameters = genericType.getTypeParameters();
+      JClassType[] typeArguments = currentParameterizedType.getTypeArgs();
+
+      for (JTypeParameter typeParameter : typeParameters) {
+        substitutionMap.put(typeParameter,
+            typeArguments[typeParameter.getOrdinal()]);
+      }
+
+      if (currentParameterizedType.isStatic()) {
+        break;
+      }
+
+      JClassType maybeParameterizedType = currentParameterizedType.getEnclosingType();
+      if (maybeParameterizedType == null
+          || maybeParameterizedType.isParameterized() == null) {
+        break;
+      }
+      currentParameterizedType = maybeParameterizedType.isParameterized();
+    }
+  }
+
+  /**
+   * Returns the flattened view of the type hierarchy.
+   */
+  private Set<JClassType> getFlattenedTypeHierarchy(JClassType type) {
+    Set<JClassType> typesSeen = new HashSet<JClassType>();
+    getFlattenedTypeHierarchyRecursive(type, typesSeen);
+    return typesSeen;
+  }
+
+  private void getFlattenedTypeHierarchyRecursive(JClassType type,
+      Set<JClassType> typesSeen) {
+    if (typesSeen.contains(type)) {
+      return;
+    }
+    typesSeen.add(type);
+
+    // Superclass
+    JClassType superclass = type.getSuperclass();
+    if (superclass != null) {
+      getFlattenedTypeHierarchyRecursive(superclass, typesSeen);
+    }
+
+    // Check the interfaces
+    JClassType[] intfs = type.getImplementedInterfaces();
+    for (JClassType intf : intfs) {
+      getFlattenedTypeHierarchyRecursive(intf, typesSeen);
+    }
+  }
+
+  /**
+   * Look at the type hierarchy and see if we can find a parameterized type that
+   * has the same base type as this instance. If we find one then we check to
+   * see if the type arguments are compatible. If they are, then we record what
+   * the typeArgument needs to be replaced with in order to make it a proper
+   * subtype of this parameterized type.
+   */
+  private boolean isSubtype(JClassType subtype, Set<JClassType> typeHierarchy,
+      Map<JClassType, JClassType> substitutions, boolean lookForSubstitutions) {
+    if (typeHierarchy.contains(this)) {
+      return true;
+    }
+
+    for (JClassType type : typeHierarchy) {
+      JParameterizedType parameterizedType = type.isParameterized();
+      if (parameterizedType == null) {
+        continue;
+      }
+
+      if (parameterizedType.getBaseType() != getBaseType()) {
+        continue;
+      }
+
+      // Check the type arguments to see if they are compatible.
+      JClassType[] otherTypeArgs = parameterizedType.getTypeArgs();
+      JClassType[] myTypeArgs = getTypeArgs();
+      boolean validSubstitution = true;
+      for (int i = 0; i < myTypeArgs.length; ++i) {
+        JClassType otherTypeArg = otherTypeArgs[i];
+        JClassType myTypeArg = myTypeArgs[i];
+
+        validSubstitution = myTypeArg == otherTypeArg;
+        if (!validSubstitution) {
+          if (lookForSubstitutions) {
+            // Make sure that the other type argument is assignable from mine
+            validSubstitution = otherTypeArg.isAssignableFrom(myTypeArg);
+          } else {
+            // Looking for strict subtypes; only wildcards allow a non-exact
+            // match
+            JWildcardType isWildcard = myTypeArg.isWildcard();
+            if (isWildcard != null) {
+              validSubstitution = myTypeArg.isAssignableFrom(otherTypeArg);
+            }
+          }
+        }
+
+        if (!validSubstitution) {
+          break;
+        }
+
+        substitutions.put(otherTypeArg, myTypeArg);
+      }
+
+      if (validSubstitution) {
+        /*
+         * At this point we know that the type can be a subtype and we know the
+         * substitution to apply.
+         */
+        return true;
+      }
+    }
+
+    // Can't be a subtype regardless of substitution.
+    return false;
+  }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java
index fbbb82c..2d1e9ce 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java
@@ -66,7 +66,7 @@
   public JType getErasedType() {
     return this;
   }
-  
+
   @Override
   public String getJNISignature() {
     return jni;
@@ -95,6 +95,11 @@
   }
 
   @Override
+  public JEnumType isEnum() {
+    return null;
+  }
+
+  @Override
   public JClassType isInterface() {
     // intentional null
     return null;
@@ -116,4 +121,14 @@
     // intentional null
     return null;
   }
+
+  @Override
+  public JWildcardType isWildcard() {
+    return null;
+  }
+
+  @Override
+  JPrimitiveType getSubstitutedType(JParameterizedType parameterizedType) {
+    return this;
+  }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
index a4aceca..2d207e2 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
@@ -15,18 +15,46 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Represents a raw type; that is a generic type with no type arguments.
  */
 public class JRawType extends JDelegatingClassType {
+  private static final Substitution ERASURE_SUBSTITUTION = new Substitution() {
+    public JType getSubstitution(JType type) {
+      return type.getErasedType();
+    }
+  };
 
-  private final Members members = new Members(this);
+  private static JClassType normalizeType(JClassType type) {
+    JRawType isRawType = type.isRawType();
+    if (isRawType != null) {
+      return isRawType.getGenericType();
+    }
 
-  public JRawType(JGenericType genericType) {
+    JParameterizedType isParameterized = type.isParameterized();
+    if (isParameterized != null) {
+      return isParameterized.getBaseType();
+    }
+
+    return type;
+  }
+
+  private final JClassType enclosingType;
+
+  private List<JClassType> interfaces;
+
+  private final AbstractMembers members;
+
+  public JRawType(JGenericType genericType, JClassType enclosingType) {
     super.setBaseType(genericType);
-    // TODO: type substitutions setting up fields/methods!
+    this.enclosingType = enclosingType;
+
+    // NOTE: this instance is not considered a nested type of the enclosing type
+
+    members = new DelegateMembers(this, getBaseType(), ERASURE_SUBSTITUTION);
   }
 
   @Override
@@ -49,8 +77,11 @@
     return members.findNestedType(typeName);
   }
 
+  @Override
   public JGenericType getBaseType() {
-    return (JGenericType) baseType;
+    JGenericType genericType = super.getBaseType().isGenericType();
+    assert (genericType != null);
+    return genericType;
   }
 
   @Override
@@ -79,6 +110,19 @@
   }
 
   @Override
+  public JClassType[] getImplementedInterfaces() {
+    if (interfaces == null) {
+      interfaces = new ArrayList<JClassType>();
+      JClassType[] intfs = getBaseType().getImplementedInterfaces();
+      for (JClassType intf : intfs) {
+        JClassType newIntf = intf.getErasedType();
+        interfaces.add(newIntf);
+      }
+    }
+    return interfaces.toArray(TypeOracle.NO_JCLASSES);
+  }
+
+  @Override
   public JMethod getMethod(String name, JType[] paramTypes)
       throws NotFoundException {
     return members.getMethod(name, paramTypes);
@@ -116,12 +160,12 @@
 
   @Override
   public String getQualifiedSourceName() {
-    return baseType.getQualifiedSourceName();
+    return getBaseType().getQualifiedSourceName();
   }
 
   @Override
   public String getSimpleSourceName() {
-    return baseType.getSimpleSourceName();
+    return getBaseType().getSimpleSourceName();
   }
 
   @Override
@@ -141,8 +185,25 @@
   }
 
   @Override
-  public boolean isDefaultInstantiable() {
-    return getBaseType().isDefaultInstantiableIfParameterized();
+  public JClassType getSuperclass() {
+    JClassType baseSuper = getBaseType().getSuperclass();
+    if (baseSuper == null) {
+      return null;
+    }
+
+    return baseSuper.getErasedType();
+  }
+
+  @Override
+  public boolean isAssignableFrom(JClassType possibleSubtype) {
+    JClassType type = normalizeType(possibleSubtype);
+    return getBaseType().isAssignableFrom(type);
+  }
+
+  @Override
+  public boolean isAssignableTo(JClassType possibleSupertype) {
+    JClassType type = normalizeType(possibleSupertype);
+    return getBaseType().isAssignableTo(type);
   }
 
   @Override
@@ -161,25 +222,15 @@
   }
 
   @Override
-  protected JClassType findNestedTypeImpl(String[] typeName, int index) {
-    return members.findNestedTypeImpl(typeName, index);
+  public JWildcardType isWildcard() {
+    return null;
   }
 
-  protected void getOverridableMethodsOnSuperclassesAndThisClass(
-      Map<String, JMethod> methodsBySignature) {
-    members.getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
-  }
-
-  /**
-   * Gets the methods declared in interfaces that this type extends. If this
-   * type is a class, its own methods are not added. If this type is an
-   * interface, its own methods are added. Used internally by
-   * {@link #getOverridableMethods()}.
-   * 
-   * @param methodsBySignature
-   */
-  protected void getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(
-      Map<String, JMethod> methodsBySignature) {
-    members.getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
+  @Override
+  JRawType getSubstitutedType(JParameterizedType parameterizedType) {
+    /*
+     * Raw types do not participate in substitution.
+     */
+    return this;
   }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
index c0c84d3..74da5ea 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
@@ -59,7 +59,7 @@
 
   private String lazyQualifiedName;
 
-  private final Members members = new Members(this);
+  private final AbstractMembers members = new Members(this);
 
   private final HasMetaData metaData = new MetaData();
 
@@ -376,6 +376,11 @@
   }
 
   @Override
+  public JEnumType isEnum() {
+    return null;
+  }
+
+  @Override
   public JGenericType isGenericType() {
     return null;
   }
@@ -439,13 +444,19 @@
     return 0 != (modifierBits & TypeOracle.MOD_STATIC);
   }
 
+  @Override
+  public JWildcardType isWildcard() {
+    return null;
+  }
+
+  @Override
   public void setSuperclass(JClassType type) {
     assert (type != null);
     assert (isInterface() == null);
     this.superclass = type;
     JRealClassType realSuperType;
     if (type.isParameterized() != null) {
-      realSuperType = type.isParameterized().getBaseType();
+      realSuperType = (JRealClassType) type.isParameterized().getBaseType();
     } else if (type.isRawType() != null) {
       realSuperType = type.isRawType().getGenericType();
     } else {
@@ -545,6 +556,11 @@
     }
   }
 
+  @Override
+  JRealClassType getSubstitutedType(JParameterizedType parameterizedType) {
+    return this;
+  }
+
   void notifySuperTypes() {
     notifySuperTypesOf(this);
   }
@@ -555,5 +571,4 @@
   void removeFromSupertypes() {
     removeSubtype(this);
   }
-
 }
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 2ed4aad..c16d62c 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
@@ -59,6 +59,15 @@
     return isInterface();
   }
 
+  /**
+   * Returns this instance if it is an enumeration or <code>null</code> if it
+   * is not.
+   * 
+   * @return this instance if it is an enumeration or <code>null</code> if it
+   *         is not
+   */
+  public abstract JEnumType isEnum();
+
   public abstract JClassType isInterface();
 
   public abstract JParameterizedType isParameterized();
@@ -67,4 +76,19 @@
 
   public abstract JRawType isRawType();
 
+  public JTypeParameter isTypeParameter() {
+    return null;
+  }
+
+  public abstract JWildcardType isWildcard();
+
+  /**
+   * Returns either the substitution of this type based on the parameterized
+   * type or this instance.
+   * 
+   * @param parameterizedType
+   * @return either the substitution of this type based on the parameterized
+   *         type or this instance
+   */
+  abstract JType getSubstitutedType(JParameterizedType parameterizedType);
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
index cfea518..58cb21d 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
@@ -15,6 +15,9 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Represents one of the type parameters in a generic type.
  */
@@ -22,30 +25,35 @@
   private JBound bounds;
   private final JGenericType declaringClass;
   private final JAbstractMethod declaringMethod;
+  private final int ordinal;
   private final String typeName;
 
-  public JTypeParameter(String typeName, JAbstractMethod declaringMethod) {
+  public JTypeParameter(String typeName, JAbstractMethod declaringMethod,
+      int ordinal) {
     this.typeName = typeName;
     this.declaringMethod = declaringMethod;
     this.declaringClass = null;
+    this.ordinal = ordinal;
     declaringMethod.addTypeParameter(this);
   }
 
-  public JTypeParameter(String typeName, JGenericType declaringClass) {
+  public JTypeParameter(String typeName, JGenericType declaringClass,
+      int ordinal) {
     this.typeName = typeName;
     this.declaringClass = declaringClass;
     this.declaringMethod = null;
+    this.ordinal = ordinal;
     declaringClass.addTypeParameter(this);
   }
 
   @Override
   public JField findField(String name) {
-    return baseType.findField(name);
+    return getBaseType().findField(name);
   }
 
   @Override
   public JMethod findMethod(String name, JType[] paramTypes) {
-    return baseType.findMethod(name, paramTypes);
+    return getBaseType().findMethod(name, paramTypes);
   }
 
   public JBound getBounds() {
@@ -58,47 +66,71 @@
 
   @Override
   public JField getField(String name) {
-    return baseType.getField(name);
+    return getBaseType().getField(name);
   }
 
   @Override
   public JField[] getFields() {
-    return baseType.getFields();
+    return getBaseType().getFields();
   }
 
   public JClassType getFirstBound() {
-    return baseType;
+    return getBaseType();
   }
 
   @Override
   public JMethod getMethod(String name, JType[] paramTypes)
       throws NotFoundException {
-    return baseType.getMethod(name, paramTypes);
+    return getBaseType().getMethod(name, paramTypes);
   }
 
   @Override
   public JMethod[] getMethods() {
-    return baseType.getMethods();
+    return getBaseType().getMethods();
   }
 
   @Override
   public String getName() {
     return typeName;
   }
-  
+
   @Override
   public String getParameterizedQualifiedSourceName() {
     return typeName;
   }
-  
+
   @Override
   public String getQualifiedSourceName() {
-    return typeName;
+    return typeName + bounds.getQualifiedSourceName();
   }
 
   @Override
   public String getSimpleSourceName() {
-    return typeName;
+    return typeName + bounds.getSimpleSourceName();
+  }
+  
+  @Override 
+  public JClassType[] getSubtypes() {
+    JClassType[] subtypes = super.getSubtypes();
+    List<JClassType> intersectionTypes = new ArrayList<JClassType>();
+    for (JClassType subtype : subtypes) {
+      if (isAssignableFrom(subtype)) {
+        intersectionTypes.add(subtype);
+      }
+    }
+    return intersectionTypes.toArray(TypeOracle.NO_JCLASSES);
+  }
+
+  @Override
+  public boolean isAssignableFrom(JClassType possibleSubtype) {
+    // TODO: Should this compute an intersection?
+    return getFirstBound().isAssignableFrom(possibleSubtype);
+  }
+
+  @Override
+  public boolean isAssignableTo(JClassType possibleSupertype) {
+    // TODO: Should this compute an intersection?
+    return getFirstBound().isAssignableTo(possibleSupertype);
   }
 
   @Override
@@ -116,6 +148,16 @@
     return null;
   }
 
+  @Override
+  public JTypeParameter isTypeParameter() {
+    return this;
+  }
+
+  @Override
+  public JWildcardType isWildcard() {
+    return null;
+  }
+
   public void setBounds(JBound bounds) {
     this.bounds = bounds;
     super.setBaseType(bounds.getFirstBound());
@@ -123,10 +165,19 @@
 
   @Override
   public String toString() {
-    if (baseType.isInterface() != null) {
+    if (getBaseType().isInterface() != null) {
       return "interface " + getQualifiedSourceName();
     } else {
       return "class " + getQualifiedSourceName();
     }
   }
+
+  int getOrdinal() {
+    return ordinal;
+  }
+
+  @Override
+  JClassType getSubstitutedType(JParameterizedType parameterizedType) {
+    return parameterizedType.getTypeParameterSubstitution(this);
+  }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JUpperBound.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JUpperBound.java
new file mode 100644
index 0000000..3762cd9
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JUpperBound.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+/**
+ * Represents the extends bound used in {@link JTypeParameter}s and
+ * {@link JWildcardType}s.
+ */
+public class JUpperBound extends JBound {
+  public JUpperBound(JClassType upperBound) {
+    this(new JClassType[] {upperBound});
+  }
+
+  /**
+   * 
+   */
+  public JUpperBound(JClassType[] upperBounds) {
+    super(upperBounds);
+  }
+
+  @Override
+  public JLowerBound isLowerBound() {
+    return null;
+  }
+
+  @Override
+  public JUpperBound isUpperBound() {
+    return this;
+  }
+
+  @Override
+  JClassType[] getSubtypes() {
+    return getFirstBound().getSubtypes();
+  }
+
+  @Override
+  boolean isAssignableFrom(JBound possibleSubWildcard) {
+    JClassType firstBound = getFirstBound();
+
+    JUpperBound upperBound = possibleSubWildcard.isUpperBound();
+    if (upperBound != null) {
+      // Upper bound
+      return firstBound.isAssignableFrom(upperBound.getFirstBound());
+    }
+
+    // Lower bound
+    JClassType javaLangObject = firstBound.getOracle().getJavaLangObject();
+    return firstBound == javaLangObject
+        && possibleSubWildcard.getFirstBound() == javaLangObject;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
index 2cece8a..580e966 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
@@ -19,22 +19,21 @@
  * Represents a wildcard type argument to a parameterized type.
  */
 public class JWildcardType extends JDelegatingClassType implements HasBounds {
-
   private final JBound bounds;
 
   public JWildcardType(JBound bounds) {
-    this.bounds = bounds;
     super.setBaseType(bounds.getFirstBound());
+    this.bounds = bounds;
   }
 
   @Override
   public JField findField(String name) {
-    return baseType.findField(name);
+    return getBaseType().findField(name);
   }
 
   @Override
   public JMethod findMethod(String name, JType[] paramTypes) {
-    return baseType.findMethod(name, paramTypes);
+    return getBaseType().findMethod(name, paramTypes);
   }
 
   public JBound getBounds() {
@@ -43,37 +42,52 @@
 
   @Override
   public JField getField(String name) {
-    return baseType.getField(name);
+    return getBaseType().getField(name);
   }
 
   @Override
   public JField[] getFields() {
-    return baseType.getFields();
+    return getBaseType().getFields();
   }
 
   public JClassType getFirstBound() {
-    return baseType;
+    return getBounds().getFirstBound();
   }
 
   @Override
   public JMethod getMethod(String name, JType[] paramTypes)
       throws NotFoundException {
-    return baseType.getMethod(name, paramTypes);
+    return getBaseType().getMethod(name, paramTypes);
   }
 
   @Override
   public JMethod[] getMethods() {
-    return baseType.getMethods();
+    return getBaseType().getMethods();
   }
 
   @Override
   public String getQualifiedSourceName() {
-    return "?";
+    return "?" + bounds.getQualifiedSourceName();
   }
 
   @Override
   public String getSimpleSourceName() {
-    return "?";
+    return "?" + bounds.getSimpleSourceName();
+  }
+
+  @Override
+  public JClassType[] getSubtypes() {
+    return bounds.getSubtypes();
+  }
+
+  @Override
+  public boolean isAssignableFrom(JClassType possibleSubtype) {
+    JWildcardType possibleSubWildcard = possibleSubtype.isWildcard();
+    if (possibleSubWildcard != null) {
+      return getBounds().isAssignableFrom(possibleSubWildcard.getBounds());
+    }
+
+    return getBaseType().isAssignableFrom(possibleSubtype);
   }
 
   @Override
@@ -90,4 +104,51 @@
   public JRawType isRawType() {
     return null;
   }
+
+  @Override
+  public JWildcardType isWildcard() {
+    return this;
+  }
+
+  @Override
+  JClassType getSubstitutedType(JParameterizedType parameterizedType) {
+    JClassType[] currentBounds = bounds.getBounds();
+    JClassType[] newBounds = new JClassType[currentBounds.length];
+    for (int i = 0; i < currentBounds.length; ++i) {
+      newBounds[i] = currentBounds[i].getSubstitutedType(parameterizedType);
+    }
+
+    JBound newBound = bounds.isLowerBound() != null
+        ? new JLowerBound(newBounds) : new JUpperBound(newBounds);
+    return getOracle().getWildcardType(newBound);
+  }
+
+  /**
+   * Returns <code>true</code> if this instance has the same bounds that are
+   * requested.
+   * 
+   * @param otherBounds
+   * @return <code>true</code> if this instance has the same bounds that are
+   *         requested
+   */
+  boolean hasBounds(JBound otherBounds) {
+    if ((bounds.isUpperBound() != null && otherBounds.isLowerBound() != null)
+        || (bounds.isLowerBound() != null && otherBounds.isUpperBound() != null)) {
+      return false;
+    }
+
+    JClassType[] boundTypes = bounds.getBounds();
+    JClassType[] otherBoundTypes = otherBounds.getBounds();
+
+    if (boundTypes.length != otherBoundTypes.length) {
+      return false;
+    }
+
+    for (int i = 0; i < boundTypes.length; ++i) {
+      if (boundTypes[i] != otherBoundTypes[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/Members.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/Members.java
index f77b78e..7ff2d8b 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/Members.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/Members.java
@@ -16,249 +16,34 @@
 package com.google.gwt.core.ext.typeinfo;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.TreeMap;
 
 /**
  * A container for methods and fields.
  */
-class Members {
-
-  protected final List<JConstructor> constructors = new ArrayList<JConstructor>();
-  private JMethod[] cachedOverridableMethods;
-  private final JClassType classType;
+class Members extends AbstractMembers {
+  private final List<JConstructor> constructors = new ArrayList<JConstructor>();
   private final Map<String, JField> fields = new HashMap<String, JField>();
   private final Map<String, List<JMethod>> methods = new HashMap<String, List<JMethod>>();
-  private final Map<String, JClassType> nestedTypes = new HashMap<String, JClassType>();
 
   public Members(JClassType classType) {
-    this.classType = classType;
+    super(classType);
   }
 
-  public JConstructor findConstructor(JType[] paramTypes) {
-    JConstructor[] ctors = getConstructors();
-    for (int i = 0; i < ctors.length; i++) {
-      JConstructor candidate = ctors[i];
-      if (candidate.hasParamTypes(paramTypes)) {
-        return candidate;
-      }
-    }
-    return null;
+  @Override
+  protected List<JConstructor> doGetConstructors() {
+    return constructors;
   }
 
-  public JField findField(String name) {
-    return fields.get(name);
+  @Override
+  protected Map<String, JField> doGetFields() {
+    return fields;
   }
 
-  public JMethod findMethod(String name, JType[] paramTypes) {
-    JMethod[] overloads = getOverloads(name);
-    for (int i = 0; i < overloads.length; i++) {
-      JMethod candidate = overloads[i];
-      if (candidate.hasParamTypes(paramTypes)) {
-        return candidate;
-      }
-    }
-    return null;
+  @Override
+  protected Map<String, List<JMethod>> doGetMethods() {
+    return methods;
   }
-
-  public JClassType findNestedType(String typeName) {
-    String[] parts = typeName.split("\\.");
-    return findNestedTypeImpl(parts, 0);
-  }
-
-  public JConstructor getConstructor(JType[] paramTypes)
-      throws NotFoundException {
-    JConstructor result = findConstructor(paramTypes);
-    if (result == null) {
-      throw new NotFoundException();
-    }
-    return result;
-  }
-
-  public JConstructor[] getConstructors() {
-    return constructors.toArray(TypeOracle.NO_JCTORS);
-  }
-
-  public JField getField(String name) {
-    JField field = findField(name);
-    assert (field != null);
-    return field;
-  }
-
-  public JField[] getFields() {
-    return fields.values().toArray(TypeOracle.NO_JFIELDS);
-  }
-
-  public JMethod getMethod(String name, JType[] paramTypes)
-      throws NotFoundException {
-    JMethod result = findMethod(name, paramTypes);
-    if (result == null) {
-      throw new NotFoundException();
-    }
-    return result;
-  }
-
-  public JMethod[] getMethods() {
-    List<JMethod> resultMethods = new ArrayList<JMethod>();
-    for (List<JMethod> overloads : methods.values()) {
-      resultMethods.addAll(overloads);
-    }
-    return resultMethods.toArray(TypeOracle.NO_JMETHODS);
-  }
-
-  public JClassType getNestedType(String typeName) throws NotFoundException {
-    JClassType result = findNestedType(typeName);
-    if (result == null) {
-      throw new NotFoundException();
-    }
-    return result;
-  }
-
-  public JClassType[] getNestedTypes() {
-    return nestedTypes.values().toArray(TypeOracle.NO_JCLASSES);
-  }
-
-  public JMethod[] getOverloads(String name) {
-    List<?> resultMethods = methods.get(name);
-    if (resultMethods != null) {
-      return resultMethods.toArray(TypeOracle.NO_JMETHODS);
-    } else {
-      return TypeOracle.NO_JMETHODS;
-    }
-  }
-
-  public JMethod[] getOverridableMethods() {
-    if (cachedOverridableMethods == null) {
-      Map<String, JMethod> methodsBySignature = new TreeMap<String, JMethod>();
-      getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
-      if (classType.isClass() != null) {
-        getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
-      }
-      int size = methodsBySignature.size();
-      Collection<JMethod> leafMethods = methodsBySignature.values();
-      cachedOverridableMethods = leafMethods.toArray(new JMethod[size]);
-    }
-    return cachedOverridableMethods;
-  }
-
-  protected void addConstructor(JConstructor ctor) {
-    assert (!constructors.contains(ctor));
-    constructors.add(ctor);
-  }
-
-  protected void addField(JField field) {
-    Object existing = fields.put(field.getName(), field);
-    assert (existing == null);
-  }
-
-  protected void addMethod(JMethod method) {
-    String methodName = method.getName();
-    List<JMethod> overloads = methods.get(methodName);
-    if (overloads == null) {
-      overloads = new ArrayList<JMethod>();
-      methods.put(methodName, overloads);
-    }
-    overloads.add(method);
-  }
-
-  protected void addNestedType(JClassType type) {
-    nestedTypes.put(type.getSimpleSourceName(), type);
-  }
-
-  protected JClassType findNestedTypeImpl(String[] typeName, int index) {
-    JClassType found = nestedTypes.get(typeName[index]);
-    if (found == null) {
-      return null;
-    } else if (index < typeName.length - 1) {
-      return found.findNestedTypeImpl(typeName, index + 1);
-    } else {
-      return found;
-    }
-  }
-
-  protected void getOverridableMethodsOnSuperclassesAndThisClass(
-      Map<String, JMethod> methodsBySignature) {
-    assert (classType.isClass() != null);
-
-    // Recurse first so that more derived methods will clobber less derived
-    // methods.
-    JClassType superClass = classType.getSuperclass();
-    if (superClass != null) {
-      superClass.getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
-    }
-
-    JMethod[] declaredMethods = getMethods();
-    for (int i = 0; i < declaredMethods.length; i++) {
-      JMethod method = declaredMethods[i];
-
-      // Ensure that this method is overridable.
-      if (method.isFinal() || method.isPrivate()) {
-        // We cannot override this method, so skip it.
-        continue;
-      }
-
-      // We can override this method, so record it.
-      String sig = computeInternalSignature(method);
-      methodsBySignature.put(sig, method);
-    }
-  }
-
-  /**
-   * Gets the methods declared in interfaces that this type extends. If this
-   * type is a class, its own methods are not added. If this type is an
-   * interface, its own methods are added. Used internally by
-   * {@link #getOverridableMethods()}.
-   * 
-   * @param methodsBySignature
-   */
-  protected void getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(
-      Map<String, JMethod> methodsBySignature) {
-    // Recurse first so that more derived methods will clobber less derived
-    // methods.
-    JClassType[] superIntfs = classType.getImplementedInterfaces();
-    for (int i = 0; i < superIntfs.length; i++) {
-      JClassType superIntf = superIntfs[i];
-      superIntf.getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
-    }
-
-    if (classType.isInterface() == null) {
-      // This is not an interface, so we're done after having visited its
-      // implemented interfaces.
-      return;
-    }
-
-    JMethod[] declaredMethods = getMethods();
-    for (int i = 0; i < declaredMethods.length; i++) {
-      JMethod method = declaredMethods[i];
-
-      String sig = computeInternalSignature(method);
-      JMethod existing = methodsBySignature.get(sig);
-      if (existing != null) {
-        JClassType existingType = existing.getEnclosingType();
-        JClassType thisType = method.getEnclosingType();
-        if (thisType.isAssignableFrom(existingType)) {
-          // The existing method is in a more-derived type, so don't replace it.
-          continue;
-        }
-      }
-      methodsBySignature.put(sig, method);
-    }
-  }
-
-  private String computeInternalSignature(JMethod method) {
-    StringBuffer sb = new StringBuffer();
-    sb.setLength(0);
-    sb.append(method.getName());
-    JParameter[] params = method.getParameters();
-    for (int j = 0; j < params.length; j++) {
-      JParameter param = params[j];
-      sb.append("/");
-      sb.append(param.getType().getQualifiedSourceName());
-    }
-    return sb.toString();
-  }
-
 }
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/MetaData.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/MetaData.java
index 92fb5ba..dd4adaf 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/MetaData.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/MetaData.java
@@ -24,6 +24,23 @@
 
 class MetaData implements HasMetaData {
 
+  /**
+   * Copy metadata from one metadata container to another. Perhaps overly
+   * paranoid but HasMetaData elements are mutable.
+   * 
+   * @param dest
+   * @param src
+   */
+  static void copy(HasMetaData dest, HasMetaData src) {
+    String[] tagNames = src.getMetaDataTags();
+    for (String tagName : tagNames) {
+      String[][] tagValueSets = src.getMetaData(tagName);
+      for (String[] tagValues : tagValueSets) {
+        dest.addMetaData(tagName, tagValues);
+      }
+    }
+  }
+
   private final Map<String, List<String[]>> tagNameToStringArrayList = new HashMap<String, List<String[]>>();
 
   public void addMetaData(String tagName, String[] values) {
@@ -83,4 +100,5 @@
     }
     return sb.toString();
   }
+
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/Substitution.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/Substitution.java
new file mode 100644
index 0000000..f9df504
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/Substitution.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * Interface used to perform type parameter substitutions or raw type
+ * substitutions.
+ */
+interface Substitution {
+  JType getSubstitution(JType type);
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
index d657e71..dab0a9e 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
@@ -53,14 +53,12 @@
  * </p>
  */
 public class TypeOracle {
-
   /**
    * A reserved metadata tag to indicates that a field type, method return type
    * or method parameter type is intended to be parameterized. Note that
    * constructor type parameters are not supported at present.
    */
   public static final String TAG_TYPEARGS = "gwt.typeArgs";
-
   static final int MOD_ABSTRACT = 0x00000001;
   static final int MOD_FINAL = 0x00000002;
   static final int MOD_NATIVE = 0x00000004;
@@ -69,8 +67,8 @@
   static final int MOD_PUBLIC = 0x00000020;
   static final int MOD_STATIC = 0x00000040;
   static final int MOD_TRANSIENT = 0x00000080;
-  static final int MOD_VOLATILE = 0x00000100;
 
+  static final int MOD_VOLATILE = 0x00000100;
   static final Annotation[] NO_ANNOTATIONS = new Annotation[0];
   static final JClassType[] NO_JCLASSES = new JClassType[0];
   static final JConstructor[] NO_JCTORS = new JConstructor[0];
@@ -174,12 +172,14 @@
 
   private final Map<String, JPackage> packages = new HashMap<String, JPackage>();
 
-  private final Map<String, JParameterizedType> parameterizedTypes = new HashMap<String, JParameterizedType>();
+  private final Map<String, List<JParameterizedType>> parameterizedTypes = new HashMap<String, List<JParameterizedType>>();
 
   private int reloadCount = 0;
 
   private final Map<CompilationUnitProvider, JClassType[]> typesByCup = new IdentityHashMap<CompilationUnitProvider, JClassType[]>();
 
+  private final Map<String, List<JWildcardType>> wildcardTypes = new HashMap<String, List<JWildcardType>>();
+
   public TypeOracle() {
     // Always create the default package.
     //
@@ -320,25 +320,65 @@
    * the same arguments return the same object.
    * 
    * @param genericType a generic base class
+   * @param enclosingType
+   * @param typeArgs the type arguments bound to the specified generic type
+   * @return a type object representing this particular binding of type
+   *         arguments to the specified generic
+   */
+  public JParameterizedType getParameterizedType(JGenericType genericType,
+      JClassType enclosingType, JClassType[] typeArgs) {
+
+    if (genericType.isMemberType()) {
+      if (genericType.getEnclosingType().isGenericType() != null
+          && enclosingType.isParameterized() == null
+          && enclosingType.isRawType() == null) {
+        throw new IllegalArgumentException(
+            "enclosingType needs to be a parameterized type or a raw type");
+      }
+    }
+
+    // TODO: validate that the type arguments satisfy the generic type parameter
+    // bounds if any were specified
+
+    // Uses the generated string signature to intern parameterized types.
+    //
+    JParameterizedType parameterized = new JParameterizedType(genericType,
+        enclosingType, typeArgs);
+
+    // TODO: parameterized qualified source name does not account for the type
+    // args of the enclosing type
+    String sig = parameterized.getParameterizedQualifiedSourceName();
+    List<JParameterizedType> candidates = parameterizedTypes.get(sig);
+    if (candidates == null) {
+      candidates = new ArrayList<JParameterizedType>();
+      parameterizedTypes.put(sig, candidates);
+    } else {
+      for (JParameterizedType candidate : candidates) {
+        if (candidate.hasTypeArgs(typeArgs)) {
+          return candidate;
+        }
+      }
+    }
+
+    candidates.add(parameterized);
+
+    return parameterized;
+  }
+
+  /**
+   * Gets the parameterized type object that represents the combination of a
+   * specified raw type and a set of type arguments. The returned type always
+   * has a stable identity so as to guarantee that all calls to this method with
+   * the same arguments return the same object.
+   * 
+   * @param genericType a generic base class
    * @param typeArgs the type arguments bound to the specified generic type
    * @return a type object representing this particular binding of type
    *         arguments to the specified generic
    */
   public JParameterizedType getParameterizedType(JGenericType genericType,
       JClassType[] typeArgs) {
-    // Uses the generated string signature to intern parameterized types.
-    //
-    JParameterizedType parameterized = new JParameterizedType(genericType);
-    for (int i = 0; i < typeArgs.length; i++) {
-      parameterized.addTypeArg(typeArgs[i]);
-    }
-    String sig = parameterized.getParameterizedQualifiedSourceName();
-    JParameterizedType existing = parameterizedTypes.get(sig);
-    if (existing == null) {
-      parameterizedTypes.put(sig, parameterized);
-      existing = parameterized;
-    }
-    return existing;
+    return getParameterizedType(genericType, null, typeArgs);
   }
 
   public long getReloadCount() {
@@ -404,6 +444,26 @@
     }
   }
 
+  public JWildcardType getWildcardType(JBound bounds) {
+    JWildcardType wildcardType = new JWildcardType(bounds);
+    String sig = wildcardType.getQualifiedSourceName();
+    List<JWildcardType> candidates = wildcardTypes.get(sig);
+    if (candidates == null) {
+      candidates = new ArrayList<JWildcardType>();
+      wildcardTypes.put(sig, candidates);
+    } else {
+      for (JWildcardType candidate : candidates) {
+        if (candidate.hasBounds(bounds)) {
+          return candidate;
+        }
+      }
+    }
+
+    candidates.add(wildcardType);
+
+    return wildcardType;
+  }
+
   /**
    * Parses the string form of a type to produce the corresponding type object.
    * The types that can be parsed include primitives, class and interface names,
@@ -500,7 +560,7 @@
 
   /**
    * Note, this method is called reflectively from the
-   * {@link CacheManager#invalidateOnRefresh(TypeOracle)}
+   * {@link CacheManager#invalidateOnRefresh(TypeOracle)}.
    * 
    * @param cup compilation unit whose types will be invalidated
    */
@@ -878,18 +938,21 @@
 
   /**
    * Remove any parameterized type that was invalidated because either its raw
-   * type or any one of its type arguements was invalidated.
+   * type or any one of its type arguments was invalidated.
    * 
    * @param invalidTypes set of types known to have been invalidated
    */
   private void removeInvalidatedParameterizedTypes(Set<JClassType> invalidTypes) {
-    Iterator<JParameterizedType> iter = parameterizedTypes.values().iterator();
+    Iterator<List<JParameterizedType>> listIterator = parameterizedTypes.values().iterator();
 
-    while (iter.hasNext()) {
-      JType type = iter.next();
-
-      if (isInvalidatedTypeRecursive(type, invalidTypes)) {
-        iter.remove();
+    while (listIterator.hasNext()) {
+      List<JParameterizedType> list = listIterator.next();
+      Iterator<JParameterizedType> typeIterator = list.iterator();
+      while (typeIterator.hasNext()) {
+        JType type = typeIterator.next();
+        if (isInvalidatedTypeRecursive(type, invalidTypes)) {
+          typeIterator.remove();
+        }
       }
     }
   }
diff --git a/dev/core/src/com/google/gwt/dev/jdt/AnnotationProxyFactory.java b/dev/core/src/com/google/gwt/dev/jdt/AnnotationProxyFactory.java
index 79b2f4e..c93536a 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/AnnotationProxyFactory.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/AnnotationProxyFactory.java
@@ -186,6 +186,7 @@
       // See if the value was explicitly declared
       Object value = identifierToValue.get(name);
       if (value != null) {
+        assert (method.getReturnType().isAssignableFrom(value.getClass()));
         return value;
       }
 
@@ -197,6 +198,11 @@
         return annotationMethod.getDefaultValue();
       }
 
+      if (method.getDeclaringClass() == Annotation.class
+          && "annotationType".equals(method.getName())) {
+        return annotationClass;
+      }
+
       /*
        * Maybe it's an Object method, just delegate to myself.
        */
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 904f5d5..4e84999 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
@@ -26,8 +26,11 @@
 import com.google.gwt.core.ext.typeinfo.JBound;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JConstructor;
+import com.google.gwt.core.ext.typeinfo.JEnumConstant;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
 import com.google.gwt.core.ext.typeinfo.JField;
 import com.google.gwt.core.ext.typeinfo.JGenericType;
+import com.google.gwt.core.ext.typeinfo.JLowerBound;
 import com.google.gwt.core.ext.typeinfo.JMethod;
 import com.google.gwt.core.ext.typeinfo.JPackage;
 import com.google.gwt.core.ext.typeinfo.JParameter;
@@ -36,8 +39,9 @@
 import com.google.gwt.core.ext.typeinfo.JRealClassType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.JTypeParameter;
-import com.google.gwt.core.ext.typeinfo.JWildcardType;
+import com.google.gwt.core.ext.typeinfo.JUpperBound;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.jdt.CacheManager.Mapper;
 import com.google.gwt.dev.util.Empty;
 import com.google.gwt.dev.util.Util;
 
@@ -46,6 +50,7 @@
 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.AbstractVariableDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.Annotation;
 import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.Argument;
@@ -68,9 +73,8 @@
 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.ParameterizedQualifiedTypeReference;
-import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
 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;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
@@ -101,6 +105,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Array;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -112,7 +117,7 @@
 import java.util.regex.Pattern;
 
 /**
- * Builds a {@link com.google.gwt.dev.typeinfo.TypeOracle} from a set of
+ * Builds a {@link com.google.gwt.core.ext.typeinfo.TypeOracle} from a set of
  * compilation units.
  * <p>
  * For example,
@@ -128,7 +133,7 @@
  * </pre>
  */
 public class TypeOracleBuilder {
-
+  private static final JClassType[] NO_JCLASSES = new JClassType[0];
   private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s");
 
   public static String computeBinaryClassName(JType type) {
@@ -283,6 +288,22 @@
     return "package-info".equals(qname);
   }
 
+  private static boolean maybeGeneric(TypeDeclaration typeDecl) {
+    if (typeDecl.typeParameters != null) {
+      // Definitely generic since it has type parameters
+      return true;
+    }
+
+    if (!typeDecl.binding.isStatic() && typeDecl.enclosingType != null) {
+      // Consider typeDecl to be generic if it is not static and its enclosing
+      // type is generic
+      return maybeGeneric(typeDecl.enclosingType);
+    }
+
+    return false;
+  }
+
+  // TODO: move into the Annotations class or create an AnnotationsUtil class?
   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>();
   }
@@ -498,46 +519,6 @@
 
       cud.traverse(new ASTVisitor() {
         @Override
-        public boolean visit(
-            ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference,
-            BlockScope scope) {
-          ParameterizedTypeBinding jparameterizedType = (ParameterizedTypeBinding) parameterizedQualifiedTypeReference.resolvedType;
-          processParameterizedType(jparameterizedType);
-          return true; // do nothing by default, keep traversing
-        }
-
-        @Override
-        public boolean visit(
-            ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference,
-            ClassScope scope) {
-          ParameterizedTypeBinding jparameterizedType = (ParameterizedTypeBinding) parameterizedQualifiedTypeReference.resolvedType;
-          processParameterizedType(jparameterizedType);
-          return true; // do nothing by default, keep traversing
-        }
-
-        @Override
-        public boolean visit(
-            ParameterizedSingleTypeReference parameterizedSingleTypeReference,
-            BlockScope scope) {
-          if (parameterizedSingleTypeReference.resolvedType instanceof ParameterizedTypeBinding) {
-            ParameterizedTypeBinding jparameterizedType = (ParameterizedTypeBinding) parameterizedSingleTypeReference.resolvedType;
-            processParameterizedType(jparameterizedType);
-          } else if (parameterizedSingleTypeReference.resolvedType instanceof SourceTypeBinding) {
-            // nothing to do
-          } else {
-            assert false;
-          }
-          return true; // do nothing by default, keep traversing
-        }
-
-        @Override
-        public boolean visit(
-            ParameterizedSingleTypeReference parameterizedSingleTypeReference,
-            ClassScope scope) {
-          return true; // do nothing by default, keep traversing
-        }
-
-        @Override
         public boolean visit(TypeDeclaration typeDecl, BlockScope scope) {
           JClassType enclosingType = identityMapper.get(typeDecl.binding.enclosingType());
           processType(typeDecl, enclosingType, true);
@@ -609,20 +590,37 @@
     return oracle;
   }
 
-  private JBound createTypeVariableBounds(TreeLogger logger,
+  private JUpperBound createTypeVariableBounds(TreeLogger logger,
       TypeVariableBinding tvBinding) {
-    TypeBinding firstBound = tvBinding.firstBound;
-    if (firstBound == null) {
-      firstBound = tvBinding.superclass;
+    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);
     }
-    JClassType firstBoundType = (JClassType) resolveType(logger, firstBound);
-    JBound bounds = new JBound(firstBoundType);
-    for (ReferenceBinding ref : tvBinding.superInterfaces()) {
-      JClassType bound = (JClassType) resolveType(logger, ref);
-      assert (bound.isInterface() != null);
-      bounds.addUpperBound(bound);
+
+    List<JClassType> bounds = new ArrayList<JClassType>();
+    JClassType firstBound = (JClassType) resolveType(logger, jfirstBound);
+    if (firstBound.isClass() != null) {
+      bounds.add(firstBound);
     }
-    return bounds;
+
+    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,
@@ -650,6 +648,26 @@
         return null;
       }
 
+      try {
+        Method method = clazz.getMethod(identifier, new Class[0]);
+        Class<?> expectedClass = method.getReturnType();
+        Class<? extends Object> actualClass = value.getClass();
+        if (expectedClass.isArray() && !actualClass.isArray()) {
+          /*
+           * JSL3 Section 9.7 single element annotations can skip the curly
+           * braces; means we do not get an array
+           */
+          assert (expression instanceof SingleMemberAnnotation);
+          Object array = Array.newInstance(expectedClass.getComponentType(), 1);
+          Array.set(array, 0, value);
+          value = array;
+        }
+      } catch (SecurityException e) {
+        return null;
+      } catch (NoSuchMethodException e) {
+        return null;
+      }
+
       identifierToValue.put(identifier, value);
     }
 
@@ -796,6 +814,11 @@
   }
 
   private String getQualifiedName(ReferenceBinding binding) {
+    if (binding.isMemberType()) {
+      return String.valueOf(CharOperation.concat(
+          binding.enclosingType().readableName(), binding.sourceName, '.'));
+    }
+
     return CharOperation.toString(binding.compoundName);
   }
 
@@ -812,15 +835,6 @@
   }
 
   /**
-   * Maps a ParameterizedTypeBinding into a JParameterizedType.
-   */
-  private void processParameterizedType(ParameterizedTypeBinding binding) {
-    TypeOracle oracle = cacheManager.getTypeOracle();
-    // TODO Get the JParameterized type for the current binding, fill it out
-    // as you would a JClassType.
-  }
-
-  /**
    * Maps a TypeDeclaration into a JClassType.
    */
   private void processType(TypeDeclaration typeDecl, JClassType enclosingType,
@@ -838,7 +852,7 @@
        */
       return;
     }
-
+ 
     String qname;
     String jclassName;
     if (binding instanceof LocalTypeBinding) {
@@ -854,10 +868,10 @@
       qname = getQualifiedName(binding);
       jclassName = getSimpleName(typeDecl);
     }
+
     if (oracle.findType(qname) != null) {
-      // The oracle already knew about this type.
-      // Don't re-add it.
-      //
+      // TODO: gname of generic types includes the type arguments, I think that
+      // this would cause inner classes to not be found. 
       return;
     }
 
@@ -876,22 +890,17 @@
     if (jclassIsAnnonation) {
       type = new JAnnotationType(oracle, cup, pkg, enclosingType, isLocalType,
           jclassName, declStart, declEnd, bodyStart, bodyEnd, jclassIsIntf);
-    } else if (typeDecl.typeParameters != null
-        && typeDecl.typeParameters.length > 0) {
+    } else if (maybeGeneric(typeDecl)) {
       type = new JGenericType(oracle, cup, pkg, enclosingType, isLocalType,
           jclassName, declStart, declEnd, bodyStart, bodyEnd, jclassIsIntf);
-      for (TypeParameter jtypeParameter : typeDecl.typeParameters) {
-        JTypeParameter typeParameter = new JTypeParameter(
-            String.valueOf(jtypeParameter.name), (JGenericType) type);
-        cacheManager.setTypeForBinding(jtypeParameter.binding, typeParameter);
-      }
+    } else if (binding.isEnum()) {
+      type = new JEnumType(oracle, cup, pkg, enclosingType, isLocalType,
+          jclassName, declStart, declEnd, bodyStart, bodyEnd, jclassIsIntf);
     } else {
       type = new JRealClassType(oracle, cup, pkg, enclosingType, isLocalType,
           jclassName, declStart, declEnd, bodyStart, bodyEnd, jclassIsIntf);
     }
 
-    // TODO: enums?
-
     cacheManager.setTypeForBinding(binding, type);
   }
 
@@ -945,7 +954,14 @@
     }
 
     String name = String.valueOf(jfield.name);
-    JField field = new JField(enclosingType, name, declaredAnnotations);
+    JField field;
+    if (jfield.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
+      assert (enclosingType.isEnum() != null);
+      field = new JEnumConstant(enclosingType, name, declaredAnnotations,
+          jfield.binding.original().id);
+    } else {
+      field = new JField(enclosingType, name, declaredAnnotations);
+    }
 
     // Get modifiers.
     //
@@ -1014,6 +1030,12 @@
     if (jmethod.isConstructor()) {
       method = new JConstructor(enclosingType, name, declStart, declEnd,
           bodyStart, bodyEnd, declaredAnnotations);
+
+      // Resolve the type parameters, since they may be used as the return type,
+      // etc.
+      if (!resolveTypeParameters(logger, method, jmethod.typeParameters())) {
+        return false;
+      }
     } else {
       if (jmethod.isAnnotationMethod()) {
         AnnotationMethodDeclaration annotationMethod = (AnnotationMethodDeclaration) jmethod;
@@ -1029,25 +1051,12 @@
             bodyStart, bodyEnd, declaredAnnotations);
       }
 
-      // Add declared type parameters.
-      if (jmethod.typeParameters() != null) {
-        for (TypeParameter jtypeParameter : jmethod.typeParameters()) {
-          JTypeParameter typeParameter = new JTypeParameter(
-              String.valueOf(jtypeParameter.name), method);
-          cacheManager.getIdentityMapper().put(jtypeParameter.binding,
-              typeParameter);
-        }
-        JTypeParameter[] typeParameters = method.getTypeParameters();
-        for (TypeParameter jtypeParameter : jmethod.typeParameters()) {
-          JTypeParameter typeParameter = (JTypeParameter) cacheManager.getIdentityMapper().get(
-              jtypeParameter.binding);
-          JBound bounds = createTypeVariableBounds(logger,
-              jtypeParameter.binding);
-          typeParameter.setBounds(bounds);
-        }
+      // Resolve the type parameters, since they may be used as the return type,
+      // etc.
+      if (!resolveTypeParameters(logger, method, jmethod.typeParameters())) {
+        return false;
       }
 
-      // Add the return type if necessary.
       TypeBinding jreturnType = ((MethodDeclaration) jmethod).returnType.resolvedType;
       JType returnType = resolveType(logger, jreturnType);
       if (returnType == null) {
@@ -1241,12 +1250,13 @@
       // First check the type oracle to prefer type identity with the type
       // oracle we're assimilating into.
       //
-      String typeName = String.valueOf(referenceBinding.readableName());
+      String typeName = getQualifiedName(referenceBinding);
       JType resolvedType = oracle.findType(typeName);
       if (resolvedType == null) {
         // Otherwise, it should be something we've mapped during this build.
         resolvedType = cacheManager.getTypeForBinding(referenceBinding);
       }
+
       if (resolvedType != null) {
         if (binding instanceof RawTypeBinding) {
           // Use the raw type instead of the generic type.
@@ -1284,78 +1294,104 @@
     // Check for parameterized.
     if (binding instanceof ParameterizedTypeBinding) {
       ParameterizedTypeBinding ptBinding = (ParameterizedTypeBinding) binding;
-      JClassType[] typeArguments = new JClassType[ptBinding.arguments.length];
+
+      /*
+       * NOTE: it is possible for ParameterizedTypeBinding.arguments to be null.
+       * This can happen if a generic class has a non-static, non-generic, inner
+       * class that references a TypeParameter from its enclosing generic type.
+       * You would think that typeVariables() would do the right thing but it
+       * does not.
+       */
+      TypeBinding[] arguments = ptBinding.arguments;
+      int nArguments = arguments != null ? arguments.length : 0;
+      JClassType[] typeArguments = new JClassType[nArguments];
+      boolean failed = false;
       for (int i = 0; i < typeArguments.length; ++i) {
-        typeArguments[i] = (JClassType) resolveType(logger,
-            ptBinding.arguments[i]);
+        typeArguments[i] = (JClassType) resolveType(logger, arguments[i]);
+        if (typeArguments[i] == null) {
+          failed = true;
+        }
       }
 
-      JGenericType baseType = (JGenericType) resolveType(logger, ptBinding.type);
-      return oracle.getParameterizedType(baseType, typeArguments);
+      JClassType enclosingType = null;
+      if (ptBinding.enclosingType() != null) {
+        enclosingType = (JClassType) resolveType(logger,
+            ptBinding.enclosingType());
+        if (enclosingType == null) {
+          failed = true;
+        }
+      }
+
+      /*
+       * NOTE: In the case where a generic type has a nested, non-static,
+       * non-generic type. The type for the binding will not be a generic type.
+       */
+      JType resolveType = resolveType(logger, ptBinding.type);
+      if (resolveType == null) {
+        failed = true;
+      }
+
+      if (!failed) {
+        JEnumType enumType = resolveType.isEnum();
+        if (enumType != null) {
+          /*
+           * An enumerated type that is nested within a generic type is referenced
+           * via a parameterized type by JDT. In this case we just return the
+           * enumerated type and don't treat it as a parameterized type since
+           * enumerations cannot be parameterized and are implicitly static.
+           */
+          return enumType;
+        }
+        
+        JGenericType genericType = (JGenericType) resolveType;
+        return oracle.getParameterizedType(genericType, enclosingType,
+            typeArguments);
+      } else {
+        // Fall-through to failure
+      }
     }
 
     if (binding instanceof TypeVariableBinding) {
       TypeVariableBinding tvBinding = (TypeVariableBinding) binding;
       JTypeParameter typeParameter = (JTypeParameter) cacheManager.getTypeForBinding(tvBinding);
       assert (typeParameter != null);
-
-      if (typeParameter.getBounds() == null) {
-        // Setup a dummy bound to prevent recursion
-        typeParameter.setBounds(new JBound(null));
-        JBound bounds = createTypeVariableBounds(logger, tvBinding);
-        typeParameter.setBounds(bounds);
-      }
       return typeParameter;
     }
 
     if (binding instanceof WildcardBinding) {
       WildcardBinding wcBinding = (WildcardBinding) binding;
+
+      assert (wcBinding.otherBounds == null);
+
       JBound bounds;
       switch (wcBinding.boundKind) {
         case Wildcard.EXTENDS: {
-          JClassType firstBoundType = (JClassType) resolveType(logger,
+          assert (wcBinding.bound != null);
+          JClassType upperBound = (JClassType) resolveType(logger,
               wcBinding.bound);
-          bounds = new JBound(firstBoundType);
-          if (wcBinding.otherBounds != null) {
-            for (TypeBinding bound : wcBinding.otherBounds) {
-              JClassType boundType = (JClassType) resolveType(logger, bound);
-              bounds.addUpperBound(boundType);
-            }
-          }
+          bounds = new JUpperBound(new JClassType[] {upperBound});
         }
           break;
         case Wildcard.SUPER: {
-          // TODO: verify
-          JClassType firstBoundType = (JClassType) resolveType(logger,
-              wcBinding.erasure());
-          assert (firstBoundType.getQualifiedSourceName().equals("java.lang.Object"));
-          bounds = new JBound(firstBoundType);
-
           assert (wcBinding.bound != null);
-          JClassType boundType = (JClassType) resolveType(logger,
+          JClassType lowerBound = (JClassType) resolveType(logger,
               wcBinding.bound);
-          bounds.addLowerBound(boundType);
-          if (wcBinding.otherBounds != null) {
-            for (TypeBinding bound : wcBinding.otherBounds) {
-              boundType = (JClassType) resolveType(logger, bound);
-              bounds.addLowerBound(boundType);
-            }
-          }
+          bounds = new JLowerBound(new JClassType[] {lowerBound});
         }
           break;
         case Wildcard.UNBOUND: {
-          JClassType firstBoundType = (JClassType) resolveType(logger,
+          JClassType upperBound = (JClassType) resolveType(logger,
               wcBinding.erasure());
-          assert (firstBoundType.getQualifiedSourceName().equals("java.lang.Object"));
-          bounds = new JBound(firstBoundType);
+          bounds = new JUpperBound(new JClassType[] {upperBound});
+          assert (bounds.getFirstBound().getQualifiedSourceName().equals("java.lang.Object"));
         }
           break;
         default:
           assert false : "WildcardBinding of unknown boundKind???";
           return null;
       }
-      JWildcardType wcType = new JWildcardType(bounds);
-      return wcType;
+
+      return oracle.getWildcardType(bounds);
     }
 
     // Log other cases we know about that don't make sense.
@@ -1411,9 +1447,11 @@
     }
     type.addAnnotations(declaredAnnotations);
 
-    // Resolve type parameters
-    List<JTypeParameter> typeParameters = new ArrayList<JTypeParameter>();
-    if (!resolveTypeParameters(logger, jclass.typeParameters, typeParameters)) {
+    // Resolve type parameters for generic types
+    JGenericType genericType = type.isGenericType();
+    if (genericType != null
+        && !resolveTypeParameters(logger, type.isGenericType(),
+            jclass.typeParameters)) {
       // Failed to resolve
       return false;
     }
@@ -1471,18 +1509,49 @@
   }
 
   private boolean resolveTypeParameter(TreeLogger logger,
-      TypeParameter jtypeParameter, List<JTypeParameter> typeParameters) {
-    JTypeParameter typeParameter = (JTypeParameter) cacheManager.getIdentityMapper().get(
-        jtypeParameter.binding);
-    typeParameters.add(typeParameter);
+      JAbstractMethod method, TypeParameter jtypeParameter, int ordinal) {
+    JTypeParameter typeParameter = new JTypeParameter(
+        String.valueOf(jtypeParameter.name), method, ordinal);
+    return resolveTypeParameter(logger, jtypeParameter.binding, typeParameter);
+  }
+
+  private boolean resolveTypeParameter(TreeLogger logger,
+      JGenericType genericType, TypeParameter jtypeParameter, int ordinal) {
+    JTypeParameter typeParameter = new JTypeParameter(
+        String.valueOf(jtypeParameter.name), genericType, ordinal);
+    return resolveTypeParameter(logger, jtypeParameter.binding, typeParameter);
+  }
+
+  private boolean resolveTypeParameter(TreeLogger logger,
+      TypeVariableBinding binding, JTypeParameter typeParameter) {
+    Mapper identityMapper = cacheManager.getIdentityMapper();
+    assert (identityMapper.get(binding) == null);
+
+    identityMapper.put(binding, typeParameter);
+
+    JBound bounds = createTypeVariableBounds(logger, binding);
+    typeParameter.setBounds(bounds);
+
     return true;
   }
 
   private boolean resolveTypeParameters(TreeLogger logger,
-      TypeParameter[] jtypeParameters, List<JTypeParameter> typeParameters) {
+      JAbstractMethod method, TypeParameter[] jtypeParameters) {
     if (jtypeParameters != null) {
       for (int i = 0; i < jtypeParameters.length; ++i) {
-        if (!resolveTypeParameter(logger, jtypeParameters[i], typeParameters)) {
+        if (!resolveTypeParameter(logger, method, jtypeParameters[i], i)) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  private boolean resolveTypeParameters(TreeLogger logger,
+      JGenericType genericType, TypeParameter[] jtypeParameters) {
+    if (jtypeParameters != null) {
+      for (int i = 0; i < jtypeParameters.length; ++i) {
+        if (!resolveTypeParameter(logger, genericType, jtypeParameters[i], i)) {
           return false;
         }
       }
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JArrayTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JArrayTypeTest.java
new file mode 100644
index 0000000..8f7c829
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JArrayTypeTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.CA;
+import com.google.gwt.core.ext.typeinfo.test.CB;
+import com.google.gwt.core.ext.typeinfo.test.MyCustomList;
+import com.google.gwt.core.ext.typeinfo.test.MyList;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link JArrayType}.
+ */
+public class JArrayTypeTest extends TestCase {
+  /*
+   * Returns int[][]
+   */
+  private static JArrayType getIntArray(TypeOracle oracle) {
+    return oracle.getArrayType(getIntVector(oracle));
+  }
+  /*
+   * Returns int[].
+   */
+  private static JArrayType getIntVector(TypeOracle oracle) {
+    return oracle.getArrayType(JPrimitiveType.INT);
+  }
+
+  /*
+   * Returns Object[][]
+   */
+  private static JArrayType getObjectArray(TypeOracle oracle) {
+    return oracle.getArrayType(getObjectVector(oracle));
+  }
+
+  /*
+   * Returns Object[].
+   */
+  private static JArrayType getObjectVector(TypeOracle oracle) {
+    return oracle.getArrayType(oracle.getJavaLangObject());
+  }
+
+  private final boolean logToConsole = false;
+
+  private final ModuleContext moduleContext = new ModuleContext(logToConsole
+      ? new PrintWriterTreeLogger() : TreeLogger.NULL,
+      "com.google.gwt.core.ext.typeinfo.TypeOracleTest");
+
+  public JArrayTypeTest() throws UnableToCompleteException {
+  }
+
+  public void testGetSubtypes() throws NotFoundException {
+    JArrayType testArrayType = getTestArrayType();
+
+    JArrayType[] arraySubtypes = testArrayType.getSubtypes();
+    assertEquals(2, arraySubtypes.length);
+
+    for (int i = 0; i < arraySubtypes.length; ++i) {
+      assertTrue(testArrayType.isAssignableFrom(arraySubtypes[i]));
+    }
+  }
+
+  public void testGetSuperclass() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JArrayType intVector = getIntVector(oracle);
+    JArrayType intArray = getIntArray(oracle);
+    JArrayType objVector = getObjectVector(oracle);
+    JArrayType objArray = getObjectArray(oracle);
+
+    // Check that superclass of int[][] is Object[]
+    assertEquals(objVector, intArray.getSuperclass());
+
+    // Check that superclass of int[] is Object
+    assertEquals(oracle.getJavaLangObject(), intVector.getSuperclass());
+
+    // CA
+    JClassType caType = oracle.getType(CA.class.getCanonicalName());
+
+    // CA[]
+    JArrayType caVector = oracle.getArrayType(caType);
+
+    // CA[][]
+    JArrayType caArray = oracle.getArrayType(caVector);
+
+    // CB
+    JClassType cbType = oracle.getType(CB.class.getCanonicalName());
+
+    // CB[]
+    JArrayType cbVector = oracle.getArrayType(cbType);
+
+    // CB[][]
+    JArrayType cbArray = oracle.getArrayType(cbVector);
+
+    // Check that CB[][] has a supertype of CA[][]
+    JClassType cbArraySuper = cbArray.getSuperclass();
+    assertEquals(caArray, cbArraySuper);
+
+    // Check that CA[][] has a supertype of Object[][]
+    JClassType cbArraySuperSuper = cbArraySuper.getSuperclass();
+    assertEquals(objArray, cbArraySuperSuper);
+
+    // Check that Object[][] has supertype of Object[]
+    JClassType cbArraySuperSuperSuper = cbArraySuperSuper.getSuperclass();
+    assertEquals(objVector, cbArraySuperSuperSuper);
+
+    // Check that Object[] has supertype of Object
+    JClassType cbArraySuperSuperSuperSuper = cbArraySuperSuperSuper.getSuperclass();
+    assertEquals(oracle.getJavaLangObject(), cbArraySuperSuperSuperSuper);
+  }
+
+  public void testIsAssignableFrom() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    
+    JArrayType intVector = getIntVector(oracle);
+    JArrayType intArray = getIntArray(oracle);
+    JArrayType objVector = getObjectVector(oracle);
+    JArrayType objArray = getObjectArray(oracle);
+
+    // Object[] is not assignable from int[]
+    assertFalse(objVector.isAssignableFrom(intVector));
+    assertFalse(intVector.isAssignableFrom(objVector));
+
+    // Object[] is assignable from int[][]
+    assertTrue(objVector.isAssignableFrom(intArray));
+
+    // Object[] is assignable from Object[][]
+    assertTrue(objVector.isAssignableFrom(objArray));
+
+    // int[] is assignable from int[]
+    assertTrue(intVector.isAssignableFrom(intVector));
+    
+    // int[] is assignable from int[][]
+    assertFalse(intArray.isAssignableFrom(intVector));
+    
+    JClassType testSubtype = oracle.getType(MyCustomList.class.getName());
+    
+    // MyCustomList[]
+    JArrayType testArraySubtype = oracle.getArrayType(testSubtype);
+
+    // MyList[] is assignable from MyCustomList[]
+    assertTrue(getTestArrayType().isAssignableFrom(testArraySubtype));
+
+    // MyCustomList[]
+    JArrayType testVectorSubtype = oracle.getArrayType(testArraySubtype);
+    
+    // MyList[] is not assignable from MyCustomList[][]    
+    assertFalse(getTestArrayType().isAssignableFrom(testVectorSubtype));
+
+    // MyCustomList[] is not assignable from MyList[]
+    assertFalse(testArraySubtype.isAssignableFrom(getTestArrayType()));
+  }
+
+  public void testIsAssignableTo() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType testSubtype = oracle.getType(MyCustomList.class.getName());
+    JArrayType testArraySubtype = oracle.getArrayType(testSubtype);
+
+    // MyCustomList[] is assignable to MyList[]
+    assertTrue(testArraySubtype.isAssignableTo(getTestArrayType()));
+    
+    // MyCustomList[] is assignable to Object
+    assertTrue(testArraySubtype.isAssignableTo(oracle.getJavaLangObject()));
+    
+    // MyList[] is not assignable to MyCustomList[]
+    assertFalse(getTestArrayType().isAssignableTo(testArraySubtype));
+    
+    // MyList[] is assignable to MyCustomList[]
+    assertFalse(getTestArrayType().isAssignableTo(testArraySubtype));
+
+  }
+
+  private JArrayType getTestArrayType() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JGenericType genericComponentType = oracle.getType(MyList.class.getName()).isGenericType();
+
+    return oracle.getArrayType(genericComponentType);
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JClassTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JClassTypeTest.java
index 26a3cf8..462da59 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JClassTypeTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JClassTypeTest.java
@@ -20,6 +20,7 @@
 import com.google.gwt.dev.jdt.StaticCompilationUnitProvider;
 import com.google.gwt.dev.jdt.TypeOracleBuilder;
 import com.google.gwt.dev.jdt.URLCompilationUnitProvider;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
 
 import junit.framework.TestCase;
 
@@ -33,11 +34,18 @@
  * Tests related to JClassType. See individual test methods to details.
  */
 public class JClassTypeTest extends TestCase {
+  private final boolean logToConsole = false;
+  private final ModuleContext moduleContext = new ModuleContext(logToConsole
+      ? new PrintWriterTreeLogger() : TreeLogger.NULL,
+      "com.google.gwt.core.ext.typeinfo.TypeOracleTest");
 
-  public void testGetOverridableMethods() throws UnableToCompleteException,
-      TypeOracleException {
+  public JClassTypeTest() throws UnableToCompleteException {
+  }
+
+  public void testGetOverridableMethods() throws TypeOracleException {
     TreeLogger logger = TreeLogger.NULL;
-    TypeOracle typeOracle = buildOracleFromTestPackage(logger);
+    TypeOracle typeOracle = moduleContext.getOracle();
+    // TypeOracle typeOracle = buildOracleFromTestPackage(logger);
 
     String[] noParams = new String[0];
     String[] intObjectParams = new String[] {"int", "java.lang.Object"};
@@ -224,7 +232,7 @@
       // No files found.
       return;
     }
-    
+
     for (int i = 0; i < files.length; i++) {
       File file = files[i];
       if (file.isFile()) {
@@ -301,54 +309,4 @@
       }
     }
   }
-
-  /**
-   * Looks in the package containing this class and uses it as an anchor for
-   * including all the classes under the "test" subpackage.
-   * 
-   * TODO: This is not generalized yet, but it could be made reusable and put
-   * into TypeOracleBuilder.
-   * 
-   * @return
-   * @throws URISyntaxException
-   * @throws UnableToCompleteException
-   * @throws MalformedURLException
-   */
-  private TypeOracle buildOracleFromTestPackage(TreeLogger logger)
-      throws UnableToCompleteException {
-    Throwable caught;
-    try {
-      // Find the source path using this class as an anchor.
-      String className = getClass().getName();
-      String resName = className.replace('.', '/') + ".java";
-      URL location = getClass().getClassLoader().getResource(resName);
-      assertNotNull("Ensure that source is in classpath for: " + resName,
-          location);
-      String absPath = new File(new URI(location.toString())).getAbsolutePath();
-      int sourcePathEntryLen = absPath.length() - resName.length();
-      File sourcePathEntry = new File(absPath.substring(0, sourcePathEntryLen));
-
-      // Determine the starting package name.
-      int lastDot = className.lastIndexOf('.');
-      String pkgName = (lastDot < 0 ? "test" : className.substring(0, lastDot)
-          + ".test");
-
-      // Create the builder to be filled in.
-      TypeOracleBuilder builder = new TypeOracleBuilder();
-
-      // Add java.lang.Object.
-      builder.addCompilationUnit(new StaticCompilationUnitProvider("java.lang",
-          "Object", "package java.lang; public class Object { }".toCharArray()));
-
-      // Recursively walk the directories.
-      addCompilationUnitsInPath(builder, sourcePathEntry, pkgName);
-      return builder.build(logger);
-    } catch (URISyntaxException e) {
-      caught = e;
-    } catch (MalformedURLException e) {
-      caught = e;
-    }
-    logger.log(TreeLogger.ERROR, "Failed to build type oracle", caught);
-    throw new UnableToCompleteException();
-  }
 }
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTest.java
new file mode 100644
index 0000000..f36935d
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTest.java
@@ -0,0 +1,814 @@
+/*
+ * 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.util.Arrays;
+
+/**
+ * Base test cases for all {@link JDelegatingClassType}s.
+ */
+public abstract class JDelegatingClassTypeTest extends TestCase {
+
+  protected static void assertArraysEqual(Object[] expected, Object[] actual) {
+    assertTrue("Expected: \n" + Arrays.toString(expected) + ",\n Actual: \n"
+        + Arrays.toString(actual), Arrays.equals(expected, actual));
+  }
+
+  protected static void validateAbstractMethodSubstitution(
+      JAbstractMethod preSubMethod, JAbstractMethod postSubMethod,
+      Substitution substitution) {
+
+    assertEquals(preSubMethod.getName(), postSubMethod.getName());
+
+    assertEquals(preSubMethod.getModifierBits(),
+        postSubMethod.getModifierBits());
+
+    validateAnnotations(preSubMethod, postSubMethod);
+
+    validateMetaData(preSubMethod, postSubMethod);
+
+    JParameter[] preSubParams = preSubMethod.getParameters();
+    JParameter[] postSubParams = postSubMethod.getParameters();
+
+    assertEquals(preSubParams.length, postSubParams.length);
+
+    for (int j = 0; j < preSubParams.length; ++j) {
+      JParameter preSubParam = preSubParams[j];
+      JParameter postSubParam = postSubParams[j];
+
+      validateAnnotations(preSubParam, postSubParam);
+      validateMetaData(preSubParam, postSubParam);
+
+      assertEquals(substitution.getSubstitution(preSubParam.getType()),
+          postSubParam.getType());
+    }
+  }
+
+  protected static void validateAnnotations(HasAnnotations ha1,
+      HasAnnotations ha2) {
+    assertArraysEqual(ha1.getAnnotations(), ha2.getAnnotations());
+  }
+
+  protected static void validateConstructorSubstitutions(
+      JClassType preSubstitution, JClassType postSubstituion,
+      Substitution substitution) {
+    // Check the constructors
+    JConstructor[] preSubCtors = preSubstitution.getConstructors();
+    JConstructor[] postSubCtors = postSubstituion.getConstructors();
+    assertEquals(preSubCtors.length, postSubCtors.length);
+    for (int i = 0; i < preSubCtors.length; ++i) {
+      validateAbstractMethodSubstitution(preSubCtors[i], postSubCtors[i],
+          substitution);
+    }
+  }
+
+  protected static void validateDeclaredAnnotations(HasAnnotations ha1,
+      HasAnnotations ha2) {
+    assertArraysEqual(ha1.getDeclaredAnnotations(),
+        ha2.getDeclaredAnnotations());
+  }
+
+  protected static void validateEquals(TypeOracle oracle,
+      JClassType[] expectedTypes, JClassType actualTypes[]) {
+    oracle.sort(expectedTypes);
+    oracle.sort(actualTypes);
+
+    assertArraysEqual(expectedTypes, actualTypes);
+  }
+
+  protected static void validateFieldSubstitutions(JClassType preSubstitution,
+      JClassType postSubstituion, Substitution substitution) {
+    // Check the fields
+    JField[] preSubfields = preSubstitution.getFields();
+    JField[] postSubFields = postSubstituion.getFields();
+    assertEquals(preSubfields.length, postSubFields.length);
+    for (int i = 0; i < preSubfields.length; ++i) {
+      JField postSubField = postSubstituion.getField(preSubfields[i].getName());
+      assertNotNull(postSubField);
+      assertEquals(substitution.getSubstitution(preSubfields[i].getType()),
+          postSubField.getType());
+    }
+  }
+
+  protected static void validateFindConstructor(JClassType preSubstitution,
+      JClassType postSubstitution, Substitution substitution) {
+
+    JConstructor[] constructors = preSubstitution.getConstructors();
+    for (JConstructor constructor : constructors) {
+      JParameter[] params = constructor.getParameters();
+      JType[] paramTypes = new JType[params.length];
+
+      for (int i = 0; i < params.length; ++i) {
+        paramTypes[i] = substitution.getSubstitution(params[i].getType());
+      }
+
+      assertNotNull(postSubstitution.findConstructor(paramTypes));
+    }
+  }
+
+  /**
+   * 
+   */
+  protected static void validateFindField(JClassType preSubstitution,
+      JClassType postSubstitution) {
+    JField[] fields = preSubstitution.getFields();
+    for (JField field : fields) {
+      assertNotNull(postSubstitution.findField(field.getName()));
+    }
+  }
+
+  protected static void validateFindMethod(JClassType preSubstitution,
+      JClassType postSubstitution, Substitution substitution) {
+
+    JMethod[] methods = preSubstitution.getMethods();
+    for (JMethod method : methods) {
+      JParameter[] params = method.getParameters();
+      JType[] paramTypes = new JType[params.length];
+
+      for (int i = 0; i < params.length; ++i) {
+        paramTypes[i] = substitution.getSubstitution(params[i].getType());
+      }
+
+      assertNotNull(postSubstitution.findMethod(method.getName(), paramTypes));
+    }
+  }
+
+  protected static void validateGetConstructor(JClassType preSubstitution,
+      JClassType postSubstitution, Substitution substitution)
+      throws NotFoundException {
+
+    JConstructor[] constructors = preSubstitution.getConstructors();
+    for (JConstructor constructor : constructors) {
+      JParameter[] params = constructor.getParameters();
+      JType[] paramTypes = new JType[params.length];
+
+      for (int i = 0; i < params.length; ++i) {
+        paramTypes[i] = substitution.getSubstitution(params[i].getType());
+      }
+
+      assertNotNull(postSubstitution.getConstructor(paramTypes));
+    }
+  }
+
+  /**
+   * 
+   */
+  protected static void validateGetField(JClassType preSubstitution,
+      JClassType postSubstitution) {
+    JField[] fields = preSubstitution.getFields();
+    for (JField field : fields) {
+      assertNotNull(postSubstitution.getField(field.getName()));
+    }
+  }
+
+  protected static void validateGetMethod(JClassType preSubstitution,
+      JClassType postSubstitution, Substitution substitution)
+      throws NotFoundException {
+
+    JMethod[] methods = preSubstitution.getMethods();
+    for (JMethod method : methods) {
+      JParameter[] params = method.getParameters();
+      JType[] paramTypes = new JType[params.length];
+
+      for (int i = 0; i < params.length; ++i) {
+        paramTypes[i] = substitution.getSubstitution(params[i].getType());
+      }
+
+      assertNotNull(postSubstitution.getMethod(method.getName(), paramTypes));
+    }
+  }
+
+  protected static void validateGetOverloads(JClassType preSubstitution,
+      JClassType postSubstitution) {
+    JMethod[] methods = preSubstitution.getMethods();
+    for (JMethod method : methods) {
+      assertEquals(preSubstitution.getOverloads(method.getName()).length,
+          postSubstitution.getOverloads(method.getName()).length);
+    }
+  }
+
+  protected static void validateImplementedInterfaceSubstitution(
+      JClassType preSubstitution, JClassType postSubstituion,
+      Substitution substitution) {
+    JClassType[] preSubIntfs = preSubstitution.getImplementedInterfaces();
+    JClassType[] postSubIntfs = postSubstituion.getImplementedInterfaces();
+
+    assertEquals(preSubIntfs.length, postSubIntfs.length);
+
+    for (int i = 0; i < preSubIntfs.length; ++i) {
+      assertEquals(postSubIntfs[i],
+          substitution.getSubstitution(postSubIntfs[i]));
+    }
+  }
+
+  protected static void validateMetaData(HasMetaData md1, HasMetaData md2) {
+    validateMetaDataTags(md1, md2);
+
+    String[] md1TagNames = md1.getMetaDataTags();
+    String[] md2TagNames = md2.getMetaDataTags();
+
+    for (int i = 0; i < md1TagNames.length; ++i) {
+      assertEquals(md1TagNames[i], md2TagNames[i]);
+
+      String[][] md1TagValues = md1.getMetaData(md1TagNames[i]);
+      String[][] md2TagValues = md2.getMetaData(md2TagNames[i]);
+
+      assertTrue(Arrays.deepEquals(md1TagValues, md2TagValues));
+    }
+  }
+
+  protected static void validateMetaDataTags(HasMetaData md1, HasMetaData md2) {
+    assertEquals(md1.getMetaDataTags().length, md2.getMetaDataTags().length);
+  }
+
+  protected static void validateMethodSubstitution(JMethod preSubMethod,
+      JMethod postSubMethod, Substitution substitution) {
+
+    assertEquals(substitution.getSubstitution(preSubMethod.getReturnType()),
+        postSubMethod.getReturnType());
+
+    validateAbstractMethodSubstitution(preSubMethod, postSubMethod,
+        substitution);
+  }
+
+  protected static void validateMethodSubstitutions(JClassType preSubstitution,
+      JClassType postSubstituion, Substitution substitution)
+      throws NotFoundException {
+    // Check the methods
+    JMethod[] preSubMethods = preSubstitution.getMethods();
+    JMethod[] postSubMethods = postSubstituion.getMethods();
+    assertEquals(preSubMethods.length, postSubMethods.length);
+
+    for (int i = 0; i < preSubMethods.length; ++i) {
+      JMethod preSubMethod = preSubMethods[i];
+
+      JParameter[] preSubParams = preSubMethod.getParameters();
+      JType[] postSubParamTypes = new JType[preSubParams.length];
+      for (int j = 0; j < preSubParams.length; ++j) {
+        postSubParamTypes[j] = substitution.getSubstitution(preSubParams[j].getType());
+      }
+      JMethod postSubMethod = postSubstituion.getMethod(preSubMethod.getName(),
+          postSubParamTypes);
+
+      validateMethodSubstitution(preSubMethod, postSubMethod, substitution);
+    }
+  }
+
+  protected static void validateTypeSubstitution(JClassType preSubstitution,
+      JClassType postSubstituion, Substitution substitution)
+      throws NotFoundException {
+    if (preSubstitution.isGenericType() == null) {
+      return;
+    }
+    
+    assertEquals(preSubstitution.getModifierBits(),
+        postSubstituion.getModifierBits());
+
+    validateAnnotations(preSubstitution, postSubstituion);
+
+    validateMetaData(preSubstitution, postSubstituion);
+
+    assertEquals(preSubstitution.getName(), postSubstituion.getName());
+    // assertEquals(preSubstitution.getSubstitution(substitution),
+    // postSubstituion);
+
+    // Check superclass
+    JClassType superClass = preSubstitution.getSuperclass();
+    if (superClass != null) {
+      validateTypeSubstitution(superClass, postSubstituion.getSuperclass(),
+          substitution);
+    }
+
+    // Check interfaces
+    validateImplementedInterfaceSubstitution(preSubstitution, postSubstituion,
+        substitution);
+
+    validateFieldSubstitutions(preSubstitution, postSubstituion, substitution);
+
+    validateConstructorSubstitutions(preSubstitution, postSubstituion,
+        substitution);
+
+    validateMethodSubstitutions(preSubstitution, postSubstituion, substitution);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#findConstructor(com.google.gwt.core.ext.typeinfo.JType[])}.
+   */
+  public void testFindConstructor() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateFindConstructor(baseType, testType, getSubstitution());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#findField(java.lang.String)}.
+   */
+  public void testFindField() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateFindField(baseType, testType);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#findMethod(java.lang.String, com.google.gwt.core.ext.typeinfo.JType[])}.
+   */
+  public void testFindMethod() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateFindMethod(baseType, testType, getSubstitution());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#findNestedType(java.lang.String)}.
+   */
+  public abstract void testFindNestedType() throws NotFoundException;
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getAnnotation(java.lang.Class)}.
+   */
+  public void testGetAnnotation() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    Annotation[] annotations = baseType.getAnnotations();
+    for (Annotation annotation : annotations) {
+      assertNotNull(testType.getAnnotation(annotation.annotationType()));
+    }
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getAnnotations()}.
+   */
+  public void testGetAnnotations() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateAnnotations(baseType, testType);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getBaseType()}.
+   */
+  public void testGetBaseType() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    assertNotNull(testType.getBaseType());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getCompilationUnit()}.
+   */
+  public void testGetCompilationUnit() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(testType.getCompilationUnit(), baseType.getCompilationUnit());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getConstructor(com.google.gwt.core.ext.typeinfo.JType[])}.
+   */
+  public void testGetConstructor() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+    JConstructor[] constructors = baseType.getConstructors();
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getConstructors()}.
+   */
+  public void testGetConstructors() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateConstructorSubstitutions(baseType, testType, getSubstitution());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getDeclaredAnnotations()}.
+   */
+  public void testGetDeclaredAnnotations() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateDeclaredAnnotations(baseType, testType);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getEnclosingType()}.
+   */
+  public abstract void testGetEnclosingType() throws NotFoundException;
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getErasedType()}.
+   */
+  public void testGetErasedType() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.getErasedType(), testType.getErasedType());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getField(java.lang.String)}.
+   */
+  public void testGetField() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateGetField(baseType, testType);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getFields()}.
+   */
+  public void testGetFields() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateFieldSubstitutions(baseType, testType, getSubstitution());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getImplementedInterfaces()}.
+   */
+  public void testGetImplementedInterfaces() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateImplementedInterfaceSubstitution(baseType, testType,
+        getSubstitution());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getJNISignature()}.
+   */
+  public void testGetJNISignature() {
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getMetaData(java.lang.String)}.
+   */
+  public void testGetMetaData() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateMetaData(baseType, testType);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getMetaDataTags()}.
+   */
+  public void testGetMetaDataTags() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateMetaDataTags(baseType, testType);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getMethod(java.lang.String, com.google.gwt.core.ext.typeinfo.JType[])}.
+   */
+  public void testGetMethod() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateGetMethod(baseType, testType, getSubstitution());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getMethods()}.
+   */
+  public void testGetMethods() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateMethodSubstitutions(baseType, testType, getSubstitution());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getModifierBits()}.
+   */
+  public void testGetModifierBits() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.getModifierBits(), testType.getModifierBits());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getName()}.
+   */
+  public void testGetName() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.getName(), testType.getName());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getNestedType(java.lang.String)}.
+   */
+  public abstract void testGetNestedType() throws NotFoundException;
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getNestedTypes()}.
+   */
+  public abstract void testGetNestedTypes() throws NotFoundException;
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getOracle()}.
+   */
+  public void testGetOracle() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.getOracle(), testType.getOracle());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getOverloads(java.lang.String)}.
+   */
+  public void testGetOverloads() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    validateGetOverloads(baseType, testType);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getOverridableMethods()}.
+   */
+  public abstract void testGetOverridableMethods() throws NotFoundException;
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getPackage()}.
+   */
+  public void testGetPackage() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.getPackage(), testType.getPackage());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getSubtypes()}.
+   */
+  public abstract void testGetSubtypes() throws NotFoundException;
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#getSuperclass()}.
+   */
+  public void testGetSuperclass() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.getSuperclass() != null,
+        testType.getSuperclass() != null);
+
+    /*
+     * TODO: need to check that the super classes are consistent, if base super
+     * is generic then test type super should be parameterized with same super,
+     * etc.
+     */
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isAbstract()}.
+   */
+  public void testIsAbstract() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isAbstract(), testType.isAbstract());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isAnnotationPresent(java.lang.Class)}.
+   */
+  public void testIsAnnotationPresent() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    Annotation[] annotations = baseType.getAnnotations();
+    for (Annotation annotation : annotations) {
+      assertTrue(testType.isAnnotationPresent(annotation.annotationType()));
+    }
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isArray()}.
+   */
+  public void testIsArray() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isArray(), testType.isArray());
+
+    // TODO: check parameterized arrays
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isAssignableFrom(com.google.gwt.core.ext.typeinfo.JClassType)}.
+   */
+  public abstract void testIsAssignableFrom() throws NotFoundException;
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isAssignableTo(com.google.gwt.core.ext.typeinfo.JClassType)}.
+   */
+  public abstract void testIsAssignableTo() throws NotFoundException;
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isClass()}.
+   */
+  public void testIsClass() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isClass() != null, testType.isClass() != null);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isClassOrInterface()}.
+   */
+  public void testIsClassOrInterface() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isClassOrInterface() != null,
+        testType.isClassOrInterface() != null);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isDefaultInstantiable()}.
+   */
+  public void testIsDefaultInstantiable() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isDefaultInstantiable(),
+        testType.isDefaultInstantiable());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isEnum()}.
+   */
+  public void testIsEnum() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isEnum() != null, testType.isEnum() != null);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isInterface()}.
+   */
+  public void testIsInterface() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isInterface() != null, testType.isInterface() != null);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isLocalType()}.
+   */
+  public void testIsLocalType() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isLocalType(), testType.isLocalType());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isMemberType()}.
+   */
+  public void testIsMemberType() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isMemberType(), testType.isMemberType());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isPrimitive()}.
+   */
+  public void testIsPrimitive() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertNull(testType.isPrimitive());
+    assertNull(baseType.isPrimitive());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isPrivate()}.
+   */
+  public void testIsPrivate() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isPrivate(), testType.isPrivate());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isProtected()}.
+   */
+  public void testIsProtected() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isProtected(), testType.isProtected());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isPublic()}.
+   */
+  public void testIsPublic() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isPublic(), testType.isPublic());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JDelegatingClassType#isStatic()}.
+   */
+  public void testIsStatic() throws NotFoundException {
+    JDelegatingClassType testType = getTestType();
+    JClassType baseType = testType.getBaseType();
+
+    assertEquals(baseType.isStatic(), testType.isStatic());
+  }
+
+  protected abstract Substitution getSubstitution() throws NotFoundException;
+
+  protected abstract JDelegatingClassType getTestType()
+      throws NotFoundException;
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java
new file mode 100644
index 0000000..fdc1b38
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.MyEnum;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link JEnumType}.
+ */
+public class JEnumTypeTest extends TestCase {
+  private final TreeLogger logger = TreeLogger.NULL;
+
+  private ModuleDef moduleDef;
+
+  private final TypeOracle typeOracle;
+
+  public JEnumTypeTest() throws UnableToCompleteException, NotFoundException {
+    moduleDef = ModuleDefLoader.loadFromClassPath(logger,
+        "com.google.gwt.core.ext.typeinfo.TypeOracleTest");
+    typeOracle = moduleDef.getTypeOracle(logger);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JEnumType#getEnumConstants()}.
+   * 
+   * @throws NotFoundException
+   */
+  public void testGetEnumConstants() throws NotFoundException {
+    JClassType type = typeOracle.getType(MyEnum.class.getName());
+    JEnumType enumType = type.isEnum();
+    assertNotNull(enumType);
+
+    JEnumConstant[] enumConstants = enumType.getEnumConstants();
+    assertEquals(3, enumConstants.length);
+
+    for (JEnumConstant enumConstant : enumConstants) {
+      assertEquals(
+          Integer.parseInt(enumConstant.getName().substring(3)),
+          enumConstant.getOrdinal());
+    }
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JClassType#getFields()}.
+   * 
+   * @throws NotFoundException
+   */
+  public void testGetFields() throws NotFoundException {
+    JClassType type = typeOracle.getType(MyEnum.class.getName());
+    JEnumType enumType = validateTypeIsEnum(type);
+
+    assertEquals(5, enumType.getFields().length);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JClassType#getField(String)}.
+   * 
+   * @throws NotFoundException
+   */
+  public void testGetFieldString() throws NotFoundException {
+    JClassType type = typeOracle.getType(MyEnum.class.getName());
+
+    validateTypeIsEnum(type);
+
+    type.getField("VAL0");
+    type.getField("VAL1");
+    type.getField("VAL2");
+    type.getField("instanceField");
+    type.getField("e");
+  }
+
+  /**
+   * Test method for {@link JEnumType#getConstructors()}
+   * 
+   * @throws NotFoundException
+   * @throws NotFoundException
+   */
+  public void testGetConstructors() throws NotFoundException {
+    JClassType type = typeOracle.getType(MyEnum.class.getName());
+    JEnumType enumType = validateTypeIsEnum(type);
+
+    assertEquals(1, enumType.getConstructors().length);
+  }
+
+  /**
+   * Test method for {@link JEnumType#getMethods()}
+   * 
+   * @throws NotFoundException
+   */
+  public void testGetMethods() throws NotFoundException {
+    JClassType type = typeOracle.getType(MyEnum.class.getName());
+    JEnumType enumType = validateTypeIsEnum(type);
+
+    assertEquals(1, enumType.getMethods().length);
+  }
+
+  /**
+   * Test method for {@link com.google.gwt.core.ext.typeinfo.JEnumType#isEnum()}.
+   * 
+   * @throws NotFoundException
+   */
+  public void testIsEnum() throws NotFoundException {
+    JClassType type = typeOracle.getType(MyEnum.class.getName());
+    validateTypeIsEnum(type);
+  }
+
+  private static JEnumType validateTypeIsEnum(JClassType type)
+      throws NotFoundException {
+    JEnumType maybeEnum = type.isEnum();
+    assertNotNull(maybeEnum);
+
+    JClassType enumType = type.getOracle().getType(Enum.class.getName());
+
+    assertTrue(enumType.isAssignableFrom(maybeEnum));
+
+    return maybeEnum;
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JGenericTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JGenericTypeTest.java
new file mode 100644
index 0000000..c054387
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JGenericTypeTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.GenericClass;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link JGenericType}.
+ */
+public class JGenericTypeTest extends TestCase {
+  private final boolean logToConsole = false;
+  private final ModuleContext moduleContext = new ModuleContext(logToConsole
+      ? new PrintWriterTreeLogger() : TreeLogger.NULL,
+      "com.google.gwt.core.ext.typeinfo.TypeOracleTest");
+
+  public JGenericTypeTest() throws UnableToCompleteException {
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JGenericType#getErasedType()}.
+   * 
+   * @throws NotFoundException
+   */
+  public void testGetErasedType() throws NotFoundException {
+    JGenericType genericClass = getTestType();
+
+    assertEquals(genericClass.getRawType(), genericClass.getErasedType());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JGenericType#getRawType()}.
+   * 
+   * @throws NotFoundException
+   */
+  public void testGetRawType() throws NotFoundException {
+    JGenericType genericClass = getTestType();
+
+    JDelegatingClassTypeTest.validateTypeSubstitution(genericClass,
+        genericClass.getRawType(), new Substitution() {
+          public JType getSubstitution(JType type) {
+            return type.getErasedType();
+          }
+        });
+  }
+
+  /**
+   * Test method for {@link
+   * com.google.gwt.core.ext.typeinfo.JGenericType#getTypeParameters()}.
+   * 
+   * @throws NotFoundException
+   */
+  public void testGetTypeParameters() throws NotFoundException {
+    JGenericType genericType = getTestType();
+    JTypeParameter[] typeParameters = genericType.getTypeParameters();
+
+    assertEquals(1, typeParameters.length);
+
+    JTypeParameter typeParameter = typeParameters[0];
+    assertEquals("T", typeParameter.getName());
+
+    JBound bound = typeParameter.getBounds();
+    assertNotNull(bound.isUpperBound());
+
+    JClassType[] bounds = bound.getBounds();
+    assertEquals(1, bounds.length);
+    assertEquals(moduleContext.getOracle().getJavaLangObject(), bounds[0]);
+  }
+
+  /**
+   * Returns the generic version of {@link GenericClass}.
+   */
+  protected JGenericType getTestType() throws NotFoundException {
+    JClassType type = moduleContext.getOracle().getType(
+        GenericClass.class.getName());
+    JGenericType genericType = type.isGenericType();
+    assertNotNull(genericType);
+    return genericType;
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JParameterizedTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JParameterizedTypeTest.java
new file mode 100644
index 0000000..50a06c4
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JParameterizedTypeTest.java
@@ -0,0 +1,401 @@
+/*
+ * 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.Base;
+import com.google.gwt.core.ext.typeinfo.test.Derived;
+import com.google.gwt.core.ext.typeinfo.test.GenericClass;
+import com.google.gwt.core.ext.typeinfo.test.MyCustomList;
+import com.google.gwt.core.ext.typeinfo.test.MyIntegerList;
+import com.google.gwt.core.ext.typeinfo.test.MyList;
+import com.google.gwt.core.ext.typeinfo.test.GenericClass.GenericInnerClass;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Test for {@link JParameterizedType}.
+ */
+public class JParameterizedTypeTest extends JDelegatingClassTypeTest {
+  /**
+   * Helper for verifying parameterized substitutions.
+   */
+  static class ParameterizedSubstitution implements Substitution {
+    private final JParameterizedType parameterizedType;
+
+    public ParameterizedSubstitution(JParameterizedType parameterizedType) {
+      this.parameterizedType = parameterizedType;
+    }
+
+    public JType getSubstitution(JType type) {
+      return type.getSubstitutedType(parameterizedType);
+    }
+  }
+
+  private final JClassType integerType;
+  private final boolean logToConsole = false;
+  private final ModuleContext moduleContext = new ModuleContext(logToConsole
+      ? new PrintWriterTreeLogger() : TreeLogger.NULL,
+      "com.google.gwt.core.ext.typeinfo.TypeOracleTest");
+
+  public JParameterizedTypeTest() throws UnableToCompleteException,
+      NotFoundException {
+    integerType = moduleContext.getOracle().getType(Integer.class.getName());
+  }
+
+  @Override
+  public void testFindNestedType() {
+    // TODO: complete this test method
+  }
+
+  /**
+   * Checks that GenericClass<Integer> ends up with the correct substitutions.
+   * 
+   * @throws NotFoundException
+   */
+  public void testGenericClass_Integer() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JGenericType genericType = getGenericTestType();
+    JClassType type = oracle.getParameterizedType(genericType,
+        new JClassType[] {integerType});
+    JParameterizedType parameterizedType = type.isParameterized();
+
+    validateTypeSubstitution(genericType, parameterizedType,
+        new ParameterizedSubstitution(parameterizedType));
+  }
+
+  public void testGenericClass_LowerBoundWildcard() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JGenericType genericType = getGenericTestType();
+    JWildcardType lowerBoundWildcard = oracle.getWildcardType(new JLowerBound(
+        new JClassType[] {integerType}));
+
+    JClassType type = oracle.getParameterizedType(genericType,
+        new JClassType[] {lowerBoundWildcard});
+    JParameterizedType parameterizedType = type.isParameterized();
+
+    validateTypeSubstitution(genericType, parameterizedType,
+        new ParameterizedSubstitution(parameterizedType));
+  }
+
+  public void testGenericClass_UnboundWildcard() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JGenericType genericType = getGenericTestType();
+    JWildcardType upperBoundWildcard = oracle.getWildcardType(new JUpperBound(
+        new JClassType[] {oracle.getJavaLangObject()}));
+
+    JClassType type = oracle.getParameterizedType(genericType,
+        new JClassType[] {upperBoundWildcard});
+    JParameterizedType parameterizedType = type.isParameterized();
+
+    validateTypeSubstitution(genericType, parameterizedType,
+        new ParameterizedSubstitution(parameterizedType));
+  }
+
+  public void testGenericClass_UpperBoundWildcard() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JGenericType genericType = getGenericTestType();
+    JWildcardType upperBoundWildcard = oracle.getWildcardType(new JUpperBound(
+        new JClassType[] {integerType}));
+
+    JClassType type = oracle.getParameterizedType(genericType,
+        new JClassType[] {upperBoundWildcard});
+    JParameterizedType parameterizedType = type.isParameterized();
+
+    validateTypeSubstitution(genericType, parameterizedType,
+        new ParameterizedSubstitution(parameterizedType));
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JParameterizedType#getEnclosingType()}.
+   * 
+   * @throws NotFoundException
+   */
+  @Override
+  public void testGetEnclosingType() throws NotFoundException {
+    JParameterizedType testType = getTestType();
+
+    // Check that GenericClass<Integer> is not nested
+    assertNull(testType.getEnclosingType());
+
+    /*
+     * Check that GenericClass<Integer>.GenericInnerClass<Boolean> has //
+     * GenericClass<Integer> as its // enclosing type
+     */
+    JParameterizedType parameterizedInnerClass = getInnerParameterizedType();
+
+    assertEquals(testType, parameterizedInnerClass.getEnclosingType());
+  }
+
+  @Override
+  public void testGetNestedType() {
+    // TODO: complete this test method
+  }
+
+  /**
+   * Test method for {@link
+   * com.google.gwt.core.ext.typeinfo.JParameterizedType#getNestedTypes()}.
+   * 
+   * @throws NotFoundException
+   */
+  @Override
+  public void testGetNestedTypes() throws NotFoundException {
+    JParameterizedType cut = getTestType();
+    JParameterizedType innerCut = getInnerParameterizedType();
+
+    // Check that inner parameterized types don't appear in the parent's nested
+    // type set
+    assertEquals(0, cut.getNestedTypes().length);
+
+    try {
+      cut.getNestedType(innerCut.getSimpleSourceName());
+      fail("Type " + cut.getQualifiedSourceName()
+          + " should report that it has no nested types");
+    } catch (NotFoundException ex) {
+      // Expected to get here
+    }
+  }
+
+  @Override
+  public void testGetOverridableMethods() throws NotFoundException {
+    // Tested via testOverridableMethods_Base, testOverridableMethods_Derived,
+    // testOverridableMethods_Derived_Integer
+  }
+
+  /**
+   * Tests the subtypes of MyList<Integer>. These should be:
+   * <ul>
+   * <li><code>MyIntegerList</code></li>
+   * <li><code>MyCustomList&lt;? extends Serializable, Integer&gt;</code></li>
+   * </ul>
+   * 
+   * @throws NotFoundException
+   */
+  @Override
+  public void testGetSubtypes() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JGenericType genericType = oracle.getType(MyList.class.getName()).isGenericType();
+
+    JParameterizedType parameterizedType = oracle.getParameterizedType(
+        genericType, new JClassType[] {integerType});
+    JClassType[] actualSubtypes = parameterizedType.getSubtypes();
+
+    JGenericType myCustomListType = oracle.getType(MyCustomList.class.getName()).isGenericType();
+    JParameterizedType parameterizedMyCustomList = oracle.getParameterizedType(
+        myCustomListType, new JClassType[] {
+            oracle.getWildcardType(new JUpperBound(
+                oracle.getType(Serializable.class.getName()))), integerType});
+    JClassType[] expected = {
+        oracle.getType(MyIntegerList.class.getName()),
+        parameterizedMyCustomList
+    };
+    
+    validateEquals(oracle, expected, actualSubtypes);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JParameterizedType#isAssignableFrom(JClassType)}.
+   */
+  @Override
+  public void testIsAssignableFrom() throws NotFoundException {
+    // Check that raw types can be assigned to a parameterized type
+    JParameterizedType testType = getTestType();
+    JClassType rawType = testType.getRawType();
+
+    assertTrue(testType.isAssignableFrom(rawType));
+
+    TypeOracle oracle = moduleContext.getOracle();
+    JGenericType genericList = (JGenericType) oracle.getType(List.class.getName());
+
+    JWildcardType unboundWildcard = oracle.getWildcardType(new JUpperBound(
+        oracle.getJavaLangObject()));
+    JWildcardType numUpperBoundWildcard = oracle.getWildcardType(new JUpperBound(
+        oracle.getType(Number.class.getName())));
+
+    // List<?> should be assignable from List<? extends Number>
+    JParameterizedType unboundList = oracle.getParameterizedType(genericList,
+        new JClassType[] {unboundWildcard});
+    JParameterizedType numUpperBoundList = oracle.getParameterizedType(
+        genericList, new JClassType[] {numUpperBoundWildcard});
+    assertTrue(unboundList.isAssignableFrom(numUpperBoundList));
+    assertFalse(numUpperBoundList.isAssignableFrom(unboundList));
+
+    // List<? extends Number> should be assignable from List<? extends Integer>
+    JWildcardType intUpperBoundWildcard = oracle.getWildcardType(new JUpperBound(
+        integerType));
+
+    JParameterizedType intUpperBoundList = oracle.getParameterizedType(
+        genericList, new JClassType[] {intUpperBoundWildcard});
+    assertTrue(numUpperBoundList.isAssignableFrom(intUpperBoundList));
+    assertFalse(intUpperBoundList.isAssignableFrom(numUpperBoundList));
+
+    // List<? super Integer> should be assignable from List<? super Number>
+    JWildcardType numLowerBoundWildcard = oracle.getWildcardType(new JLowerBound(
+        oracle.getType(Number.class.getName())));
+    JWildcardType intLowerBoundWildcard = oracle.getWildcardType(new JLowerBound(
+        integerType));
+
+    JParameterizedType numLowerBoundList = oracle.getParameterizedType(
+        genericList, new JClassType[] {numLowerBoundWildcard});
+    JParameterizedType intLowerBoundList = oracle.getParameterizedType(
+        genericList, new JClassType[] {intLowerBoundWildcard});
+
+    assertTrue(intLowerBoundList.isAssignableFrom(numLowerBoundList));
+    assertFalse(numLowerBoundList.isAssignableFrom(intLowerBoundList));
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.core.ext.typeinfo.JParameterizedType#isAssignableTo(JClassType)}.
+   */
+  @Override
+  public void testIsAssignableTo() {
+    // TODO:
+  }
+
+  public void testOverridableMethods_Base() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType type = oracle.getType(Base.class.getName());
+    JGenericType genericType = type.isGenericType();
+    assertNotNull(genericType);
+
+    List<JMethod> expected = new ArrayList<JMethod>(
+        Arrays.asList(type.getOverloads("m")));
+    List<JMethod> actual = new ArrayList<JMethod>(
+        Arrays.asList(type.getOverridableMethods()));
+
+    validateOverridableMethods(expected, actual, true);
+  }
+
+  public void testOverridableMethods_Derived() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType type = oracle.getType(Derived.class.getName());
+    JGenericType genericType = type.isGenericType();
+    assertNotNull(genericType);
+
+    JClassType supertype = type.getSuperclass();
+    JParameterizedType paramType = supertype.isParameterized();
+    // JGenericType genericSuperType = paramType.getBaseType().isGenericType();
+    assertNotNull(paramType);
+
+    List<JMethod> expected = new ArrayList<JMethod>();
+    expected.addAll(Arrays.asList(genericType.getOverloads("m")));
+    expected.add(paramType.getMethod("m",
+        new JType[] {paramType.getTypeArgs()[0]}));
+
+    List<JMethod> actual = new ArrayList<JMethod>(
+        Arrays.asList(type.getOverridableMethods()));
+
+    validateOverridableMethods(expected, actual, true);
+  }
+
+  public void testOverridableMethods_Derived_Integer() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType type = oracle.getType(Derived.class.getName());
+    JGenericType genericType = type.isGenericType();
+    assertNotNull(genericType);
+
+    JParameterizedType paramType = oracle.getParameterizedType(genericType,
+        new JClassType[] {integerType});
+
+    List<JMethod> expected = new ArrayList<JMethod>();
+    expected.addAll(Arrays.asList(paramType.getOverloads("m")));
+
+    List<JMethod> actual = new ArrayList<JMethod>(
+        Arrays.asList(paramType.getOverridableMethods()));
+
+    validateOverridableMethods(expected, actual, true);
+  }
+
+  /**
+   * Returns the <code>TypeOracle</code> type for {@link GenericClass}.
+   * 
+   * @return <code>TypeOracle</code> type for {@link GenericClass}
+   * @throws NotFoundException
+   */
+  protected JGenericType getGenericTestType() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType type = oracle.getType(GenericClass.class.getName());
+    assertNotNull(type.isGenericType());
+    return type.isGenericType();
+  }
+
+  @Override
+  protected Substitution getSubstitution() throws NotFoundException {
+    return new ParameterizedSubstitution(getTestType());
+  }
+
+  @Override
+  protected JParameterizedType getTestType() throws NotFoundException {
+    JGenericType type = getGenericTestType();
+
+    return moduleContext.getOracle().getParameterizedType(type, null,
+        new JClassType[] {integerType});
+  }
+
+  /**
+   * Returns the type for GenericClass<Integer>.GenericInnerClass<Boolean>.
+   * 
+   * @throws NotFoundException
+   * @return type for GenericClass<Integer>.GenericInnerClass<Boolean>
+   */
+  private JParameterizedType getInnerParameterizedType()
+      throws NotFoundException {
+    JParameterizedType cut = getTestType();
+    TypeOracle oracle = moduleContext.getOracle();
+    JGenericType innerGenericClass = cut.getBaseType().getNestedType(
+        GenericInnerClass.class.getSimpleName()).isGenericType();
+
+    JClassType booleanType = oracle.getType(Boolean.class.getName());
+
+    /*
+     * Check that GenericClass<Integer>.GenericInnerClass<Boolean> has
+     * GenericClass<Integer> as its enclosing type
+     */
+    // 
+    JParameterizedType parameterizedInnerClass = oracle.getParameterizedType(
+        innerGenericClass, cut, new JClassType[] {booleanType});
+
+    return parameterizedInnerClass;
+  }
+
+  private void validateOverridableMethods(List<JMethod> expected,
+      List<JMethod> actual, boolean addObjectMethods) {
+    Set<JMethod> expectedMethods = new HashSet<JMethod>();
+    expectedMethods.addAll(expected);
+    if (addObjectMethods) {
+      TypeOracle oracle = moduleContext.getOracle();
+      expectedMethods.addAll(Arrays.asList(oracle.getJavaLangObject().getMethods()));
+    }
+
+    for (JMethod method : actual) {
+      assertEquals("Method " + method.getReadableDeclaration() + " from type "
+          + method.getEnclosingType().getQualifiedSourceName()
+          + " was not expected", true, expectedMethods.remove(method));
+    }
+
+    assertTrue(expectedMethods.isEmpty());
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JRawTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JRawTypeTest.java
new file mode 100644
index 0000000..b1f4425
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JRawTypeTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.MyCustomList;
+import com.google.gwt.core.ext.typeinfo.test.MyIntegerList;
+import com.google.gwt.core.ext.typeinfo.test.MyList;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import java.util.ArrayList;
+
+/**
+ * Test for {@link JRawType}.
+ */
+public class JRawTypeTest extends JDelegatingClassTypeTest {
+  private final boolean logToConsole = false;
+
+  private final ModuleContext moduleContext = new ModuleContext(logToConsole
+      ? new PrintWriterTreeLogger() : TreeLogger.NULL,
+      "com.google.gwt.core.ext.typeinfo.TypeOracleTest");
+
+  public JRawTypeTest() throws UnableToCompleteException {
+  }
+
+  @Override
+  public void testFindNestedType() {
+    // TODO Auto-generated method stub
+  }
+  @Override
+  public void testGetEnclosingType() {
+    // TODO Auto-generated method stub
+  }
+  @Override
+  public void testGetNestedType() {
+    // TODO Auto-generated method stub
+  }
+
+  @Override
+  public void testGetNestedTypes() {
+    // TODO Auto-generated method stub
+  }
+
+  @Override
+  public void testGetOverridableMethods() {
+    // TODO Auto-generated method stub
+  }
+
+  @Override
+  public void testGetSubtypes() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType testType = oracle.getType(MyList.class.getName());
+    JGenericType genericTestType = testType.isGenericType();
+    assertNotNull(genericTestType);
+    
+    JRawType rawTestType = genericTestType.getRawType();
+    JClassType[] expectedTypes = new JClassType[] {
+      oracle.getType(MyCustomList.class.getName()).isGenericType().getRawType(),
+      oracle.getType(MyIntegerList.class.getName())
+    };
+    JClassType[] actualSubtypes = rawTestType.getSubtypes();
+   
+    validateEquals(oracle, expectedTypes, actualSubtypes);
+  }
+
+  @Override
+  public void testIsAssignableFrom() throws NotFoundException {
+    JRawType rawType = getTestType();
+    assertTrue(rawType.isAssignableFrom(rawType.getBaseType()));
+  }
+
+  @Override
+  public void testIsAssignableTo() throws NotFoundException {
+    JRawType rawType = getTestType();
+    assertTrue(rawType.getBaseType().isAssignableTo(rawType));
+  }
+
+  @Override
+  protected Substitution getSubstitution() throws NotFoundException {
+    return new Substitution() {
+      public JType getSubstitution(JType type) {
+        return type.getErasedType();
+      }
+    };
+  }
+
+  /**
+   * Returns the RawType for {@link ArrayList}.
+   */
+  @Override
+  protected JRawType getTestType() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType testType = oracle.getType(ArrayList.class.getName());
+    return testType.isGenericType().getRawType();
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JTypeParameterTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JTypeParameterTest.java
new file mode 100644
index 0000000..0fb9438
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JTypeParameterTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.GenericClass;
+import com.google.gwt.core.ext.typeinfo.test.GenericSubclass;
+import com.google.gwt.core.ext.typeinfo.test.MyCustomList;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import java.util.Arrays;
+
+/**
+ * Tests for {@link JTypeParameter}.
+ */
+public class JTypeParameterTest extends JDelegatingClassTypeTest {
+  private final boolean logToConsole = false;
+  private final ModuleContext moduleContext = new ModuleContext(logToConsole
+      ? new PrintWriterTreeLogger() : TreeLogger.NULL,
+      "com.google.gwt.core.ext.typeinfo.TypeOracleTest");
+
+  public JTypeParameterTest() throws UnableToCompleteException {
+  }
+
+  @Override
+  public void testFindConstructor() {
+  }
+
+  @Override
+  public void testFindNestedType() {
+  }
+
+  @Override
+  public void testGetConstructors() {
+  }
+
+  @Override
+  public void testGetEnclosingType() throws NotFoundException {
+    JTypeParameter testType = getTestType();
+    assertEquals(
+        moduleContext.getOracle().getType(GenericClass.class.getName()),
+        testType.getDeclaringClass());
+  }
+
+  @Override
+  public void testGetName() throws NotFoundException {
+    assertEquals("T", getTestType().getName());
+  }
+
+  @Override
+  public void testGetNestedType() {
+  }
+
+  @Override
+  public void testGetNestedTypes() throws NotFoundException {
+    JTypeParameter testType = getTestType();
+
+    assertTrue(Arrays.deepEquals(testType.getNestedTypes(),
+        testType.getFirstBound().getNestedTypes()));
+  }
+
+  @Override
+  public void testGetOverridableMethods() throws NotFoundException {
+    JTypeParameter testType = getTestType();
+
+    assertTrue(Arrays.deepEquals(testType.getOverridableMethods(),
+        testType.getFirstBound().getOverridableMethods()));
+  }
+
+  @Override
+  public void testGetSubtypes() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType testType = oracle.getType(MyCustomList.class.getName());
+    JGenericType genericType = testType.isGenericType();
+    JTypeParameter[] typeParameters = genericType.getTypeParameters();
+    JTypeParameter typeParameter = typeParameters[0];
+
+    JClassType[] expected = new JClassType[] {
+    /*
+     * TODO: Re-eneable this once java.io.Serializable is added to the JRE
+     * 
+     * emulation classes oracle.getType(Integer.class.getName()),
+     * oracle.getType(Float.class.getName()),
+     * oracle.getType(Short.class.getName()),
+     * oracle.getType(Double.class.getName()),
+     * oracle.getType(Number.class.getName()),
+     * oracle.getType(Long.class.getName()),
+     * oracle.getType(Byte.class.getName()),
+     */
+    };
+    validateEquals(oracle, expected, typeParameter.getSubtypes());
+  }
+
+  @Override
+  public void testIsAssignableFrom() throws NotFoundException {
+    JTypeParameter testType = getTestType();
+    assertTrue(testType.isAssignableFrom(moduleContext.getOracle().getJavaLangObject()));
+  }
+
+  @Override
+  public void testIsAssignableTo() throws NotFoundException {
+    JTypeParameter testType = getTestType();
+    assertTrue(testType.isAssignableTo(moduleContext.getOracle().getJavaLangObject()));
+  }
+
+  @Override
+  protected Substitution getSubstitution() {
+    return new Substitution() {
+      public JType getSubstitution(JType type) {
+        return type;
+      }
+    };
+  }
+
+  /*
+   * NOTE: This method returns the type parameter T from the GenericClass<T>
+   * type.
+   */
+  @Override
+  protected JTypeParameter getTestType() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType testType = oracle.getType(GenericClass.class.getName());
+    JGenericType genericTestType = testType.isGenericType();
+    assertNotNull(genericTestType);
+    JTypeParameter[] typeParameters = genericTestType.getTypeParameters();
+    assertTrue(typeParameters.length > 0);
+    return typeParameters[0];
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JWildcardTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JWildcardTypeTest.java
new file mode 100644
index 0000000..becea26
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JWildcardTypeTest.java
@@ -0,0 +1,238 @@
+/*
+ * 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.CA;
+import com.google.gwt.core.ext.typeinfo.test.CB;
+import com.google.gwt.core.ext.typeinfo.test.CC;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Test for {@link JWildcardType}.
+ */
+public class JWildcardTypeTest extends JDelegatingClassTypeTest {
+  private final boolean logToConsole = false;
+  private final ModuleContext moduleContext = new ModuleContext(logToConsole
+      ? new PrintWriterTreeLogger() : TreeLogger.NULL,
+      "com.google.gwt.core.ext.typeinfo.TypeOracleTest");
+
+  public JWildcardTypeTest() throws UnableToCompleteException {
+  }
+
+  @Override
+  public void testFindConstructor() {
+    // Wildcard types do not have constructors
+  }
+
+  @Override
+  public void testFindNestedType() {
+    // Wildcard do not have nested types...
+  }
+
+  @Override
+  public void testGetConstructors() {
+  }
+
+  @Override
+  public void testGetEnclosingType() {
+    // Wildcard do not have nested types...
+  }
+
+  @Override
+  public void testGetNestedType() {
+    // No nested types
+  }
+
+  @Override
+  public void testGetNestedTypes() {
+    // No nested types
+  }
+
+  @Override
+  public void testGetOverridableMethods() {
+    // No overridable methods
+  }
+
+  @Override
+  public void testGetSubtypes() {
+    // Tested by testGetSubtypes_LowerBound() and testGetSubtypes_UpperBound()
+  }
+
+  public void testGetSubtypes_LowerBound() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    // <? super Number>
+    JWildcardType lowerBoundWildcard = oracle.getWildcardType(new JLowerBound(
+        oracle.getType(Number.class.getName())));
+    JClassType[] subtypes = lowerBoundWildcard.getSubtypes();
+    assertEquals(0, subtypes.length);
+//    assertEquals(oracle.getJavaLangObject(), subtypes[0]);
+  }
+
+  public void testGetSubtypes_UpperBound() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    // <? extends CA>
+    JWildcardType upperBoundWildcard = oracle.getWildcardType(new JUpperBound(
+        oracle.getType(CA.class.getName())));
+
+    JClassType[] expected = new JClassType[] {
+        oracle.getType(CB.class.getName()), oracle.getType(CC.class.getName())};
+    Set<JClassType> expectedSet = new HashSet<JClassType>();
+    expectedSet.addAll(Arrays.asList(expected));
+    
+    JClassType[] actual = upperBoundWildcard.getSubtypes();
+    assertEquals(expectedSet.size(), actual.length);
+    
+    for (int i = 0; i < actual.length; ++i) {
+      expectedSet.remove(actual[i]);
+    }
+    assertTrue(expectedSet.isEmpty());
+  }
+
+  @Override
+  public void testIsAssignableFrom() {
+    // Covered by the different testIsAssignableFrom*() variants below.
+  }
+
+  /**
+   * Tests that <? extends Number> is assignable from <? extends Integer> and
+   * that the reverse is not <code>true</code>.
+   */
+  public void testIsAssignableFrom_Extends_Number_To_Extends_Integer()
+      throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType numberType = oracle.getType(Number.class.getName());
+    JClassType integerType = oracle.getType(Integer.class.getName());
+
+    JUpperBound numberBound = new JUpperBound(numberType);
+    JUpperBound integerBound = new JUpperBound(integerType);
+
+    JWildcardType numberWildcard = oracle.getWildcardType(numberBound);
+    JWildcardType integerWildcard = oracle.getWildcardType(integerBound);
+
+    assertTrue(numberWildcard.isAssignableFrom(integerWildcard));
+    assertFalse(integerWildcard.isAssignableFrom(numberWildcard));
+  }
+
+  /**
+   * Tests that <? extends Number> is assignable from <? extends Integer> and
+   * that the reverse is not <code>true</code>.
+   */
+  public void testIsAssignableFrom_Extends_Object_From_Super_Object()
+      throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+
+    JClassType javaLangObject = oracle.getJavaLangObject();
+    JLowerBound lowerBound = new JLowerBound(javaLangObject);
+    JUpperBound upperBound = new JUpperBound(javaLangObject);
+
+    JWildcardType lowerWildcard = oracle.getWildcardType(lowerBound);
+    JWildcardType upperWildcard = oracle.getWildcardType(upperBound);
+
+    assertTrue(upperWildcard.isAssignableFrom(lowerWildcard));
+    assertFalse(lowerWildcard.isAssignableFrom(upperWildcard));
+  }
+
+  /**
+   * Tests that <? super Integer> is assignable from <? super Number> and that
+   * the reverse is not <code>true</code>.
+   */
+  public void testIsAssignableFrom_Super_Integer_From_Super_Number()
+      throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType numberType = oracle.getType(Number.class.getName());
+    JClassType integerType = oracle.getType(Integer.class.getName());
+
+    JLowerBound numberBound = new JLowerBound(numberType);
+    JLowerBound integerBound = new JLowerBound(integerType);
+
+    JWildcardType numberWildcard = oracle.getWildcardType(numberBound);
+    JWildcardType integerWildcard = oracle.getWildcardType(integerBound);
+
+    assertFalse(numberWildcard.isAssignableFrom(integerWildcard));
+    assertTrue(integerWildcard.isAssignableFrom(numberWildcard));
+  }
+
+  /**
+   * Tests that <? super Number> is assignable to <? super Integer> and that the
+   * reverse is not <code>true</code>.
+   */
+  public void testIsAssignableFrom_Super_Number_To_Super_Integer()
+      throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType numberType = oracle.getType(Number.class.getName());
+    JClassType integerType = oracle.getType(Integer.class.getName());
+
+    JLowerBound numberBound = new JLowerBound(numberType);
+    JLowerBound integerBound = new JLowerBound(integerType);
+
+    JWildcardType numberWildcard = oracle.getWildcardType(numberBound);
+    JWildcardType integerWildcard = oracle.getWildcardType(integerBound);
+
+    assertFalse(numberWildcard.isAssignableTo(integerWildcard));
+    assertTrue(integerWildcard.isAssignableTo(numberWildcard));
+  }
+
+  @Override
+  public void testIsAssignableTo() {
+  }
+
+  /**
+   * Tests that <? extends Integer> is assignable to <? extends Number> and that
+   * the reverse is not <code>true</code>.
+   */
+  public void testIsAssignableTo_Extends_Integer_To_Extends_Number()
+      throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType numberType = oracle.getType(Number.class.getName());
+    JClassType integerType = oracle.getType(Integer.class.getName());
+
+    JUpperBound numberBound = new JUpperBound(numberType);
+    JUpperBound integerBound = new JUpperBound(integerType);
+
+    JWildcardType numberWildcard = oracle.getWildcardType(numberBound);
+    JWildcardType integerWildcard = oracle.getWildcardType(integerBound);
+
+    assertTrue(integerWildcard.isAssignableTo(numberWildcard));
+    assertFalse(numberWildcard.isAssignableTo(integerWildcard));
+  }
+
+  @Override
+  protected Substitution getSubstitution() {
+    return new Substitution() {
+      public JType getSubstitution(JType type) {
+        return type;
+      }
+    };
+  }
+  
+  public void testGetMethods() throws NotFoundException {
+    super.testGetMethods();
+  }
+
+
+  @Override
+  protected JWildcardType getTestType() throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    return oracle.getWildcardType(new JUpperBound(
+        oracle.getType(Number.class.getName())));
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/ModuleContext.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/ModuleContext.java
new file mode 100644
index 0000000..acb8b2f
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/ModuleContext.java
@@ -0,0 +1,43 @@
+/*
+ * 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.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+
+/**
+ * Helper for loading modules from the classpath. 
+ */
+class ModuleContext {
+  private final TypeOracle oracle;
+  private final ModuleDef moduleDef;
+
+  ModuleContext(TreeLogger logger, String moduleName)
+      throws UnableToCompleteException {
+    moduleDef = ModuleDefLoader.loadFromClassPath(logger, moduleName);
+    oracle = moduleDef.getTypeOracle(logger);
+  }
+  
+  public TypeOracle getOracle() {
+    return oracle;
+  }
+  
+  public ModuleDef getModule() {
+    return moduleDef;
+  }
+}
\ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleGenericsSupportTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleGenericsSupportTest.java
index d1b4f93..e69de29 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleGenericsSupportTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleGenericsSupportTest.java
@@ -1,49 +0,0 @@
-/*
- * 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.GenericSubclass;
-import com.google.gwt.dev.cfg.ModuleDef;
-import com.google.gwt.dev.cfg.ModuleDefLoader;
-
-import junit.framework.TestCase;
-
-/**
- * Test cases for the {@link TypeOracle}'s and TypeOracleBuilder support for
- * generics.
- */
-public class TypeOracleGenericsSupportTest extends TestCase {
-  static {
-    ModuleDefLoader.setEnableCachingModules(true);
-  }
-
-  private final TreeLogger logger = TreeLogger.NULL;
-  private ModuleDef moduleDef;
-
-  private final TypeOracle typeOracle;
-
-  public TypeOracleGenericsSupportTest() throws UnableToCompleteException {
-    moduleDef = ModuleDefLoader.loadFromClassPath(logger,
-        "com.google.gwt.core.ext.typeinfo.TypeOracleTest");
-    typeOracle = moduleDef.getTypeOracle(logger);
-  }
-  
-  public void test() throws NotFoundException {
-    JClassType type = typeOracle.getType(GenericSubclass.class.getName());
-  }
-}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleSuite.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleSuite.java
new file mode 100644
index 0000000..307e7da
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleSuite.java
@@ -0,0 +1,35 @@
+/*
+ * 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.Test;
+import junit.framework.TestSuite;
+
+public class TypeOracleSuite extends TestSuite {
+  public static Test suite() {
+    TestSuite suite = new TestSuite("Test suite for TypeOracle");
+    suite.addTestSuite(AnnotationsTest.class);
+    suite.addTestSuite(JArrayTypeTest.class);
+    suite.addTestSuite(JClassTypeTest.class);
+    suite.addTestSuite(JEnumTypeTest.class);
+    suite.addTestSuite(JGenericTypeTest.class);
+    suite.addTestSuite(JParameterizedTypeTest.class);
+    suite.addTestSuite(JRawTypeTest.class);
+    suite.addTestSuite(JTypeParameterTest.class);
+    suite.addTestSuite(JWildcardTypeTest.class);
+    return suite;
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/Base.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/Base.java
new file mode 100644
index 0000000..8300078
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/Base.java
@@ -0,0 +1,36 @@
+/*
+ * 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.io.Serializable;
+
+/**
+ * Class used to test
+ * {@link com.google.gwt.core.ext.typeinfo.JClassType#getOverridableMethods()}.
+ */
+public class Base<T> {
+  void m(T t) {
+    System.out.println("Base<T>.m(T)");
+  }
+
+  <N extends Number> void m(N n) {
+    System.out.println("Base<T>.m(N)");
+  }
+
+  static <N extends Serializable> void serialize(N n) {
+    System.out.println("Base<T>.<N extends Serializable>.serialize(N)");
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/Derived.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/Derived.java
new file mode 100644
index 0000000..1cf20c7
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/Derived.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.test;
+
+import java.io.Serializable;
+
+/**
+ * Class used to test
+ * {@link com.google.gwt.core.ext.typeinfo.JClassType#getOverridableMethods()}.
+ * 
+ * Derived<T> Overridable Methods (methods from java.lang.Object not shown):
+ * Derived<T>.m(Integer)
+ * Derived<T>.m(Number)
+ * Derived<T>.m(Integer)
+ * Derived<T>.<T extends Serializable> void m(T t)
+ */
+public class Derived<T> extends Base<T> {
+  public void m(Integer i) {
+    System.out.println("Derived<T>.m(Integer)");
+  } // new Overload
+
+  /**
+   * Overrides Base<T>.m(T)
+   * 
+   * NOTE: this is commented out because JDT 3.1 will report it as an error,
+   * even though javac 1.5 allows this.
+   */
+//  @Override
+//  public void m(Object t) {
+//    System.out.println("Derived<T>.m(Object)");
+//  } // 
+
+  /**
+   * Overrides Base<T>.<N extends Number>.m(N)
+   */
+  @Override
+  public void m(Number n) {
+    System.out.println("Derived<T>.m(Number)");
+  } // overrides m(N)
+
+  /**
+   * Overloads m
+   * @param <T>
+   * @param t
+   */
+  public <T extends Serializable> void m(T t) {
+    System.out.println("Derived<T>.<T extends Serializable>.m(T)");
+  }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericClass.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericClass.java
index 085a567..047b3af 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericClass.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericClass.java
@@ -15,21 +15,106 @@
  */
 package com.google.gwt.core.ext.typeinfo.test;
 
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
 /**
- *
+ * Generic class.
+ * 
+ * @param <T>
+ * 
+ * NOTE: It seems that the JDT 3.1 will not allow: GenericClass<Integer> if the
+ * definition of GenericClass is as follows: class GenericClass<T extends
+ * Serializable & Comparable<T>> implements Comparable<T> { ... }
  */
-public class GenericClass<T> { 
-  T t;
-  
+public class GenericClass<T> implements Comparable<T> {
+  /**
+   * Non-static, generic inner class.
+   * 
+   * @param <U>
+   */
+  public class GenericInnerClass<U> {
+    T t2;
+    U u2;
+  }
+
+  /**
+   * This class is not technically a generic class although it has a member that
+   * references a type parameter from its enclosing type.
+   */
+  public class NonGenericInnerClass {
+    T t3;
+  }
+
+  public class Foo {
+    public class Bar {
+      T t4;
+    }
+  }
+
+  /**
+   * Field of an inner class that is enclosed in a parameterized type.
+   */
+  GenericClass<Integer>.NonGenericInnerClass nonGenericInnerClassField;
+
+  /**
+   * NOTE: The following is disabled because it violates an assumption in TOB
+   * line 1228.
+   */
+//  GenericClass.NonGenericInnerClass rawNonGenericInnerClassField;
+
+  GenericClass<Integer>.Foo.Bar wtf;
+
+  Class rawClazzField;
+
+  /**
+   * Field of a raw type.
+   */
+  ArrayList rawFieldType;
+
+  /**
+   * Field of a type parameter type.
+   */
+  T typeParameterField;
+
+  /**
+   * Parameterized with a array type argument.
+   */
+  List<T[]> parameterizedListField;
+
+  public GenericClass() {
+  }
+
   public GenericClass(T t) {
-    this.t = t;
+    this.typeParameterField = t;
   }
-  
+
+  public int compareTo(T o) {
+    // TODO Auto-generated method stub
+    return 0;
+  }
+
   public T getT() {
-    return t;
+    return typeParameterField;
   }
-  
+
+  /*
+   * Generic method
+   */
+  public <U> U max(Collection<U> collection) {
+    return collection.iterator().next();
+  }
+
+  /*
+   * Generic method
+   */
+  public <U> U min(Collection<U> collection) {
+    return collection.iterator().next();
+  }
+
   public void setT(T t) {
-    this.t = t;
+    this.typeParameterField = t;
   }
-}
+}
\ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericSubclass.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericSubclass.java
index 056313a..23187a6 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericSubclass.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericSubclass.java
@@ -16,13 +16,18 @@
 package com.google.gwt.core.ext.typeinfo.test;
 
 /**
- *
+ * Test a generic class that extends a generic class.
  */
 public class GenericSubclass<U> extends GenericClass<U> {
-  // Explore what JDT does when there is a parameterized type
   GenericClass<Integer> child;
-  
+
   public GenericSubclass(U t) {
     super(t);
   }
+
+  // TODO: This triggers a name clash problem with JDT 3.1 but not with JDT
+  // 3.3.0 or with javac 1.5.06.
+  // public void setT(Object t) {
+  // // this should override GenericClass<U>.setT(T t);
+  // }
 }
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyCustomList.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyCustomList.java
new file mode 100644
index 0000000..53ce7ed
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyCustomList.java
@@ -0,0 +1,26 @@
+/*
+ * 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.io.Serializable;
+
+/**
+ * Used to test wildcard card expansion when looking for List<Integer>
+ * subtypes.
+ */
+public class MyCustomList<T extends Serializable & Comparable<T>, U> extends
+    MyList<U> {
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyEnum.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyEnum.java
new file mode 100644
index 0000000..d703461
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyEnum.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.test;
+
+/**
+ * Enumerated type used in the
+ * {@link com.google.gwt.core.ext.typeinfo.JEnumTypeTest}.
+ * 
+ * NOTE: do not reorder the enumerated values
+ */
+public enum MyEnum {
+  @Deprecated
+  VAL0(-1) {
+    @Override
+    public int getId() {
+      return instanceField;
+    }
+  },
+  
+  VAL1(-2) {
+    @Override
+    public int getId() {
+      return instanceField;
+    }
+  },
+
+  VAL2(-3) {
+    @Override
+    public int getId() {
+      return instanceField;
+    }
+  };
+
+  MyEnum(int instanceField) {
+    this.instanceField = instanceField;
+  }
+
+  public final int instanceField;
+
+  public static final MyEnum e = VAL2;
+
+  public abstract int getId();
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyIntegerList.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyIntegerList.java
new file mode 100644
index 0000000..0d1e559
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyIntegerList.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.
+ */
+package com.google.gwt.core.ext.typeinfo.test;
+
+/**
+ * Used to test that this type gets picked up as a subtype of List<Integer>.
+ */
+public class MyIntegerList extends MyList<Integer> {
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyList.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyList.java
new file mode 100644
index 0000000..94fa2f1
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/MyList.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.
+ */
+package com.google.gwt.core.ext.typeinfo.test;
+
+/**
+ * Used for testing parameterized subtypes.
+ */
+public class MyList<T> {
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/NonGenericSubclass.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/NonGenericSubclass.java
index 44413a4..03f98fb 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/NonGenericSubclass.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/NonGenericSubclass.java
@@ -16,7 +16,7 @@
 package com.google.gwt.core.ext.typeinfo.test;
 
 /**
- *
+ * Test a non-generic class that extends a parameterized type.
  */
 public class NonGenericSubclass extends GenericClass<Integer> {
   /**
@@ -24,6 +24,12 @@
    */
   public NonGenericSubclass(Integer t) {
     super(t);
-    // TODO Auto-generated constructor stub
+  }
+  
+  
+  /**
+   * Tests overloading of generic methods. 
+   */
+  public void setT(Integer t) {
   }
 }