1. ApiChecker was broken after FSOD. This patch fixes it. 
2. Simple things like using meaningful variable names and some more java-doc. 
3. Hiding a lot of the API that should be kept private and making classes
immutable where possible. 
4. Other style changes mentioned by toby.
5. Adds unit tests. 
6. Updates the build.
7. Updates the whitelist in the config file. 


Review by: tobyr



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3105 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/tools/api-checker/build.xml b/tools/api-checker/build.xml
index 9251196..9e2691d 100755
--- a/tools/api-checker/build.xml
+++ b/tools/api-checker/build.xml
@@ -42,6 +42,35 @@
 		</gwt.jar>
 	</target>
 
+	<target name="test" depends="build, compile.tests" description="Run unit tests for this project.">
+		<taskdef name="junit" classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTask">
+			<classpath>
+				<pathelement location="${gwt.tools.lib}/junit/junit-3.8.1.jar" />
+				<pathelement location="${gwt.tools.antlib}/ant-junit-1.6.5.jar" />
+			</classpath>
+		</taskdef>
+	
+		<echo message="Writing test results to ${junit.out}/reports" />
+		<mkdir dir="${junit.out}/reports" />
+	
+		<junit dir="${junit.out}" fork="yes" printsummary="yes" haltonfailure="true">
+			<classpath>
+				<pathelement location="test" />
+				<pathelement location="${gwt.build.out}/dev/core/bin"/>
+				<pathelement location="${gwt.build.out}/tools/api-checker/bin"/>
+				<pathelement location="${gwt.tools.lib}/eclipse/jdt-3.3.1.jar"/>
+				<pathelement location="${gwt.tools.lib}/junit/junit-3.8.1.jar" />
+				<pathelement location="${javac.junit.out}" />
+			</classpath>
+	
+			<formatter type="plain" />
+			<formatter type="xml" />
+			<batchtest todir="${junit.out}/reports">
+                          <fileset dir="${javac.junit.out}"
+                            includes="**/*Test.class"/> 
+			</batchtest>
+		</junit>
+	</target>
 
 	<target name="checkstyle" description="Static analysis of source">
 		<gwt.checkstyle>
diff --git a/tools/api-checker/config/gwt14_15userApi.conf b/tools/api-checker/config/gwt14_15userApi.conf
index 679a413..632afdc 100644
--- a/tools/api-checker/config/gwt14_15userApi.conf
+++ b/tools/api-checker/config/gwt14_15userApi.conf
@@ -2,17 +2,18 @@
 
 name_old gwt1462userApi
 #sourceFiles and excludedFiles are specified as colon-separated list of files
-sourceFiles_old @OLDROOT@/dev/core/super/com/google/gwt/dev/jjs/intrinsic:@OLDROOT@/user/super/com/google/gwt/emul:@OLDROOT@/user/src/com/google/gwt/core/client:@OLDROOT@/user/src/com/google/gwt/user/client:@OLDROOT@/user/src/com/google/gwt/http/client:@OLDROOT@/user/com/google/gwt/i18n
-excludedFiles_old @OLDROOT@/user/com/google/gwt/benchmarks
+sourceFiles_old @OLDROOT@/dev/core/super:@OLDROOT@/user/super:@OLDROOT@/user/src
+excludedFiles_old @OLDROOT@/user/src/com/google/gwt/junit:@OLDROOT@/user/src/com/google/gwt/i18n/rebind:@OLDROOT@/user/src/com/google/gwt/i18n/tools:@OLDROOT@/user/src/com/google/gwt/json:@OLDROOT@/user/src/com/google/gwt/user/rebind:@OLDROOT@/user/src/com/google/gwt/user/server:@OLDROOT@/user/src/com/google/gwt/user/tools:@OLDROOT@/user/super/com/google/gwt/junit
 
 ##############################################
 #new Api
 
 name_new gwt15userApi
 #sourceFiles and excludedFiles are specified as colon-separated list of files
-#works with GWT 1.5 Milestone 2, revision 2415
-sourceFiles_new ./dev/core/super/com/google/gwt/dev/jjs/intrinsic:./user/super/com/google/gwt/emul:./user/src/com/google/gwt/core/client:./user/src/com/google/gwt/user/client:./user/src/com/google/gwt/http/client:./user/src/com/google/gwt/dom:./user/src/com/google/gwt/i18n
-excludedFiles_new ./user/src/com/google/gwt/benchmarks:./user/src/com/google/gwt/i18n/rebind:./user/src/com/google/gwt/i18n/tools
+#works with GWT 1.5, revision 2859
+sourceFiles_new ./dev/core/super:./user/super:./user/src
+#:dev/core/src
+excludedFiles_new ./user/src/com/google/gwt/benchmarks:./user/src/com/google/gwt/junit:./user/src/com/google/gwt/i18n/rebind:./user/src/com/google/gwt/i18n/tools:./user/src/com/google/gwt/json:./user/src/com/google/gwt/user/rebind:./user/src/com/google/gwt/user/server:./user/src/com/google/gwt/user/tools:./user/super/com/google/gwt/benchmarks:./user/super/com/google/gwt/junit
 
 
 ##############################################
@@ -20,243 +21,174 @@
 # when adding to the white-list, include comments as to why the addition is
 # being made. This needs to be done for this initial white-list below
 
-  package com.google.gwt.core.client
-    class com.google.gwt.core.client.JavaScriptObject
-      com.google.gwt.core.client.JavaScriptObject::hostedModeReference MISSING
-      com.google.gwt.core.client.JavaScriptObject::equals(Ljava/lang/Object;) FINAL_ADDED
-      com.google.gwt.core.client.JavaScriptObject::hashCode() FINAL_ADDED
-      com.google.gwt.core.client.JavaScriptObject::toString() FINAL_ADDED
-  package com.google.gwt.lang
-    class com.google.gwt.lang.Array
-      com.google.gwt.lang.Array::Array(IIILjava/lang/String;) MISSING
-      com.google.gwt.lang.Array::clonify([Ljava/lang/Object;I) MISSING
-      com.google.gwt.lang.Array::initDims(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;) MISSING
-      com.google.gwt.lang.Array::initValues(Ljava/lang/String;IILjava/lang/Object;) MISSING
-  package com.google.gwt.user.client
-    class com.google.gwt.user.client.Element
-      com.google.gwt.user.client.Element::equals(Ljava/lang/Object;) FINAL_ADDED
-      com.google.gwt.user.client.Element::hashCode() FINAL_ADDED
-      com.google.gwt.user.client.Element::toString() FINAL_ADDED
-    class com.google.gwt.user.client.Event
-      com.google.gwt.user.client.Event::equals(Ljava/lang/Object;) FINAL_ADDED
-      com.google.gwt.user.client.Event::hashCode() FINAL_ADDED
-      com.google.gwt.user.client.Event::toString() FINAL_ADDED
-  package com.google.gwt.user.client.impl
-    class com.google.gwt.user.client.impl.DOMImplMozillaOld
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::appendChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::compare(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::createElement(Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::createInputElement(Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::createInputRadioElement(Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::createSelectElement(Z) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getAbsoluteLeft(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getAbsoluteTop(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getElementAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getElementById(Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getElementProperty(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getElementPropertyBoolean(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getElementPropertyInt(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getFirstChild(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getImgSrc(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getInnerHTML(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getInnerText(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getIntStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getNextSibling(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getParent(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::getStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::iframeGetSrc(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::init() MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::initMozilla() MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::insertBefore(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::insertListItem(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;I) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::isOrHasChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::removeChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::removeElementAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::scrollIntoView(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::setElementAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::setElementProperty(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::setElementPropertyBoolean(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Z) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::setElementPropertyInt(Lcom/google/gwt/user/client/Element;Ljava/lang/String;I) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::setImgSrc(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::setInnerHTML(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::setInnerText(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::setIntStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;I) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::setOptionText(Lcom/google/gwt/user/client/Element;Ljava/lang/String;I) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::setStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::toString(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::windowGetClientHeight() MISSING
-      com.google.gwt.user.client.impl.DOMImplMozillaOld::windowGetClientWidth() MISSING
-    class com.google.gwt.user.client.impl.DOMImplOpera
-      com.google.gwt.user.client.impl.DOMImplOpera::appendChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::compare(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::createElement(Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::createInputElement(Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::createInputRadioElement(Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::createSelectElement(Z) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getAbsoluteLeft(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getAbsoluteTop(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getElementAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getElementById(Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getElementProperty(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getElementPropertyBoolean(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getElementPropertyInt(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getFirstChild(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getImgSrc(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getInnerHTML(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getInnerText(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getIntStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getNextSibling(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getParent(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::getStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::iframeGetSrc(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::init() MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::insertBefore(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::insertListItem(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;I) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::isOrHasChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::removeChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::removeElementAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::scrollIntoView(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::setElementAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::setElementProperty(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::setElementPropertyBoolean(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Z) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::setElementPropertyInt(Lcom/google/gwt/user/client/Element;Ljava/lang/String;I) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::setImgSrc(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::setInnerHTML(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::setInnerText(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::setIntStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;I) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::setOptionText(Lcom/google/gwt/user/client/Element;Ljava/lang/String;I) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::setStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::toString(Lcom/google/gwt/user/client/Element;) MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::windowGetClientHeight() MISSING
-      com.google.gwt.user.client.impl.DOMImplOpera::windowGetClientWidth() MISSING
-  package com.google.gwt.user.client.rpc.core.java.lang
-    com.google.gwt.user.client.rpc.core.java.lang.boolean_Array_CustomFieldSerializer MISSING
-    com.google.gwt.user.client.rpc.core.java.lang.byte_Array_CustomFieldSerializer MISSING
-    com.google.gwt.user.client.rpc.core.java.lang.char_Array_CustomFieldSerializer MISSING
-    com.google.gwt.user.client.rpc.core.java.lang.double_Array_CustomFieldSerializer MISSING
-    com.google.gwt.user.client.rpc.core.java.lang.float_Array_CustomFieldSerializer MISSING
-    com.google.gwt.user.client.rpc.core.java.lang.int_Array_CustomFieldSerializer MISSING
-    com.google.gwt.user.client.rpc.core.java.lang.long_Array_CustomFieldSerializer MISSING
-    com.google.gwt.user.client.rpc.core.java.lang.short_Array_CustomFieldSerializer MISSING
-  package com.google.gwt.user.client.ui
-    class com.google.gwt.user.client.ui.Button
-      com.google.gwt.user.client.ui.Button::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
-      com.google.gwt.user.client.ui.Button::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [protected void setElement(com.google.gwt.user.client.Element elem), protected final void setElement(com.google.gwt.dom.client.Element elem)] This might break API source compatibility
-    class com.google.gwt.user.client.ui.ChangeListenerCollection
-      com.google.gwt.user.client.ui.ChangeListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.ChangeListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.ChangeListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.CheckBox
-      com.google.gwt.user.client.ui.CheckBox::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
-      com.google.gwt.user.client.ui.CheckBox::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [protected void setElement(com.google.gwt.user.client.Element elem), protected final void setElement(com.google.gwt.dom.client.Element elem)] This might break API source compatibility
-    class com.google.gwt.user.client.ui.ClickListenerCollection
-      com.google.gwt.user.client.ui.ClickListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.ClickListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.ClickListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.FocusListenerCollection
-      com.google.gwt.user.client.ui.FocusListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.FocusListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.FocusListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.FormHandlerCollection
-      com.google.gwt.user.client.ui.FormHandlerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.FormHandlerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.FormHandlerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.KeyboardListenerCollection
-      com.google.gwt.user.client.ui.KeyboardListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.KeyboardListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.KeyboardListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.ListBox
-      com.google.gwt.user.client.ui.ListBox::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [protected final void setElement(com.google.gwt.dom.client.Element elem), protected void setElement(com.google.gwt.user.client.Element elem)] This might break API source compatibility
-      com.google.gwt.user.client.ui.ListBox::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
-    class com.google.gwt.user.client.ui.LoadListenerCollection
-      com.google.gwt.user.client.ui.LoadListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.LoadListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.LoadListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.MenuItem
-      com.google.gwt.user.client.ui.MenuItem::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
-      com.google.gwt.user.client.ui.MenuItem::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [protected final void setElement(com.google.gwt.dom.client.Element elem), protected void setElement(com.google.gwt.user.client.Element elem)] This might break API source compatibility
-    class com.google.gwt.user.client.ui.MouseListenerCollection
-      com.google.gwt.user.client.ui.MouseListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.MouseListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.MouseListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.MouseWheelListenerCollection
-      com.google.gwt.user.client.ui.MouseWheelListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.MouseWheelListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.MouseWheelListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.PopupListenerCollection
-      com.google.gwt.user.client.ui.PopupListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.PopupListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.PopupListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.PushButton
-      com.google.gwt.user.client.ui.PushButton::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [protected final void setElement(com.google.gwt.dom.client.Element elem), protected void setElement(com.google.gwt.user.client.Element elem)] This might break API source compatibility
-      com.google.gwt.user.client.ui.PushButton::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
-    class com.google.gwt.user.client.ui.RichTextArea
-      com.google.gwt.user.client.ui.RichTextArea::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [protected final void setElement(com.google.gwt.dom.client.Element elem), protected void setElement(com.google.gwt.user.client.Element elem)] This might break API source compatibility
-      com.google.gwt.user.client.ui.RichTextArea::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
-    class com.google.gwt.user.client.ui.ScrollListenerCollection
-      com.google.gwt.user.client.ui.ScrollListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.ScrollListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.ScrollListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.TabListenerCollection
-      com.google.gwt.user.client.ui.TabListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.TabListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.TabListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.TableListenerCollection
-      com.google.gwt.user.client.ui.TableListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.TableListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.TableListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.TextBoxBase
-      com.google.gwt.user.client.ui.TextBoxBase::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [protected void setElement(com.google.gwt.user.client.Element elem), protected final void setElement(com.google.gwt.dom.client.Element elem)] This might break API source compatibility
-      com.google.gwt.user.client.ui.TextBoxBase::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
-    class com.google.gwt.user.client.ui.ToggleButton
-      com.google.gwt.user.client.ui.ToggleButton::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [protected void setElement(com.google.gwt.user.client.Element elem), protected final void setElement(com.google.gwt.dom.client.Element elem)] This might break API source compatibility
-      com.google.gwt.user.client.ui.ToggleButton::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
-    class com.google.gwt.user.client.ui.TreeItem
-      com.google.gwt.user.client.ui.TreeItem::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
-      com.google.gwt.user.client.ui.TreeItem::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [protected final void setElement(com.google.gwt.dom.client.Element elem), protected void setElement(com.google.gwt.user.client.Element elem)] This might break API source compatibility
-    class com.google.gwt.user.client.ui.TreeListenerCollection
-      com.google.gwt.user.client.ui.TreeListenerCollection::add(ILjava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.TreeListenerCollection::add(Ljava/lang/Object;) MISSING
-      com.google.gwt.user.client.ui.TreeListenerCollection::set(ILjava/lang/Object;) MISSING
-    class com.google.gwt.user.client.ui.Widget
-      com.google.gwt.user.client.ui.Widget::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
-      com.google.gwt.user.client.ui.Widget::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [protected final void setElement(com.google.gwt.dom.client.Element elem), protected void setElement(com.google.gwt.user.client.Element elem)] This might break API source compatibility
-  package java.lang
-    class java.lang.Byte
-      java.lang.Byte::compareTo(Ljava/lang/Object;) MISSING
-    class java.lang.Character
-      java.lang.Character::compareTo(Ljava/lang/Object;) MISSING
-    java.lang.Class FINAL_ADDED
-    class java.lang.Double
-      java.lang.Double::compareTo(Ljava/lang/Object;) MISSING
-    class java.lang.Float
-      java.lang.Float::compareTo(Ljava/lang/Object;) MISSING
-    class java.lang.Integer
-      java.lang.Integer::compareTo(Ljava/lang/Object;) MISSING
-    class java.lang.Long
-      java.lang.Long::compareTo(Ljava/lang/Object;) MISSING
-    class java.lang.Number
-      java.lang.Number::__hexDigits MISSING
-      java.lang.Number::__decodeAndValidateLong(Ljava/lang/String;JJ) MISSING
-      java.lang.Number::__parseAndValidateLong(Ljava/lang/String;IJJ) MISSING
-    class java.lang.Object
-      java.lang.Object::typeName MISSING
-    class java.lang.Short
-      java.lang.Short::compareTo(Ljava/lang/Object;) MISSING
-    class java.lang.String
-      java.lang.String::String([CII) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [public String(char[] value, int offset, int count), public String(int[] codePoints, int offset, int count)] This might break API source compatibility
-      java.lang.String::compareTo(Ljava/lang/Object;) MISSING
-    class java.lang.StringBuffer
-      java.lang.StringBuffer::append([CII) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [public java.lang.StringBuffer append(char[] x, int start, int len), public java.lang.StringBuffer append(java.lang.CharSequence x, int start, int end)] This might break API source compatibility
-      java.lang.StringBuffer::insert(I[CII) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [public java.lang.StringBuffer insert(int index, char[] x, int offset, int len), public java.lang.StringBuffer insert(int index, java.lang.CharSequence chars, int start, int end)] This might break API source compatibility
-  package java.util
-    class java.util.ArrayList
-      java.util.ArrayList::indexOutOfBounds(I) MISSING
-    class java.util.Arrays
-      java.util.Arrays::sort([Ljava/lang/Object;) OVERLOADED_METHOD_CALL Many methods in the new API with similar signatures. Methods = [public static void sort(float[] array), public static void sort(short[] array), public static void sort(double[] array), public static void sort(java.lang.Object[] array), public static void sort(byte[] array), public static void sort(long[] array), public static void sort(int[] array)] This might break API source compatibility
-    class java.util.Date
-      java.util.Date::__parse(Ljava/lang/String;) MISSING
-      java.util.Date::compareTo(Ljava/lang/Object;) MISSING
-    class java.util.Vector
-      java.util.Vector::indexOutOfBounds(I) MISSING
-
+com.google.gwt.core.client.JavaScriptObject::equals(Ljava/lang/Object;) FINAL_ADDED
+com.google.gwt.core.client.JavaScriptObject::hashCode() FINAL_ADDED
+com.google.gwt.core.client.JavaScriptObject::hostedModeReference MISSING
+com.google.gwt.core.client.JavaScriptObject::toString() FINAL_ADDED
+com.google.gwt.i18n.client.DateTimeFormat::parse(Ljava/lang/String;) EXCEPTION_TYPE_ERROR
+com.google.gwt.i18n.client.NumberFormat::NumberFormat(Lcom/google/gwt/i18n/client/constants/NumberConstants;Lcom/google/gwt/i18n/client/constants/CurrencyCodeMapConstants;Ljava/lang/String;Ljava/lang/String;) MISSING
+com.google.gwt.i18n.client.NumberFormat::NumberFormat(Ljava/lang/String;Ljava/lang/String;) MISSING
+com.google.gwt.i18n.client.NumberFormat::parse(Ljava/lang/String;) EXCEPTION_TYPE_ERROR
+com.google.gwt.i18n.client.NumberFormat::parse(Ljava/lang/String;[I) EXCEPTION_TYPE_ERROR
+com.google.gwt.i18n.client.impl.ConstantMap::put(Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.lang.Array::Array(IIILjava/lang/String;) MISSING
+com.google.gwt.lang.Array::clonify([Ljava/lang/Object;I) MISSING
+com.google.gwt.lang.Array::initDims(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.lang.Array::initValues(Ljava/lang/String;IILjava/lang/Object;) MISSING
+com.google.gwt.user.client.Element::equals(Ljava/lang/Object;) FINAL_ADDED
+com.google.gwt.user.client.Element::hashCode() FINAL_ADDED
+com.google.gwt.user.client.Element::toString() FINAL_ADDED
+com.google.gwt.user.client.Event::equals(Ljava/lang/Object;) FINAL_ADDED
+com.google.gwt.user.client.Event::hashCode() FINAL_ADDED
+com.google.gwt.user.client.Event::toString() FINAL_ADDED
+com.google.gwt.user.client.impl.DOMImpl::appendChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::compare(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::createElement(Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::createInputElement(Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::createInputRadioElement(Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::createSelectElement(Z) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getAbsoluteLeft(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getAbsoluteTop(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getElementAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getElementById(Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getElementProperty(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getElementPropertyBoolean(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getElementPropertyInt(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getFirstChild(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getImgSrc(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getInnerHTML(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getInnerText(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getIntStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getNextSibling(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getParent(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::getStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::init() MISSING
+com.google.gwt.user.client.impl.DOMImpl::insertBefore(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::insertListItem(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;I) MISSING
+com.google.gwt.user.client.impl.DOMImpl::isOrHasChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::removeChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::removeElementAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::scrollIntoView(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::setElementAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::setElementProperty(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::setElementPropertyBoolean(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Z) MISSING
+com.google.gwt.user.client.impl.DOMImpl::setElementPropertyInt(Lcom/google/gwt/user/client/Element;Ljava/lang/String;I) MISSING
+com.google.gwt.user.client.impl.DOMImpl::setImgSrc(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::setInnerHTML(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::setInnerText(Lcom/google/gwt/user/client/Element;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::setIntStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;I) MISSING
+com.google.gwt.user.client.impl.DOMImpl::setOptionText(Lcom/google/gwt/user/client/Element;Ljava/lang/String;I) MISSING
+com.google.gwt.user.client.impl.DOMImpl::setStyleAttribute(Lcom/google/gwt/user/client/Element;Ljava/lang/String;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::toString(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImpl::windowGetClientHeight() MISSING
+com.google.gwt.user.client.impl.DOMImpl::windowGetClientWidth() MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::compare(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::createInputRadioElement(Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::getAbsoluteLeft(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::getAbsoluteTop(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::getFirstChild(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::getNextSibling(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::getParent(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::iframeGetSrc(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::init() MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::initMozilla() MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::isOrHasChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplMozillaOld::toString(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplOpera::compare(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplOpera::createInputRadioElement(Ljava/lang/String;) MISSING
+com.google.gwt.user.client.impl.DOMImplOpera::getAbsoluteLeft(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplOpera::getAbsoluteTop(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplOpera::getFirstChild(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplOpera::getNextSibling(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplOpera::getParent(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplOpera::iframeGetSrc(Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.impl.DOMImplOpera::init() MISSING
+com.google.gwt.user.client.impl.DOMImplOpera::isOrHasChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;) MISSING
+com.google.gwt.user.client.rpc.core.java.lang.boolean_Array_CustomFieldSerializer MISSING
+com.google.gwt.user.client.rpc.core.java.lang.byte_Array_CustomFieldSerializer MISSING
+com.google.gwt.user.client.rpc.core.java.lang.char_Array_CustomFieldSerializer MISSING
+com.google.gwt.user.client.rpc.core.java.lang.double_Array_CustomFieldSerializer MISSING
+com.google.gwt.user.client.rpc.core.java.lang.float_Array_CustomFieldSerializer MISSING
+com.google.gwt.user.client.rpc.core.java.lang.int_Array_CustomFieldSerializer MISSING
+com.google.gwt.user.client.rpc.core.java.lang.long_Array_CustomFieldSerializer MISSING
+com.google.gwt.user.client.rpc.core.java.lang.short_Array_CustomFieldSerializer MISSING
+com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader::rememberDecodedObject(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter::writeLong(J) ABSTRACT_ADDED
+com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter::writeLong(J) EXCEPTION_TYPE_ERROR
+com.google.gwt.user.client.ui.ChangeListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.ChangeListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.ChangeListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.ClickListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.ClickListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.ClickListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.FocusListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.FocusListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.FocusListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.FocusWidget::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
+com.google.gwt.user.client.ui.FocusWidget::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL
+com.google.gwt.user.client.ui.FormHandlerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.FormHandlerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.FormHandlerCollection::fireOnComplete(Ljava/lang/Object;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.ui.FormHandlerCollection::fireOnSubmit(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.FormHandlerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.FormSubmitCompleteEvent::FormSubmitCompleteEvent(Ljava/lang/Object;Ljava/lang/String;) MISSING
+com.google.gwt.user.client.ui.FormSubmitEvent::FormSubmitEvent(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.KeyboardListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.KeyboardListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.KeyboardListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.LoadListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.LoadListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.LoadListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.MouseListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.MouseListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.MouseListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.MouseWheelListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.MouseWheelListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.MouseWheelListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.PopupListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.PopupListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.PopupListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.ScrollListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.ScrollListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.ScrollListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.TabListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.TabListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.TabListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.TableListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.TableListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.TableListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.TreeListenerCollection::add(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.TreeListenerCollection::add(Ljava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.TreeListenerCollection::set(ILjava/lang/Object;) MISSING
+com.google.gwt.user.client.ui.UIObject::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
+com.google.gwt.user.client.ui.UIObject::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL
+com.google.gwt.user.client.ui.Widget::setElement(Lcom/google/gwt/user/client/Element;) FINAL_ADDED
+com.google.gwt.user.client.ui.Widget::setElement(Lcom/google/gwt/user/client/Element;) OVERLOADED_METHOD_CALL
+java.lang.Byte::compareTo(Ljava/lang/Object;) MISSING
+java.lang.Character::compareTo(Ljava/lang/Object;) MISSING
+java.lang.Class FINAL_ADDED
+java.lang.Double::compareTo(Ljava/lang/Object;) MISSING
+java.lang.Float::compareTo(Ljava/lang/Object;) MISSING
+java.lang.IllegalArgumentException::IllegalArgumentException(Ljava/lang/String;) OVERLOADED_METHOD_CALL
+java.lang.Integer::compareTo(Ljava/lang/Object;) MISSING
+java.lang.Long::compareTo(Ljava/lang/Object;) MISSING
+java.lang.Number::__decodeAndValidateLong(Ljava/lang/String;JJ) MISSING
+java.lang.Number::__hexDigits MISSING
+java.lang.Number::__parseAndValidateLong(Ljava/lang/String;IJJ) MISSING
+java.lang.Object::typeId MISSING
+java.lang.Object::typeName MISSING
+java.lang.Short::compareTo(Ljava/lang/Object;) MISSING
+java.lang.StackTraceElement FINAL_ADDED
+java.lang.StackTraceElement::finalize() MISSING
+java.lang.String::String([CII) OVERLOADED_METHOD_CALL
+java.lang.String::compareTo(Ljava/lang/Object;) MISSING
+java.lang.StringBuffer::append([CII) OVERLOADED_METHOD_CALL
+java.lang.StringBuffer::insert(I[CII) OVERLOADED_METHOD_CALL
+java.lang.UnsupportedOperationException::UnsupportedOperationException(Ljava/lang/String;) OVERLOADED_METHOD_CALL
+java.util.AbstractList::indexOutOfBounds(I) MISSING
+java.util.ArrayList::indexOf(Ljava/lang/Object;I) MISSING
+java.util.ArrayList::lastIndexOf(Ljava/lang/Object;I) MISSING
+java.util.ArrayList::setSize(I) MISSING
+java.util.Arrays::sort([Ljava/lang/Object;) OVERLOADED_METHOD_CALL
+java.util.Date::__parse(Ljava/lang/String;) MISSING
+java.util.Date::compareTo(Ljava/lang/Object;) MISSING
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiAbstractMethod.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiAbstractMethod.java
index 4d86526..7089ef5 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiAbstractMethod.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiAbstractMethod.java
@@ -16,17 +16,19 @@
 package com.google.gwt.tools.apichecker;
 
 import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
+import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JParameter;
 import com.google.gwt.core.ext.typeinfo.JType;
 
-import java.util.ArrayList;
+import java.util.List;
 
 /**
  * abstract super-class for ApiMethod and ApiConstructor.
  */
-public abstract class ApiAbstractMethod {
+abstract class ApiAbstractMethod implements Comparable<ApiAbstractMethod>,
+    ApiElement {
 
-  public static String computeApiSignature(JAbstractMethod method) {
+  static String computeApiSignature(JAbstractMethod method) {
     String className = method.getEnclosingType().getQualifiedSourceName();
     StringBuffer sb = new StringBuffer();
     sb.append(className);
@@ -35,7 +37,7 @@
     return sb.toString();
   }
 
-  public static String computeInternalSignature(JAbstractMethod method) {
+  static String computeInternalSignature(JAbstractMethod method) {
     StringBuffer sb = new StringBuffer();
     sb.append(method.getName());
     sb.append("(");
@@ -49,44 +51,66 @@
     return sb.toString();
   }
 
-  ApiClass apiClass;
-
+  final ApiClass apiClass;
   String apiSignature = null;
-
-  JAbstractMethod method;
+  final JAbstractMethod method;
+  String relativeSignature = null;
 
   public ApiAbstractMethod(JAbstractMethod method, ApiClass apiClass) {
     this.method = method;
     this.apiClass = apiClass;
   }
 
-  public String getApiSignature() {
-    if (apiSignature == null) {
-      apiSignature = computeApiSignature();
-    }
-    return apiSignature;
+  public int compareTo(ApiAbstractMethod other) {
+    return getRelativeSignature().compareTo(other.getRelativeSignature());
   }
 
-  // for a non-primitive type, someone can pass it null as a parameter
-  public String getCoarseSignature() {
-    StringBuffer returnStr = new StringBuffer();
-    JParameter[] parameters = method.getParameters();
-    for (JParameter parameter : parameters) {
-      JType type = parameter.getType();
-      if (type.isPrimitive() != null) {
-        returnStr.append(type.getJNISignature());
-      } else {
-        returnStr.append("c");
-      }
-      returnStr.append(";"); // to mark the end of a type
+  /**
+   * Used in set comparisons.
+   */
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof ApiAbstractMethod) {
+      ApiAbstractMethod other = (ApiAbstractMethod) o;
+      return getApiSignature().equals(other.getApiSignature());
     }
-    return returnStr.toString();
+    return false;
   }
 
-  public JAbstractMethod getMethodObject() {
+  public JAbstractMethod getMethod() {
     return method;
   }
 
+  public String getRelativeSignature() {
+    if (relativeSignature == null) {
+      relativeSignature = computeRelativeSignature();
+    }
+    return relativeSignature;
+  }
+
+  @Override
+  public int hashCode() {
+    return getApiSignature().hashCode();
+  }
+
+  public boolean isCompatible(ApiAbstractMethod methodInNew) {
+    JParameter[] parametersInNew = methodInNew.getMethod().getParameters();
+    int length = parametersInNew.length;
+    if (length != method.getParameters().length) {
+      return false;
+    }
+    for (int i = 0; i < length; i++) {
+      if (!ApiDiffGenerator.isFirstTypeAssignableToSecond(
+          method.getParameters()[i].getType(), parametersInNew[i].getType())) {
+        return false;
+      }
+    }
+    // Control reaches here iff methods are compatible with respect to
+    // parameters and return type. For source compatibility, I do not need to
+    // check in which classes the methods are declared.
+    return true;
+  }
+
   // Not sure the above implementation is sufficient. If need be, look at the
   // implementation below.
   // public String getCoarseSignature() {
@@ -109,27 +133,6 @@
   // return returnStr.toString();
   // }
 
-  public abstract ArrayList<ApiChange.Status> getModifierChanges(
-      ApiAbstractMethod newMethod);
-
-  public boolean isCompatible(ApiAbstractMethod methodInNew) {
-    JParameter[] parametersInNew = methodInNew.getMethodObject().getParameters();
-    int length = parametersInNew.length;
-    if (length != method.getParameters().length) {
-      return false;
-    }
-    for (int i = 0; i < length; i++) {
-      if (!ApiDiffGenerator.isFirstTypeAssignableToSecond(
-          method.getParameters()[i].getType(), parametersInNew[i].getType())) {
-        return false;
-      }
-    }
-    // Control reaches here iff methods are compatible with respect to
-    // parameters and return type. For source compatibility, I do not need to
-    // check in which classes the methods are declared.
-    return true;
-  }
-
   @Override
   public String toString() {
     return method.toString();
@@ -141,4 +144,48 @@
 
   abstract ApiChange checkReturnTypeCompatibility(ApiAbstractMethod newMethod);
 
+  String getApiSignature() {
+    if (apiSignature == null) {
+      apiSignature = computeApiSignature();
+    }
+    return apiSignature;
+  }
+
+  // for a non-primitive type, someone can pass it null as a parameter
+  String getCoarseSignature() {
+    StringBuffer returnStr = new StringBuffer();
+    JParameter[] parameters = method.getParameters();
+    for (JParameter parameter : parameters) {
+      JType type = parameter.getType();
+      if (type.isPrimitive() != null) {
+        returnStr.append(type.getJNISignature());
+      } else {
+        returnStr.append("c");
+      }
+      returnStr.append(";"); // to mark the end of a type
+    }
+    return returnStr.toString();
+  }
+
+  /**
+   * Find changes in modifiers. returns a possibly immutable list.
+   * 
+   */
+  abstract List<ApiChange.Status> getModifierChanges(ApiAbstractMethod newMethod);
+
+  private String computeRelativeSignature() {
+    String signature = computeInternalSignature(method);
+    if (ApiCompatibilityChecker.DEBUG) {
+      JClassType enclosingType = method.getEnclosingType();
+      return apiClass.getClassObject().getQualifiedSourceName()
+          + "::"
+          + signature
+          + " defined in "
+          + (enclosingType == null ? "null enclosing type "
+              : enclosingType.getQualifiedSourceName());
+    }
+    return apiClass.getClassObject().getQualifiedSourceName() + "::"
+        + signature;
+  }
+
 }
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiChange.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiChange.java
index 2bdd6dc..323454f 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiChange.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiChange.java
@@ -16,6 +16,10 @@
 
 package com.google.gwt.tools.apichecker;
 
+
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * 
  * An ApiChange message.Each message is a Status followed by an optional String
@@ -23,73 +27,91 @@
  * 
  */
 
-public class ApiChange {
+final class ApiChange implements Comparable<ApiChange> {
 
   // add specific changes. For example, add FINAL_ADDED and
   // ABSTRACT_ADDED instead of API_ATTRIBUTES_CHANGED
-  static enum Status {
-    ABSTRACT_ADDED("ABSTRACT_ADDED"),
+  enum Status {
+    ABSTRACT_ADDED,
 
-    ATTRIBUTES_WARNING("ATTRIBUTES_CHANGED_WARNING"),
+    ATTRIBUTES_CHANGED_WARNING,
 
-    COMPATIBLE("COMPATIBLE"),
+    COMPATIBLE,
 
-    COMPATIBLE_WITH("COMPATIBLE_WITH"),
+    COMPATIBLE_WITH,
 
-    EXCEPTIONS_ERROR("EXCEPTION_TYPE_ERROR"),
+    EXCEPTION_TYPE_ERROR,
 
-    FINAL_ADDED("FINAL_ADDED"),
+    FINAL_ADDED,
 
-    MISSING("MISSING"),
+    MISSING,
 
-    NONABSTRACT_CLASS_MADE_INTERFACE("NONABSTRACT_CLASS_MADE_INTERFACE"),
+    NONABSTRACT_CLASS_MADE_INTERFACE,
 
-    OVERLOADED("OVERLOADED_METHOD_CALL"),
+    OVERLOADED_METHOD_CALL,
 
-    RETURN_TYPE_ERROR("RETURN_TYPE_ERROR"),
+    RETURN_TYPE_ERROR,
 
-    STATIC_ADDED("STATIC_ADDED"),
+    STATIC_ADDED,
 
-    STATIC_REMOVED("STATIC_REMOVED"),
+    STATIC_REMOVED,
 
-    SUBCLASSABLE_API_CLASS_MADE_INTERFACE("SUBCLASSABLE_CLASS_MADE_INTERFACE"),
+    SUBCLASSABLE_API_CLASS_MADE_INTERFACE,
 
-    SUBCLASSABLE_API_INTERFACE_MADE_CLASS("SUBCLASSABLE_INTERFACE_MADE_CLASS");
+    SUBCLASSABLE_API_INTERFACE_MADE_CLASS,
+  }
 
-    private final String str;
-
-    Status(String str) {
-      this.str = str;
-    }
-
-    @Override
-    public final String toString() {
-      return str;
+  private static Map<String, Status> cache = new HashMap<String, Status>();
+  static {
+    for (Status tempStatus : Status.values()) {
+      cache.put(tempStatus.name(), tempStatus);
     }
   }
 
+  public static boolean contains(String str) {
+    return cache.get(str) != null;
+  }
+
+  private ApiElement element = null;
   private String message = null;
+
   private Status status = null;
 
-  public ApiChange(Status status) {
-    this.status = status;
+  private String stringRepresentation = null;
+
+  public ApiChange(ApiElement element, Status status) {
+    this(element, status, null);
   }
 
-  public ApiChange(Status status, String message) {
+  public ApiChange(ApiElement element, Status status, String message) {
+    this.element = element;
     this.status = status;
     this.message = message;
   }
 
+  public int compareTo(ApiChange arg0) {
+    return this.toString().compareTo(arg0.toString());
+  }
+
+  public ApiElement getApiElement() {
+    return element;
+  }
+
+  public String getMessage() {
+    return message;
+  }
+
   public Status getStatus() {
     return status;
   }
- 
+
   @Override
   public String toString() {
-    if (message != null) {
-      return status + " " + message;
+    if (stringRepresentation == null) {
+      stringRepresentation =
+          element.getRelativeSignature() + ApiDiffGenerator.DELIMITER
+              + status.name();
     }
-    return status.toString();
+    return stringRepresentation;
   }
-
 }
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiClass.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiClass.java
index f2a0d74..c883930 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiClass.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiClass.java
@@ -17,226 +17,160 @@
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
-import com.google.gwt.core.ext.typeinfo.JMethod;
 import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.core.ext.typeinfo.JConstructor;
 import com.google.gwt.core.ext.typeinfo.JField;
+import com.google.gwt.core.ext.typeinfo.JMethod;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Encapsulates an API class.
  */
-public class ApiClass {
+final class ApiClass implements Comparable<ApiClass>, ApiElement {
   /**
    * Enum for indexing the common storage used for methods and constructors
    * 
    */
   public static enum MethodType {
-    CONSTRUCTOR(0), METHOD(1);
-
-    private final int id;
-
-    MethodType(int id) {
-      this.id = id;
-    }
-
-    public int getId() {
-      return id;
-    }
+    CONSTRUCTOR, METHOD;
   }
 
-  public static String computeFieldApiSignature(JField field) {
-    return field.getEnclosingType().getQualifiedSourceName() + "::"
-        + field.getName();
-  }
+  private HashMap<String, ApiField> apiFields = null;
 
   /**
-   * Assumption: Clients may sub-class an API class, but they will not add their
-   * class to the package.
+   * TODO (amitmanjhi): Toby felt that combining structures for storing
+   * MethodType and Constructors was unnecessary. In particular, the hashMap of
+   * name#args -> object is meaningless for constructor, since name is empty for
+   * constructors. Make it separate.
    * 
-   * Notes: -- A class with only private constructors can be an Api class.
+   * In addition, the current method fails when constructors or methods accept
+   * variable arguments. In future, just index everything by name [and not add
+   * the number of arguments that they accept].
    */
-  public static boolean isApiClass(JClassType classType) {
-    // check for outer classes
-    if (isPublicOuterClass(classType)) {
-      return true;
-    }
-    // if classType is not a member type, return false
-    if (!classType.isMemberType()) {
-      return false;
-    }
-    JClassType enclosingType = classType.getEnclosingType();
-    if (classType.isPublic()) {
-      return isApiClass(enclosingType) || isAnySubtypeAnApiClass(enclosingType);
-    }
-    if (classType.isProtected()) {
-      return isSubclassableApiClass(enclosingType)
-          || isAnySubtypeASubclassableApiClass(enclosingType);
-    }
-    return false;
-  }
-
-  public static boolean isInstantiableApiClass(JClassType classType) {
-    return !classType.isAbstract()
-        && hasPublicOrProtectedConstructor(classType);
-  }
-
-  public static boolean isNotsubclassableApiClass(JClassType classType) {
-    return isApiClass(classType) && !isSubclassable(classType);
-  }
-
-  /**
-   * @return returns true if classType is public AND an outer class
-   */
-  public static boolean isPublicOuterClass(JClassType classType) {
-    return classType.isPublic() && !classType.isMemberType();
-  }
-
-  public static boolean isSubclassable(JClassType classType) {
-    return !classType.isFinal() && hasPublicOrProtectedConstructor(classType);
-  }
-
-  public static boolean isSubclassableApiClass(JClassType classType) {
-    return isApiClass(classType) && isSubclassable(classType);
-  }
-
-  private static boolean hasPublicOrProtectedConstructor(JClassType classType) {
-    JConstructor[] constructors = classType.getConstructors();
-    for (JConstructor constructor : constructors) {
-      if (constructor.isPublic() || constructor.isProtected()) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private static boolean isAnySubtypeAnApiClass(JClassType classType) {
-    JClassType subTypes[] = classType.getSubtypes();
-    for (JClassType tempType : subTypes) {
-      if (isApiClass(tempType)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private static boolean isAnySubtypeASubclassableApiClass(JClassType classType) {
-    JClassType subTypes[] = classType.getSubtypes();
-    for (JClassType tempType : subTypes) {
-      if (isSubclassableApiClass(tempType)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private HashMap<String, JField> apiFields = null;
 
   /**
    * 2 entries in the list: one for CONSTRUCTOR, and the other for METHOD. Each
    * entry is a mapping from MethodName#args to a set of ApiAbstractMethod
    */
-  private ArrayList<HashMap<String, HashSet<ApiAbstractMethod>>> apiMembersByName = new ArrayList<HashMap<String, HashSet<ApiAbstractMethod>>>(
-      MethodType.values().length);
+  private EnumMap<MethodType, Map<String, Set<ApiAbstractMethod>>> apiMembersByName =
+      null;
 
-  private ApiPackage apiPackage = null;
+  private final ApiPackage apiPackage;
+  private final JClassType classType;
 
-  private JClassType classType = null;
+  private final boolean isInstantiableApiClass;
+  private final boolean isNotsubclassableApiClass;
+  private final boolean isSubclassableApiClass;
+  private final TreeLogger logger;
 
-  private boolean isInstantiableApiClass = false;
-
-  private boolean isNotsubclassableApiClass = false;
-  private boolean isSubclassableApiClass = false;
-  private TreeLogger logger = null;
-
-  public ApiClass(JClassType classType, ApiPackage apiPackage) {
+  ApiClass(JClassType classType, ApiPackage apiPackage) {
     this.classType = classType;
     this.apiPackage = apiPackage;
     logger = apiPackage.getApiContainer().getLogger();
-    isSubclassableApiClass = isSubclassableApiClass(classType);
-    isNotsubclassableApiClass = isNotsubclassableApiClass(classType);
-    isInstantiableApiClass = isInstantiableApiClass(classType);
+    ApiContainer apiContainer = apiPackage.getApiContainer();
+    isSubclassableApiClass = apiContainer.isSubclassableApiClass(classType);
+    isNotsubclassableApiClass =
+        apiContainer.isNotsubclassableApiClass(classType);
+    isInstantiableApiClass = apiContainer.isInstantiableApiClass(classType);
   }
 
-  public <E> String computeRelativeSignature(E element) {
-    String signature = element.toString();
-    JClassType enclosingType = null;
-    if (element instanceof JField) {
-      JField field = (JField) element;
-      signature = field.getName();
-      enclosingType = field.getEnclosingType();
-    }
-    if (element instanceof JAbstractMethod) {
-      JAbstractMethod jam = (JAbstractMethod) element;
-      signature = ApiAbstractMethod.computeInternalSignature(jam);
-      enclosingType = jam.getEnclosingType();
-    }
-    if (ApiCompatibilityChecker.DEBUG) {
-      return classType.getQualifiedSourceName()
-          + "::"
-          + signature
-          + " defined in "
-          + (enclosingType == null ? "null enclosing type "
-              : enclosingType.getQualifiedSourceName());
-    }
-    return classType.getQualifiedSourceName() + "::" + signature;
+  public int compareTo(ApiClass other) {
+    return getName().compareTo(other.getName());
   }
 
-  public JField getApiFieldByName(String name) {
+  public String getRelativeSignature() {
+    return classType.getQualifiedSourceName();
+  }
+
+  @Override
+  public String toString() {
+    return classType.toString();
+  }
+
+  String getApiAsString() {
+    StringBuffer sb = new StringBuffer();
+    sb.append("\t" + getName() + "\n");
+    if (apiFields != null) {
+      ArrayList<ApiField> apiFieldsList =
+          new ArrayList<ApiField>(apiFields.values());
+      Collections.sort(apiFieldsList);
+      for (ApiField apiField : apiFieldsList) {
+        sb.append("\t\t" + apiField.getRelativeSignature() + "\n");
+      }
+    }
+    if (apiMembersByName != null
+        && apiMembersByName.get(MethodType.METHOD) != null) {
+      for (MethodType method : MethodType.values()) {
+        HashSet<ApiAbstractMethod> apiMethodsSet =
+            new HashSet<ApiAbstractMethod>();
+        for (Set<ApiAbstractMethod> methodsSets : apiMembersByName.get(method).values()) {
+          apiMethodsSet.addAll(methodsSets);
+        }
+        ArrayList<ApiAbstractMethod> apiMethodsList =
+            new ArrayList<ApiAbstractMethod>(apiMethodsSet);
+        Collections.sort(apiMethodsList);
+        for (ApiAbstractMethod apiMethod : apiMethodsList) {
+          sb.append("\t\t" + apiMethod.getRelativeSignature() + "\n");
+        }
+      }
+    }
+    return sb.toString();
+  }
+
+  ApiField getApiFieldByName(String name) {
     return apiFields.get(name);
   }
 
-  public HashSet<String> getApiFieldNames() {
+  Set<String> getApiFieldNames() {
     if (apiFields == null) {
       initializeApiFields();
     }
     return new HashSet<String>(apiFields.keySet());
   }
 
-  public HashSet<JField> getApiFieldsBySet(HashSet<String> names) {
-    HashSet<JField> ret = new HashSet<JField>();
-    String tempStrings[] = names.toArray(new String[0]);
-    for (String temp : tempStrings) {
-      ret.add(apiFields.get(temp));
+  Set<ApiField> getApiFieldsBySet(Set<String> names) {
+    Set<ApiField> ret = new HashSet<ApiField>();
+    for (String name : names) {
+      ret.add(apiFields.get(name));
     }
     return ret;
   }
 
-  public HashSet<String> getApiMemberNames(MethodType type) {
-    if (apiMembersByName.size() == 0) {
-      initializeApiConstructorsAndFields();
+  Set<String> getApiMemberNames(MethodType type) {
+    if (apiMembersByName == null) {
+      initializeApiConstructorsAndMethods();
     }
-    return new HashSet<String>(apiMembersByName.get(type.getId()).keySet());
+    return new HashSet<String>(apiMembersByName.get(type).keySet());
   }
 
-  public HashSet<ApiAbstractMethod> getApiMembersBySet(
-      HashSet<String> methodNames, MethodType type) {
-    Iterator<String> iteratorString = methodNames.iterator();
-    HashMap<String, HashSet<ApiAbstractMethod>> current = apiMembersByName.get(type.getId());
-    HashSet<ApiAbstractMethod> tempMethods = new HashSet<ApiAbstractMethod>();
-    while (iteratorString.hasNext()) {
-      tempMethods.addAll(current.get(iteratorString.next()));
+  Set<ApiAbstractMethod> getApiMembersBySet(Set<String> methodNames,
+      MethodType type) {
+    Map<String, Set<ApiAbstractMethod>> current = apiMembersByName.get(type);
+    Set<ApiAbstractMethod> tempMethods = new HashSet<ApiAbstractMethod>();
+    for (String methodName : methodNames) {
+      tempMethods.addAll(current.get(methodName));
     }
     return tempMethods;
   }
 
-  public HashSet<ApiAbstractMethod> getApiMethodsByName(String name,
-      MethodType type) {
-    return apiMembersByName.get(type.getId()).get(name);
+  Set<ApiAbstractMethod> getApiMethodsByName(String name, MethodType type) {
+    return apiMembersByName.get(type).get(name);
   }
 
-  public JClassType getClassObject() {
+  JClassType getClassObject() {
     return classType;
   }
 
-  public String getFullName() {
+  String getFullName() {
     return classType.getQualifiedSourceName();
   }
 
@@ -246,9 +180,9 @@
    * (if a non-abstract class is made into interface, the client class/interface
    * inheriting from it would need to change)
    */
-  public ArrayList<ApiChange.Status> getModifierChanges(ApiClass newClass) {
+  List<ApiChange.Status> getModifierChanges(ApiClass newClass) {
     JClassType newClassType = newClass.getClassObject();
-    ArrayList<ApiChange.Status> statuses = new ArrayList<ApiChange.Status>(5);
+    List<ApiChange.Status> statuses = new ArrayList<ApiChange.Status>(5);
 
     // check for addition of 'final', 'abstract', 'static'
     if (!classType.isFinal() && newClassType.isFinal()) {
@@ -269,7 +203,7 @@
     if (!classType.isAbstract() && (newClassType.isInterface() != null)) {
       statuses.add(ApiChange.Status.NONABSTRACT_CLASS_MADE_INTERFACE);
     }
-    if (isSubclassableApiClass(classType)) {
+    if (apiPackage.getApiContainer().isSubclassableApiClass(classType)) {
       if ((classType.isClass() != null) && (newClassType.isInterface() != null)) {
         statuses.add(ApiChange.Status.SUBCLASSABLE_API_CLASS_MADE_INTERFACE);
       }
@@ -280,21 +214,22 @@
     return statuses;
   }
 
-  public String getName() {
+  String getName() {
     return classType.getName();
   }
 
-  public ApiPackage getPackage() {
+  ApiPackage getPackage() {
     return apiPackage;
   }
 
-  public void initializeApiFields() {
-    apiFields = new HashMap<String, JField>();
-    ArrayList<String> notAddedFields = new ArrayList<String>();
+  void initializeApiFields() {
+    apiFields = new HashMap<String, ApiField>();
+    List<String> notAddedFields = new ArrayList<String>();
     JField fields[] = getAccessibleFields();
     for (JField field : fields) {
       if (isApiMember(field)) {
-        apiFields.put(computeFieldApiSignature(field), field);
+        apiFields.put(ApiField.computeApiSignature(field), new ApiField(field,
+            this));
       } else {
         notAddedFields.add(field.toString());
       }
@@ -305,11 +240,6 @@
     }
   }
 
-  @Override
-  public String toString() {
-    return classType.toString();
-  }
-
   private JField[] getAccessibleFields() {
     Map<String, JField> fieldsBySignature = new HashMap<String, JField>();
     JClassType tempClassType = classType;
@@ -331,16 +261,6 @@
     return fieldsBySignature.values().toArray(new JField[0]);
   }
 
-  private JAbstractMethod[] getAccessibleMembers(MethodType member) {
-    switch (member) {
-      case CONSTRUCTOR:
-        return classType.getConstructors();
-      case METHOD:
-        return getAccessibleMethods();
-    }
-    throw new AssertionError("Unknown value : " + member.getId());
-  }
-
   // TODO(amitmanjhi): to optimize, cache results
   private JMethod[] getAccessibleMethods() {
     boolean isInterface = false;
@@ -377,21 +297,35 @@
     return methodsBySignature.values().toArray(new JMethod[0]);
   }
 
-  private void initializeApiConstructorsAndFields() {
-    for (MethodType member : MethodType.values()) {
-      apiMembersByName.add(member.getId(),
-          new HashMap<String, HashSet<ApiAbstractMethod>>());
-      HashMap<String, HashSet<ApiAbstractMethod>> pointer = apiMembersByName.get(member.getId());
-      ArrayList<String> notAddedMembers = new ArrayList<String>();
-      JAbstractMethod jams[] = getAccessibleMembers(member);
+  private JAbstractMethod[] getAccessibleMethods(MethodType member) {
+    switch (member) {
+      case CONSTRUCTOR:
+        return classType.getConstructors();
+      case METHOD:
+        return getAccessibleMethods();
+    }
+    throw new AssertionError("Unknown value : " + member);
+  }
+
+  private void initializeApiConstructorsAndMethods() {
+    apiMembersByName =
+        new EnumMap<MethodType, Map<String, Set<ApiAbstractMethod>>>(
+            MethodType.class);
+    for (MethodType method : MethodType.values()) {
+      apiMembersByName.put(method,
+          new HashMap<String, Set<ApiAbstractMethod>>());
+      Map<String, Set<ApiAbstractMethod>> pointer =
+          apiMembersByName.get(method);
+      List<String> notAddedMembers = new ArrayList<String>();
+      JAbstractMethod jams[] = getAccessibleMethods(method);
       for (JAbstractMethod jam : jams) {
         if (isApiMember(jam)) {
           String tempName = jam.getName() + jam.getParameters().length;
-          HashSet<ApiAbstractMethod> existingMembers = pointer.get(tempName);
+          Set<ApiAbstractMethod> existingMembers = pointer.get(tempName);
           if (existingMembers == null) {
             existingMembers = new HashSet<ApiAbstractMethod>();
           }
-          switch (member) {
+          switch (method) {
             case CONSTRUCTOR:
               existingMembers.add(new ApiConstructor(jam, this));
               break;
@@ -399,7 +333,7 @@
               existingMembers.add(new ApiMethod(jam, this));
               break;
             default:
-              throw new AssertionError("Unknown memberType : " + member);
+              throw new AssertionError("Unknown memberType : " + method);
           }
           pointer.put(tempName, existingMembers);
         } else {
@@ -418,7 +352,7 @@
    * Note: Instance members of a class that is not instantiable are not api
    * members.
    */
-  private <E> boolean isApiMember(final E member) {
+  private boolean isApiMember(final Object member) {
     boolean isPublic = false;
     boolean isPublicOrProtected = false;
     boolean isStatic = false;
@@ -440,12 +374,13 @@
         isStatic = false; // constructors can't be static
       }
     }
-    if (ApiCompatibilityChecker.REMOVE_ABSTRACT_CLASS_FROM_API) {
-      if (!isInstantiableApiClass && !isStatic) {
+    if (ApiCompatibilityChecker.REMOVE_NON_SUBCLASSABLE_ABSTRACT_CLASS_FROM_API) {
+      if (!isInstantiableApiClass && !isStatic && !isSubclassableApiClass) {
         return false;
       }
     }
     return (isSubclassableApiClass && isPublicOrProtected)
         || (isNotsubclassableApiClass && isPublic);
   }
+
 }
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiClassDiffGenerator.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiClassDiffGenerator.java
index ed607e0..d32b39f 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiClassDiffGenerator.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiClassDiffGenerator.java
@@ -16,67 +16,43 @@
 
 package com.google.gwt.tools.apichecker;
 
-import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
 import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.core.ext.typeinfo.JField;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Produces the diff between the API of two apiClasses.
  */
-public class ApiClassDiffGenerator {
-  abstract static class DuplicateDetector<E> {
-    ApiDiffGenerator apiDiffGenerator;
-    JClassType currentClass;
-    int initialValue = 1;
-    ApiClass.MethodType methodType;
+final class ApiClassDiffGenerator implements Comparable<ApiClassDiffGenerator> {
 
-    DuplicateDetector(JClassType currentClass,
-        ApiDiffGenerator apiDiffGenerator, ApiClass.MethodType methodType) {
-      this.currentClass = currentClass;
-      this.apiDiffGenerator = apiDiffGenerator;
-      initialValue++;
-      this.methodType = methodType;
+  static final Collection<ApiChange> EMPTY_COLLECTION =
+      new ArrayList<ApiChange>(0);
+
+  static String printSetWithHashCode(Set<?> set, String identifier) {
+    StringBuffer sb = new StringBuffer();
+    sb.append(identifier + ", size = " + set.size());
+    for (Object element : set) {
+      sb.append(element + ", hashcode = " + element.hashCode());
     }
-
-    JClassType getClassType(E element) {
-      if (element instanceof JAbstractMethod) {
-        JAbstractMethod jam = (JAbstractMethod) element;
-        return jam.getEnclosingType();
-      }
-      if (element instanceof JField) {
-        JField field = (JField) element;
-        return field.getEnclosingType();
-      }
-      return null;
-    }
-
-    abstract HashSet<E> getElements(ApiClassDiffGenerator other);
-
-    boolean isDuplicate(E element) {
-      JClassType classType = getClassType(element);
-      if (classType == currentClass) {
-        return false;
-      }
-      ApiClassDiffGenerator other = apiDiffGenerator.findApiClassDiffGenerator(classType);
-      if (other == null) {
-        return false;
-      }
-      return getElements(other).contains(element);
-    }
+    sb.append("\n");
+    return sb.toString();
   }
 
-  private static ArrayList<ApiChange> checkExceptions(
-      JAbstractMethod newMethod, JAbstractMethod oldMethod) {
-    JType oldExceptions[] = oldMethod.getThrows();
-    JType newExceptions[] = newMethod.getThrows();
-    ArrayList<ApiChange> ret = new ArrayList<ApiChange>();
+  private static List<ApiChange> checkExceptions(ApiAbstractMethod newMethod,
+      ApiAbstractMethod oldMethod) {
+    JType newExceptions[] = newMethod.getMethod().getThrows();
+    JType oldExceptions[] = oldMethod.getMethod().getThrows();
+    List<ApiChange> ret = new ArrayList<ApiChange>();
     for (JType newException : newExceptions) {
       boolean isSubclass = false;
       for (JType oldException : oldExceptions) {
@@ -87,313 +63,206 @@
         }
       }
       if (!isSubclass) {
-        ret.add(new ApiChange(ApiChange.Status.EXCEPTIONS_ERROR,
-            "unhandled exception in new code " + newException.toString()));
+        ret.add(new ApiChange(oldMethod, ApiChange.Status.EXCEPTION_TYPE_ERROR,
+            "unhandled exception in new code " + newException));
       }
     }
     return ret;
   }
 
-  HashSet<JField> allIntersectingFields = new HashSet<JField>();
-
+  private Set<ApiField> allIntersectingFields = new HashSet<ApiField>();
   /**
    * Find all constructors, methods, fields that are present in either
    * intersection or missing members of this class or any superclass. Total of 6
    * things to keep track of. These variables are useful for memoization.
    */
-  ArrayList<HashSet<JAbstractMethod>> allIntersectingMethods = new ArrayList<HashSet<JAbstractMethod>>(
-      2);
+  private EnumMap<ApiClass.MethodType, Set<ApiAbstractMethod>> allIntersectingMethods =
+      null;
 
-  ArrayList<HashSet<JAbstractMethod>> allMissingMethods = new ArrayList<HashSet<JAbstractMethod>>(
-      2);
-  ApiDiffGenerator apiDiffGenerator = null;
-  ApiPackageDiffGenerator apiPackageDiffGenerator = null;
-  String className = null;
+  private Set<ApiField> allMissingFields = null;
+  private EnumMap<ApiClass.MethodType, Set<ApiAbstractMethod>> allMissingMethods =
+      null;
 
-  HashMap<JField, HashSet<ApiChange.Status>> intersectingFields = null;
+  private final ApiDiffGenerator apiDiffGenerator;
+  private final String className;
+  private HashMap<ApiField, Set<ApiChange.Status>> intersectingFields = null;
 
   /**
    * Map from methods and constructors in intersection to a string describing
    * how they have changed. The description could be the addition/removal of a
    * static/abstract/final keyword.
    */
-  ArrayList<HashMap<JAbstractMethod, HashSet<ApiChange>>> intersectingMethods;
-  HashSet<JField> missingFields = null;
+  private EnumMap<ApiClass.MethodType, Map<ApiAbstractMethod, Set<ApiChange>>> intersectingMethods;
+  private Set<ApiField> missingFields = null;
   /**
    * list of missing constructors and methods.
    */
-  ArrayList<HashSet<JAbstractMethod>> missingMethods;
-  ApiClass newClass = null;
+  private EnumMap<ApiClass.MethodType, Set<ApiAbstractMethod>> missingMethods;
+  private final ApiClass newClass;
 
-  ApiClass oldClass = null;
-  private HashSet<JField> allMissingFields = new HashSet<JField>();
+  private final ApiClass oldClass;
 
-  public ApiClassDiffGenerator(String className,
+  ApiClassDiffGenerator(String className,
       ApiPackageDiffGenerator apiPackageDiffGenerator) throws NotFoundException {
     this.className = className;
-    this.apiPackageDiffGenerator = apiPackageDiffGenerator;
     apiDiffGenerator = apiPackageDiffGenerator.getApiDiffGenerator();
-    this.newClass = apiPackageDiffGenerator.getNewApiPackage().getApiClass(
-        className);
-    this.oldClass = apiPackageDiffGenerator.getOldApiPackage().getApiClass(
-        className);
+    this.newClass =
+        apiPackageDiffGenerator.getNewApiPackage().getApiClass(className);
+    this.oldClass =
+        apiPackageDiffGenerator.getOldApiPackage().getApiClass(className);
     if (newClass == null || oldClass == null) {
       throw new NotFoundException("for class " + className
           + ", one of the class objects is null");
     }
-    intersectingFields = new HashMap<JField, HashSet<ApiChange.Status>>();
-    intersectingMethods = new ArrayList<HashMap<JAbstractMethod, HashSet<ApiChange>>>(
-        ApiClass.MethodType.values().length);
-    missingMethods = new ArrayList<HashSet<JAbstractMethod>>(
-        ApiClass.MethodType.values().length);
+
+    intersectingFields = new HashMap<ApiField, Set<ApiChange.Status>>();
+    intersectingMethods =
+        new EnumMap<ApiClass.MethodType, Map<ApiAbstractMethod, Set<ApiChange>>>(
+            ApiClass.MethodType.class);
+    missingMethods =
+        new EnumMap<ApiClass.MethodType, Set<ApiAbstractMethod>>(
+            ApiClass.MethodType.class);
     for (ApiClass.MethodType methodType : ApiClass.MethodType.values()) {
-      intersectingMethods.add(methodType.getId(),
-          new HashMap<JAbstractMethod, HashSet<ApiChange>>());
+      intersectingMethods.put(methodType,
+          new HashMap<ApiAbstractMethod, Set<ApiChange>>());
     }
   }
 
-  public void cleanApiDiff() {
-    /**
-     * Two different ways of eliminating duplicates from apiDiffs. 1.
-     * computeUnionsAndCleandApiDiff: remove an ApiDiff message from this class,
-     * if it is present in the apiDiffs of any of the superclasses. (Not sure if
-     * the implementation always yield the correct result. Therefore, I have not
-     * removed the cleanApiDiff2 implementation.)
-     * 
-     * 2. cleanApiDiff2: If the ApiDiff message is about member 'x', remove the
-     * ApiDiff message from this class, if the class defining 'x' also contains
-     * this message.
-     */
-    if (true) {
-      computeUnionsAndCleanApiDiff();
-    } else {
-      cleanApiDiff2();
-    }
-  }
-
-  public void cleanApiDiff2() {
-    DuplicateDetector<JField> fieldRemover = new DuplicateDetector<JField>(
-        oldClass.getClassObject(), apiDiffGenerator,
-        ApiClass.MethodType.CONSTRUCTOR) {
-      @Override
-      HashSet<JField> getElements(ApiClassDiffGenerator other) {
-        return other.getMissingFields();
-      }
-    };
-    missingFields.removeAll(getDuplicateElements(missingFields.iterator(),
-        fieldRemover));
-
-    DuplicateDetector<JField> intersectingFieldRemover = new DuplicateDetector<JField>(
-        oldClass.getClassObject(), apiDiffGenerator,
-        ApiClass.MethodType.CONSTRUCTOR) {
-      @Override
-      HashSet<JField> getElements(ApiClassDiffGenerator other) {
-        return other.getIntersectingFields();
-      }
-    };
-    removeAll(intersectingFields, getDuplicateElements(
-        intersectingFields.keySet().iterator(), intersectingFieldRemover));
-
-    for (ApiClass.MethodType methodType : ApiClass.MethodType.values()) {
-      DuplicateDetector<JAbstractMethod> missingMemberRemover = new DuplicateDetector<JAbstractMethod>(
-          oldClass.getClassObject(), apiDiffGenerator, methodType) {
-        @Override
-        HashSet<JAbstractMethod> getElements(final ApiClassDiffGenerator other) {
-          return other.getMissingMethods(methodType);
-        }
-      };
-      missingMethods.get(methodType.getId()).removeAll(
-          getDuplicateElements(
-              missingMethods.get(methodType.getId()).iterator(),
-              missingMemberRemover));
-
-      DuplicateDetector<JAbstractMethod> intersectingMemberRemover = new DuplicateDetector<JAbstractMethod>(
-          oldClass.getClassObject(), apiDiffGenerator, methodType) {
-        @Override
-        HashSet<JAbstractMethod> getElements(final ApiClassDiffGenerator other) {
-          return other.getIntersectingMethods(methodType);
-        }
-      };
-      removeAll(intersectingMethods.get(methodType.getId()),
-          getDuplicateElements(
-              intersectingMethods.get(methodType.getId()).keySet().iterator(),
-              intersectingMemberRemover));
-    }
-  }
-
-  public void computeApiDiff() {
-    HashSet<String> newFieldNames = newClass.getApiFieldNames();
-    HashSet<String> oldFieldNames = oldClass.getApiFieldNames();
-    HashSet<String> intersection = ApiDiffGenerator.extractCommonElements(
-        newFieldNames, oldFieldNames);
-    missingFields = oldClass.getApiFieldsBySet(oldFieldNames);
-    processFieldsInIntersection(intersection);
-
-    for (ApiClass.MethodType methodType : ApiClass.MethodType.values()) {
-      HashSet<String> newMethodNames = newClass.getApiMemberNames(methodType);
-      HashSet<String> oldMethodNames = oldClass.getApiMemberNames(methodType);
-      intersection = ApiDiffGenerator.extractCommonElements(newMethodNames,
-          oldMethodNames);
-      missingMethods.add(methodType.getId(),
-          getAbstractMethodObjects(oldClass.getApiMembersBySet(oldMethodNames,
-              methodType)));
-      processElementsInIntersection(intersection, methodType);
-    }
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.lang.Comparable#compareTo(java.lang.Object)
+   */
+  public int compareTo(ApiClassDiffGenerator other) {
+    return getName().compareTo(other.getName());
   }
 
   /**
-   * Compute the union of apiDiffs of all superclasses. Must be invoked only
-   * after intersectingMembers et al. have been computed. Algorithm: - Find the
-   * immediate superclass that has computed these apiDiffs. - Compute the union
-   * of apiDiffs of superClass with own apiDiffs.
+   * 
+   * cleanApiDiff: remove an ApiDiff message from this class, if it is present
+   * in the apiDiffs of any of the super-classes.
+   * 
+   * Compute the union of apiDiffs of all superclasses as part of this method.
+   * Must be invoked only after intersectingMembers et al. have been computed.
+   * Algorithm: - Find the immediate superclass that has computed these
+   * apiDiffs. - Compute the union of apiDiffs of superClass with own apiDiffs.
    */
-  public void computeUnionsAndCleanApiDiff() {
+  void cleanApiDiff() {
     // check if unions have already been computed.
-    if (allMissingMethods.size() > 0) {
+    if (allMissingMethods != null) {
       return;
     }
     ApiClassDiffGenerator other = getSuperclassApiClassDiffGenerator();
     // compute 'all*' fields for the 'other' object.
     if (other != null) {
-      other.computeUnionsAndCleanApiDiff();
+      other.cleanApiDiff();
     }
+    allIntersectingMethods =
+        new EnumMap<ApiClass.MethodType, Set<ApiAbstractMethod>>(
+            ApiClass.MethodType.class);
+    allMissingMethods =
+        new EnumMap<ApiClass.MethodType, Set<ApiAbstractMethod>>(
+            ApiClass.MethodType.class);
 
     for (ApiClass.MethodType methodType : ApiClass.MethodType.values()) {
-      // clean the current apiDiffs
+      // for methods/constructors: clean the current apiDiffs
       if (other != null) {
-        removeAll(intersectingMethods.get(methodType.getId()),
-            other.allIntersectingMethods.get(methodType.getId()));
-        missingMethods.get(methodType.getId()).removeAll(
-            other.allMissingMethods.get(methodType.getId()));
+        removeAll(intersectingMethods.get(methodType),
+            other.allIntersectingMethods.get(methodType));
+        missingMethods.get(methodType).removeAll(
+            other.allMissingMethods.get(methodType));
       }
-      // compute the union
-      HashSet<JAbstractMethod> tempSet1 = new HashSet<JAbstractMethod>(
-          intersectingMethods.get(methodType.getId()).keySet());
-      HashSet<JAbstractMethod> tempSet2 = new HashSet<JAbstractMethod>(
-          missingMethods.get(methodType.getId()));
+      // for methods/constructors: compute the allIntersecting*, allMissing*
+      HashSet<ApiAbstractMethod> tempSet1 =
+          new HashSet<ApiAbstractMethod>(
+              intersectingMethods.get(methodType).keySet());
+      HashSet<ApiAbstractMethod> tempSet2 =
+          new HashSet<ApiAbstractMethod>(missingMethods.get(methodType));
       if (other != null) {
-        tempSet1.addAll(other.allIntersectingMethods.get(methodType.getId()));
-        tempSet2.addAll(other.allMissingMethods.get(methodType.getId()));
+        tempSet1.addAll(other.allIntersectingMethods.get(methodType));
+        tempSet2.addAll(other.allMissingMethods.get(methodType));
       }
-      allIntersectingMethods.add(methodType.getId(), tempSet1);
-      allMissingMethods.add(methodType.getId(), tempSet2);
+      allIntersectingMethods.put(methodType, tempSet1);
+      allMissingMethods.put(methodType, tempSet2);
     }
-    // clean the current apiDiffs
+
+    // for fields: clean the current apiDiffs
     if (other != null) {
       removeAll(intersectingFields, other.allIntersectingFields);
       missingFields.removeAll(other.allMissingFields);
     }
-    // compute the union
-    allIntersectingFields = new HashSet<JField>(intersectingFields.keySet());
-    allMissingFields = new HashSet<JField>(missingFields);
+    // for fields: compute allIntersectingFields, allMissingFields
+    allIntersectingFields = new HashSet<ApiField>(intersectingFields.keySet());
+    allMissingFields = new HashSet<ApiField>(missingFields);
     if (other != null) {
       allIntersectingFields.addAll(other.allIntersectingFields);
       allMissingFields.addAll(other.allMissingFields);
     }
   }
 
-  public HashSet<JAbstractMethod> getAbstractMethodObjects(
-      HashSet<? extends ApiAbstractMethod> temp) {
-    Iterator<? extends ApiAbstractMethod> apiMethodsIterator = temp.iterator();
-    HashSet<JAbstractMethod> returnSet = new HashSet<JAbstractMethod>();
-    while (apiMethodsIterator.hasNext()) {
-      returnSet.add(apiMethodsIterator.next().getMethodObject());
-    }
-    return returnSet;
-  }
+  // TODO(amitmanjhi): for methods, think about variable length arguments
+  void computeApiDiff() {
+    Set<String> newFieldNames = newClass.getApiFieldNames();
+    Set<String> oldFieldNames = oldClass.getApiFieldNames();
+    Set<String> intersection =
+        ApiDiffGenerator.removeIntersection(newFieldNames, oldFieldNames);
+    missingFields = oldClass.getApiFieldsBySet(oldFieldNames);
+    processFieldsInIntersection(intersection);
 
-  public <T> HashSet<T> getDuplicateElements(Iterator<T> iterator,
-      DuplicateDetector<T> detector) {
-    HashSet<T> returnSet = new HashSet<T>();
-    while (iterator.hasNext()) {
-      T element = iterator.next();
-      if (detector.isDuplicate(element)) {
-        returnSet.add(element);
-      }
-    }
-    return returnSet;
-  }
-
-  public HashSet<JField> getIntersectingFields() {
-    return new HashSet<JField>(intersectingFields.keySet());
-  }
-
-  public HashSet<JAbstractMethod> getIntersectingMethods(
-      ApiClass.MethodType methodType) {
-    return new HashSet<JAbstractMethod>(intersectingMethods.get(
-        methodType.getId()).keySet());
-  }
-
-  public HashSet<JField> getMissingFields() {
-    return missingFields;
-  }
-
-  public HashSet<JAbstractMethod> getMissingMethods(
-      ApiClass.MethodType methodType) {
-    return missingMethods.get(methodType.getId());
-  }
-
-  public HashSet<ApiChange.Status> getModifierChangesForField(JField newField,
-      JField oldField) {
-    HashSet<ApiChange.Status> statuses = new HashSet<ApiChange.Status>();
-    if (!oldField.isFinal() && newField.isFinal()) {
-      statuses.add(ApiChange.Status.FINAL_ADDED);
-    }
-    if ((oldField.isStatic() && !newField.isStatic())) {
-      statuses.add(ApiChange.Status.STATIC_REMOVED);
-    }
-    return statuses;
-  }
-
-  public String printApiDiff() {
-    ArrayList<ApiChange.Status> apiChanges = oldClass.getModifierChanges(newClass);
-    int totalSize = missingFields.size() + intersectingFields.size()
-        + apiChanges.size();
     for (ApiClass.MethodType methodType : ApiClass.MethodType.values()) {
-      totalSize += (missingMethods.get(methodType.getId()).size() + intersectingMethods.get(
-          methodType.getId()).size());
+      Set<String> newMethodNames = newClass.getApiMemberNames(methodType);
+      Set<String> oldMethodNames = oldClass.getApiMemberNames(methodType);
+      intersection =
+          ApiDiffGenerator.removeIntersection(newMethodNames, oldMethodNames);
+      missingMethods.put(methodType, oldClass.getApiMembersBySet(
+          oldMethodNames, methodType));
+      processElementsInIntersection(intersection, methodType);
     }
-    if (totalSize == 0) {
-      return "";
+  }
+
+  Collection<ApiChange> getApiDiff() {
+    Collection<ApiChange.Status> apiStatusChanges =
+        oldClass.getModifierChanges(newClass);
+    /*
+     * int totalSize = missingFields.size() + intersectingFields.size() +
+     * apiStatusChanges.size(); for (ApiClass.MethodType methodType :
+     * ApiClass.MethodType.values()) { totalSize +=
+     * (missingMethods.get(methodType).size() + intersectingMethods.get(
+     * methodType).size()); } if (totalSize == 0) { return EMPTY_COLLECTION; }
+     */
+    Collection<ApiChange> apiChangeCollection = new ArrayList<ApiChange>();
+    for (ApiChange.Status apiStatus : apiStatusChanges) {
+      apiChangeCollection.add(new ApiChange(oldClass, apiStatus));
     }
-    StringBuffer sb = new StringBuffer();
-    Iterator<ApiChange.Status> apiChangeIterator = apiChanges.iterator();
-    while (apiChangeIterator.hasNext()) {
-      sb.append("\t\t" + oldClass.getFullName() + " "
-          + apiChangeIterator.next() + "\n");
+    // missing fields
+    for (ApiElement element : missingFields) {
+      apiChangeCollection.add(new ApiChange(element, ApiChange.Status.MISSING));
     }
-    if (apiChanges.size() == 0) {
-      sb.append("\t\tclass " + oldClass.getFullName() + "\n");
-    }
-    sb.append(printCollectionElements(missingFields.iterator()));
-    sb.append(printCollectionElements2(intersectingFields));
+    apiChangeCollection.addAll(getIntersectingFields());
     for (ApiClass.MethodType methodType : ApiClass.MethodType.values()) {
-      sb.append(printCollectionElements(missingMethods.get(methodType.getId()).iterator()));
-      sb.append(printCollectionElements(intersectingMethods.get(methodType.getId())));
+      apiChangeCollection.addAll(getMissingMethods(methodType));
+      apiChangeCollection.addAll(getIntersectingMethods(methodType));
     }
-    sb.append("\n");
-    return sb.toString();
+    return apiChangeCollection;
   }
 
-  public <E, V> void removeAll(HashMap<E, HashSet<V>> tempMap,
-      HashSet<E> removeKeys) {
-    Iterator<E> keyIterator = removeKeys.iterator();
-    while (keyIterator.hasNext()) {
-      tempMap.remove(keyIterator.next());
-    }
+  String getName() {
+    return className;
   }
 
-  private <T> void addProperty(HashMap<T, HashSet<ApiChange>> hashMap, T key,
+  /*
+   * Even though the method name is contained in the "property" parameter, the
+   * type information is lost. TODO (amitmanjhi): fix this issue later.
+   */
+  private <T> void addProperty(Map<T, Set<ApiChange>> hashMap, T key,
       ApiChange property) {
-    if (!ApiCompatibilityChecker.PRINT_INTERSECTIONS
-        && (property.getStatus() == ApiChange.Status.COMPATIBLE)) {
-      return;
-    }
-    if (!ApiCompatibilityChecker.PRINT_COMPATIBLE_WITH
-        && property.getStatus() == ApiChange.Status.COMPATIBLE_WITH) {
-      return;
-    }
-    HashSet<ApiChange> value = hashMap.get(key);
+    /*
+     * if (!ApiCompatibilityChecker.PRINT_COMPATIBLE && (property.getStatus() ==
+     * ApiChange.Status.COMPATIBLE)) { return; } if
+     * (!ApiCompatibilityChecker.PRINT_COMPATIBLE_WITH && property.getStatus() ==
+     * ApiChange.Status.COMPATIBLE_WITH) { return; }
+     */
+    Set<ApiChange> value = hashMap.get(key);
     if (value == null) {
       value = new HashSet<ApiChange>();
     }
@@ -401,6 +270,43 @@
     hashMap.put(key, value);
   }
 
+  private Collection<ApiChange> getIntersectingFields() {
+    Collection<ApiChange> collection = new ArrayList<ApiChange>();
+    List<ApiField> intersectingFieldsList =
+        new ArrayList<ApiField>(intersectingFields.keySet());
+    Collections.sort(intersectingFieldsList);
+    for (ApiField apiField : intersectingFieldsList) {
+      for (ApiChange.Status status : intersectingFields.get(apiField)) {
+        collection.add(new ApiChange(apiField, status));
+      }
+    }
+    return collection;
+  }
+
+  private Collection<ApiChange> getIntersectingMethods(
+      ApiClass.MethodType methodType) {
+    Collection<ApiChange> collection = new ArrayList<ApiChange>();
+    List<ApiAbstractMethod> apiMethodsList =
+        new ArrayList<ApiAbstractMethod>(
+            intersectingMethods.get(methodType).keySet());
+    Collections.sort(apiMethodsList);
+    for (ApiAbstractMethod apiMethod : apiMethodsList) {
+      collection.addAll(intersectingMethods.get(methodType).get(apiMethod));
+    }
+    return collection;
+  }
+
+  private Collection<ApiChange> getMissingMethods(ApiClass.MethodType methodType) {
+    Collection<ApiChange> collection = new ArrayList<ApiChange>();
+    List<ApiAbstractMethod> apiMethodsList =
+        new ArrayList<ApiAbstractMethod>(missingMethods.get(methodType));
+    Collections.sort(apiMethodsList);
+    for (ApiAbstractMethod apiMethod : apiMethodsList) {
+      collection.add(new ApiChange(apiMethod, ApiChange.Status.MISSING));
+    }
+    return collection;
+  }
+
   /**
    * return the ApiClassDiffGenerator object for the "closest" ancestor of
    * oldClass. return null if no ancestor of oldClass has ApiClassDiffGenerator
@@ -409,7 +315,8 @@
     ApiClassDiffGenerator other = null;
     JClassType classType = oldClass.getClassObject();
     while ((classType = classType.getSuperclass()) != null) {
-      other = apiDiffGenerator.findApiClassDiffGenerator(classType);
+      other =
+          apiDiffGenerator.findApiClassDiffGenerator(classType.getQualifiedSourceName());
       if (other != null) {
         return other;
       }
@@ -418,17 +325,16 @@
   }
 
   private boolean isIncompatibileDueToMethodOverloading(
-      HashSet<ApiAbstractMethod> methodsInNew,
-      HashSet<ApiAbstractMethod> methodsInExisting) {
+      Set<ApiAbstractMethod> methodsInNew,
+      Set<ApiAbstractMethod> methodsInExisting) {
     if (!ApiCompatibilityChecker.API_SOURCE_COMPATIBILITY
         || methodsInExisting.size() != 1 || methodsInNew.size() <= 1) {
       return false;
     }
-    String signature = methodsInExisting.toArray(new ApiAbstractMethod[0])[0].getCoarseSignature();
-    Iterator<ApiAbstractMethod> newMethodsIterator = methodsInNew.iterator();
+    String signature =
+        methodsInExisting.toArray(new ApiAbstractMethod[0])[0].getCoarseSignature();
     int numMatchingSignature = 0;
-    while (newMethodsIterator.hasNext() && numMatchingSignature < 2) {
-      ApiAbstractMethod current = newMethodsIterator.next();
+    for (ApiAbstractMethod current : methodsInNew) {
       if (current.getCoarseSignature().equals(signature)) {
         ++numMatchingSignature;
       }
@@ -436,105 +342,50 @@
     return numMatchingSignature > 1;
   }
 
-  private <V> String printCollectionElements(
-      HashMap<JAbstractMethod, HashSet<V>> tempHashMap) {
-    StringBuffer sb = new StringBuffer();
-    Iterator<JAbstractMethod> tempIterator = tempHashMap.keySet().iterator();
-    while (tempIterator.hasNext()) {
-      JAbstractMethod element = tempIterator.next();
-      String identifier = oldClass.computeRelativeSignature(element);
-      Iterator<V> tempIterator2 = tempHashMap.get(element).iterator();
-      while (tempIterator2.hasNext()) {
-        sb.append("\t\t\t" + identifier + ApiDiffGenerator.DELIMITER
-            + tempIterator2.next() + "\n");
-      }
-    }
-    return sb.toString();
-  }
-
-  private <E> String printCollectionElements(Iterator<E> temp) {
-    StringBuffer sb = new StringBuffer();
-    while (temp.hasNext()) {
-      sb.append("\t\t\t" + oldClass.computeRelativeSignature(temp.next())
-          + ApiDiffGenerator.DELIMITER + ApiChange.Status.MISSING + "\n");
-    }
-    return sb.toString();
-  }
-
-  private <V> String printCollectionElements2(
-      HashMap<JField, HashSet<V>> tempHashMap) {
-    StringBuffer sb = new StringBuffer();
-    Iterator<JField> tempIterator = tempHashMap.keySet().iterator();
-    while (tempIterator.hasNext()) {
-      JField element = tempIterator.next();
-      String identifier = oldClass.computeRelativeSignature(element);
-      Iterator<V> tempIterator2 = tempHashMap.get(element).iterator();
-      while (tempIterator2.hasNext()) {
-        sb.append("\t\t\t" + identifier + ApiDiffGenerator.DELIMITER
-            + tempIterator2.next() + "\n");
-      }
-    }
-    return sb.toString();
-  }
-
-  private void processElementsInIntersection(HashSet<String> intersection,
+  private void processElementsInIntersection(Set<String> intersection,
       ApiClass.MethodType methodType) {
-    if (intersection.size() == 0) {
-      return;
-    }
-    HashSet<JAbstractMethod> missingElements = missingMethods.get(methodType.getId());
-    HashMap<JAbstractMethod, HashSet<ApiChange>> intersectingElements = intersectingMethods.get(methodType.getId());
 
-    HashSet<ApiAbstractMethod> onlyInExisting = new HashSet<ApiAbstractMethod>();
-    HashSet<ApiAbstractMethod> onlyInNew = new HashSet<ApiAbstractMethod>();
-    HashSet<String> commonSignature = new HashSet<String>();
-    Iterator<String> intersectionNames = intersection.iterator();
+    Set<ApiAbstractMethod> missingElements = missingMethods.get(methodType);
+    Map<ApiAbstractMethod, Set<ApiChange>> intersectingElements =
+        intersectingMethods.get(methodType);
 
-    while (intersectionNames.hasNext()) {
-      String tempName = intersectionNames.next();
-      HashSet<ApiAbstractMethod> methodsInNew = newClass.getApiMethodsByName(
-          tempName, methodType);
-      HashSet<ApiAbstractMethod> methodsInExisting = oldClass.getApiMethodsByName(
-          tempName, methodType);
+    Set<ApiAbstractMethod> onlyInExisting = new HashSet<ApiAbstractMethod>();
+    Set<ApiAbstractMethod> onlyInNew = new HashSet<ApiAbstractMethod>();
+    Set<String> commonSignature = new HashSet<String>();
+
+    for (String elementName : intersection) {
+      Set<ApiAbstractMethod> methodsInNew =
+          newClass.getApiMethodsByName(elementName, methodType);
+      Set<ApiAbstractMethod> methodsInExisting =
+          oldClass.getApiMethodsByName(elementName, methodType);
       onlyInNew.addAll(methodsInNew);
       onlyInExisting.addAll(methodsInExisting);
       if (isIncompatibileDueToMethodOverloading(methodsInNew, methodsInExisting)) {
-        addProperty(
-            intersectingElements,
-            methodsInExisting.toArray(new ApiAbstractMethod[0])[0].getMethodObject(),
-            new ApiChange(ApiChange.Status.OVERLOADED,
-                "Many methods in the new API with similar signatures. Methods = "
-                    + methodsInNew
-                    + " This might break API source compatibility"));
+        ApiAbstractMethod methodInExisting =
+            methodsInExisting.toArray(new ApiAbstractMethod[0])[0];
+        addProperty(intersectingElements, methodInExisting, new ApiChange(
+            methodInExisting, ApiChange.Status.OVERLOADED_METHOD_CALL,
+            "Many methods in the new API with similar signatures. Methods = "
+                + methodsInNew + " This might break API source compatibility"));
       }
-      Iterator<ApiAbstractMethod> iterator1 = methodsInExisting.iterator();
       // We want to find out which method calls that the current API supports
       // will succeed even with the new API. Determine this by iterating over
       // the methods of the current API
-      while (iterator1.hasNext()) {
-        ApiAbstractMethod methodInExisting = iterator1.next();
-        Iterator<ApiAbstractMethod> iterator2 = methodsInNew.iterator();
-        while (iterator2.hasNext()) {
-          ApiAbstractMethod methodInNew = iterator2.next();
+      for (ApiAbstractMethod methodInExisting : methodsInExisting) {
+        for (ApiAbstractMethod methodInNew : methodsInNew) {
           if (methodInExisting.isCompatible(methodInNew)) {
-            ApiChange returnType = methodInExisting.checkReturnTypeCompatibility(methodInNew);
+            ApiChange returnType =
+                methodInExisting.checkReturnTypeCompatibility(methodInNew);
             if (returnType != null) {
-              addProperty(intersectingElements,
-                  methodInExisting.getMethodObject(), returnType);
+              addProperty(intersectingElements, methodInExisting, returnType);
             }
-            Iterator<ApiChange> apiChangeIterator = checkExceptions(
-                methodInNew.getMethodObject(),
-                methodInExisting.getMethodObject()).iterator();
-            while (apiChangeIterator.hasNext()) {
-              addProperty(intersectingElements,
-                  methodInExisting.getMethodObject(), apiChangeIterator.next());
+            for (ApiChange apiChange : checkExceptions(methodInNew,
+                methodInExisting)) {
+              addProperty(intersectingElements, methodInExisting, apiChange);
             }
-            Iterator<ApiChange.Status> apiChanges = methodInExisting.getModifierChanges(
-                methodInNew).iterator();
-            while (apiChanges.hasNext()) {
-              addProperty(intersectingElements,
-                  methodInExisting.getMethodObject(), new ApiChange(
-                      apiChanges.next()));
+            for (ApiChange.Status status : methodInExisting.getModifierChanges(methodInNew)) {
+              addProperty(intersectingElements, methodInExisting,
+                  new ApiChange(methodInExisting, status));
             }
             onlyInNew.remove(methodInNew);
             onlyInExisting.remove(methodInExisting);
@@ -542,40 +393,36 @@
             String signatureInExisting = methodInExisting.getApiSignature();
             if (signatureInNew.equals(signatureInExisting)) {
               commonSignature.add(signatureInNew);
-              addProperty(intersectingElements,
-                  methodInExisting.getMethodObject(), new ApiChange(
-                      ApiChange.Status.COMPATIBLE));
+              addProperty(intersectingElements, methodInExisting,
+                  new ApiChange(methodInExisting, ApiChange.Status.COMPATIBLE));
             } else {
-              addProperty(intersectingElements,
-                  methodInExisting.getMethodObject(), new ApiChange(
-                      ApiChange.Status.COMPATIBLE_WITH, " compatible with "
-                          + signatureInNew));
+              addProperty(intersectingElements, methodInExisting,
+                  new ApiChange(methodInExisting,
+                      ApiChange.Status.COMPATIBLE_WITH, signatureInNew));
             }
           }
         }
       }
       // printOutput(commonSignature, onlyInExisting, onlyInNew);
     }
-    missingElements.addAll(getAbstractMethodObjects(onlyInExisting));
+    missingElements.addAll(onlyInExisting);
   }
 
-  private void processFieldsInIntersection(HashSet<String> intersection) {
-    if (intersection.size() == 0) {
-      return;
-    }
-    Iterator<String> intersectionNames = intersection.iterator();
-
-    while (intersectionNames.hasNext()) {
-      String tempName = intersectionNames.next();
-      JField newField = newClass.getApiFieldByName(tempName);
-      JField oldField = oldClass.getApiFieldByName(tempName);
-      HashSet<ApiChange.Status> apiChanges = getModifierChangesForField(
-          newField, oldField);
+  private void processFieldsInIntersection(Set<String> intersection) {
+    for (String fieldName : intersection) {
+      ApiField newField = newClass.getApiFieldByName(fieldName);
+      ApiField oldField = oldClass.getApiFieldByName(fieldName);
+      Set<ApiChange.Status> apiChanges = oldField.getModifierChanges(newField);
       if (apiChanges.size() > 0) {
-        intersectingFields.put(oldField, getModifierChangesForField(newField,
-            oldField));
+        intersectingFields.put(oldField, apiChanges);
       }
     }
   }
 
+  private <E, V> void removeAll(Map<E, Set<V>> tempMap, Set<E> removeKeys) {
+    for (E element : removeKeys) {
+      tempMap.remove(element);
+    }
+  }
+
 }
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java
index 8489d8b..631f64a 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java
@@ -23,54 +23,92 @@
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 /**
- * Checks if the new API is compatible with the existing API.
+ * {@link ApiCompatibilityChecker} Main class to check if the new API is
+ * compatible with the existing API.
  * 
  * 
- * To compute Api diffs, follow these 5 steps: i) for each of the two
- * repositories, construct an ApiContainer, ii) construct an ApiDiffGenerator,
- * iii) call computeApiDiff on the ApiDiffGenerator iv) call cleanApiDiff on the
- * ApiDiffGenerator v) call printApiDiff on the ApiDiffGenerator
+ * <p>
+ * To compute API diffs, follow these 2 steps:
+ * <ol>
+ * <li> for each of the two repositories, construct an {@link ApiContainer}
+ * <li> call getApiDiff on the {@code ApiDiffGenerator}
+ * </ol>
+ * </p>
  * 
- * An apicontainer object has a list of apiPackage objects. ApiPackage objects
- * themselves are list of ApiClass objects. ApiClass objects contain list of
- * ApiConstructor, ApiMethod, and JField objects.
+ * <p>
+ * An {@code ApiContainer} object is a list of {@link ApiPackage} objects.
+ * {@code ApiPackage} objects themselves are list of {@link ApiClass} objects.
+ * {@code ApiClass} objects contain list of {@code ApiConstructor},
+ * {@code ApiMethod}, and {@code JField} objects.
+ * </p>
  * 
- * Each ApiDiffGenerator object has a list of intersecting and missing
- * ApiPackageDiffGenerator objects. Each ApiPackageDiffGenerator object has a
- * list of intersecting and missing ApiClassDiffGenerator objects. Each
- * ApiClassDiffGenerator object has a list of intersecting and missing
- * apiMembers, where these members are constructors, methods, and fields.
+ * <p>
+ * Each {@code ApiDiffGenerator} object computes the list of intersecting and
+ * missing {@link ApiPackageDiffGenerator} objects. Each
+ * {@code ApiPackageDiffGenerator} object in turn computes the list of
+ * intersecting and missing {@link ApiClassDiffGenerator} objects. Each
+ * {@code ApiClassDiffGenerator} object in turn computes the list of
+ * intersecting and missing API members. The members are represented by
+ * {@link ApiConstructor} for constructors, {@link ApiMethod} for methods, and
+ * {@link ApiField} for fields.
+ * </p>
  * 
- * For each intersecting apiMember, a list of ApiChange objects is stored. Each
- * ApiChange object encodes a specific Api change like adding the 'final'
- * keyword to an apiMethod.
+ * <p>
+ * For each intersecting API member, a list of {@link ApiChange} objects is
+ * stored. Each ApiChange object encodes a specific {@code ApiChange} like
+ * adding the 'final' keyword to the API member.
  * 
  */
 public class ApiCompatibilityChecker {
 
+  // TODO(amitmanjhi): use ToolBase for command-line processing
+
+  // TODO(amitmanjhi): check gwt's dev/core/src files. Would need the ability to
+  // build TypeOracle from class files
+
+  // TODO(amitmanjhi): ignore API breakages due to impl package. More generally,
+  // white-list of packages that should not be checked.
+
+  // TODO(amitmanjhi): better handling of exceptions and exception-chaining.
+
+  // currently doing only source_compatibility. true by default.
   public static final boolean API_SOURCE_COMPATIBILITY = true;
-  public static final boolean REMOVE_ABSTRACT_CLASS_FROM_API = true;
-  public static final boolean IGNORE_WHITELIST = false;
-  public static final boolean PRINT_COMPATIBLE_WITH = false;
-  public static final boolean PRINT_INTERSECTIONS = false;
-  public static final boolean REMOVE_DUPLICATES = true;
+
+  // prints which class the member was declared in, false by default
   public static final boolean DEBUG = false;
-  public static final boolean DISABLE_CHECKS = true;
 
-  private static final AbstractTreeLogger logger1 = createTreeLogger();
+  // prints the API of the two containers, false by default.
+  public static final boolean DEBUG_PRINT_ALL_API = false;
 
-  public static String getApiDiff(ApiDiffGenerator temp,
-      HashSet<String> whiteList, boolean removeDuplicates)
-      throws NotFoundException {
-    temp.computeApiDiff();
-    if (removeDuplicates) {
-      temp.cleanApiDiff();
-    }
-    String apiDifferences = temp.printApiDiff();
-    return removeWhiteListMatches(apiDifferences, whiteList);
+  // these two parameters print APIs common in the two repositories. Should be
+  // false by default.
+  public static final boolean PRINT_COMPATIBLE = false;
+
+  public static final boolean PRINT_COMPATIBLE_WITH = false;
+  // for debugging. To see if TypeOracle builds
+  public static final boolean PROCESS_EXISTING_API = true;
+
+  public static final boolean PROCESS_NEW_API = true;
+  // true by default
+  public static final boolean REMOVE_NON_SUBCLASSABLE_ABSTRACT_CLASS_FROM_API =
+      true;
+
+  // Tweak for log output.
+  public static final TreeLogger.Type type = TreeLogger.ERROR;
+
+  // remove duplicates by default
+  public static Collection<ApiChange> getApiDiff(ApiContainer newApi,
+      ApiContainer existingApi, Set<String> whiteList) throws NotFoundException {
+    ApiDiffGenerator apiDiff = new ApiDiffGenerator(newApi, existingApi);
+    return getApiDiff(apiDiff, whiteList, true);
   }
 
   // Call APIBuilders for each of the 2 source trees
@@ -78,100 +116,132 @@
 
     try {
       ApiContainer newApi = null, existingApi = null;
-      boolean processNewApi = true;
-      boolean processExistingApi = true;
 
       if (args.length < 1) {
         printHelp();
         System.exit(-1);
       }
 
-      if (processNewApi) {
-        newApi = new ApiContainer(args[0], "_new", logger1);
-      }
-      if (processExistingApi) {
-        existingApi = new ApiContainer(args[0], "_old", logger1);
-      }
-
-      if ((processNewApi && processExistingApi)
-          && (newApi != null && existingApi != null)) {
-        HashSet<String> whiteList = new HashSet<String>();
-        if (!IGNORE_WHITELIST) {
-          whiteList = readStringFromFile(args[0]);
+      AbstractTreeLogger logger = new PrintWriterTreeLogger();
+      logger.setMaxDetail(type);
+      if (PROCESS_NEW_API) {
+        newApi = new ApiContainer(args[0], "_new", logger);
+        if (ApiCompatibilityChecker.DEBUG_PRINT_ALL_API) {
+          logger.log(TreeLogger.INFO, newApi.getApiAsString()); // print the API
         }
-
-        ApiDiffGenerator apiDiff = new ApiDiffGenerator(newApi, existingApi);
-        String apiDifferences = getApiDiff(apiDiff, whiteList,
-            REMOVE_DUPLICATES);
-        System.out.println(apiDifferences);
-        System.out.println("\t\t\t\tApi Compatibility Checker tool, Copyright Google Inc. 2008");
-        System.exit(apiDifferences.length() == 0 ? 0 : 1);
+      }
+      if (PROCESS_EXISTING_API) {
+        existingApi = new ApiContainer(args[0], "_old", logger);
+        if (ApiCompatibilityChecker.DEBUG_PRINT_ALL_API) {
+          logger.log(TreeLogger.INFO, existingApi.getApiAsString());
+        }
       }
 
+      if (PROCESS_NEW_API && PROCESS_EXISTING_API) {
+        Collection<ApiChange> apiDifferences =
+            getApiDiff(newApi, existingApi, readWhiteListFromFile(args[0]));
+        for (ApiChange apiChange : apiDifferences) {
+          System.out.println(apiChange);
+        }
+        System.out.println("\t\t\t\tApi Compatibility Checker tool, Copyright Google Inc. 2008");
+        System.exit(apiDifferences.size() == 0 ? 0 : 1);
+      }
     } catch (Exception e) {
-      System.err.println("Exception " + e.getMessage()
-          + ", printing stacktrace");
+      // intercepting all exceptions in main, because I have to exit with -1 so
+      // that the build breaks.
       e.printStackTrace();
       System.exit(-1);
     }
   }
 
   public static void printHelp() {
-    System.out.println("java ApiCompatibilityChecker configFile\n");
-    System.out.println("The ApiCompatibilityChecker tool requires a config file as an argument. "
-        + "The config file must specify two repositories of java source files "
-        + "that must be compared for API source compatibility. Each repository "
-        + "must specify three properties: 'name', 'sourceFiles', and 'excludeFiles.' "
-        + "A suffix of '_old' is attached to properties of the first repository, "
-        + "while a suffix of '_new' is attached to properties of the second "
-        + "repository. An optional whitelist can also be present at the end of "
-        + "the config file. The format of the whitelist is same as the output of "
-        + "the tool without the whitelist.");
-    System.out.println();
-    System.out.println("The name property specifies the api name that should "
-        + "be used in the output. The sourceFiles property, a colon-separated "
-        + "list of files/directories, specifies the roots of the the filesystem "
-        + "trees that must be included. The excludeFiles property, "
-        + "a colon-separated lists of files/directories specifies the roots of "
-        + "the filesystem trees that must be excluded.");
-    System.out.println();
-    System.out.println();
-    System.out.println("Example api.conf file:\n"
+    StringBuffer sb = new StringBuffer();
+    sb.append("java ApiCompatibilityChecker configFile\n");
+    sb.append("The ApiCompatibilityChecker tool requires a config file as an argument. ");
+    sb.append("The config file must specify two repositories of java source files: ");
+    sb.append("'_old' and '_new', which are to be compared for API source compatibility.\n");
+    sb.append("An optional whitelist is present at the end of ");
+    sb.append("the config file. The format of the whitelist is same as the output of ");
+    sb.append("the tool without the whitelist.\n");
+    sb.append("Each repository is specified by the following four properties:\n");
+    sb.append("name           specifies how the api should be refered to in the output\n");
+    sb.append("dirRoot        optional argument that specifies the base directory of all other file/directory names\n");
+    sb.append("sourceFiles    a colon-separated list of files/directories that specify the roots of the the filesystem trees to be included.\n");
+    sb.append("excludeFiles   a colon-separated lists of files/directories that specify the roots of the filesystem trees to be excluded");
 
-        + "name_old gwtEmulator\n"
-        + "sourceFiles_old dev/core/super/com/google/gwt/dev/jjs/intrinsic/:user/super/com/google/gwt/emul/:user/src/com/google/gwt/core/client\n"
-        + "excludeFiles_old \n\n"
+    sb.append("\n\n");
+    sb.append("Example api.conf file:\n");
+    sb.append("name_old         gwtEmulator");
+    sb.append("\n");
+    sb.append("dirRoot_old      ./");
+    sb.append("\n");
+    sb.append("sourceFiles_old  dev/core/super:user/super:user/src");
+    sb.append("\n");
+    sb.append("excludeFiles_old user/super/com/google/gwt/junit");
+    sb.append("\n\n");
 
-        + "name_new gwtEmulatorCopy\n"
-        + "sourceFiles_new dev/core/super/com/google/gwt/dev/jjs/intrinsic/:user/super/com/google/gwt/emul/:user/src/com/google/gwt/core/client\n"
-        + "excludeFiles_new \n\n");
+    sb.append("name_new         gwtEmulatorCopy");
+    sb.append("\n");
+    sb.append("dirRoot_new      ../gwt-14/");
+    sb.append("\n");
+    sb.append("sourceFiles_new  dev/core:user/super:user/src");
+    sb.append("\n");
+    sb.append("excludeFiles_new user/super/com/google/gwt/junit");
+    sb.append("\n\n");
+
+    System.out.println(sb.toString());
+  }
+
+  // interface for testing, since do not want to build ApiDiff frequently
+  static Collection<ApiChange> getApiDiff(ApiDiffGenerator apiDiff,
+      Set<String> whiteList, boolean removeDuplicates) throws NotFoundException {
+    Collection<ApiChange> collection = apiDiff.getApiDiff(removeDuplicates);
+    Set<ApiChange> prunedCollection = new HashSet<ApiChange>();
+    for (ApiChange apiChange : collection) {
+      String apiChangeAsString = apiChange.toString();
+      apiChangeAsString = apiChangeAsString.trim();
+      if (whiteList.remove(apiChangeAsString)) {
+        continue;
+      }
+      // check for Status.Compatible and Status.Compatible_with
+      if (!PRINT_COMPATIBLE
+          && apiChange.getStatus().equals(ApiChange.Status.COMPATIBLE)) {
+        continue;
+      }
+      if (!PRINT_COMPATIBLE_WITH
+          && apiChange.getStatus().equals(ApiChange.Status.COMPATIBLE_WITH)) {
+        continue;
+      }
+      prunedCollection.add(apiChange);
+    }
+    if (whiteList.size() > 0) {
+      List<String> al = new ArrayList<String>(whiteList);
+      Collections.sort(al);
+      System.err.println("ApiChanges "
+          + al
+          + ",  not found. Are you using a properly formatted configuration file?");
+    }
+    List<ApiChange> apiChangeList = new ArrayList<ApiChange>(prunedCollection);
+    Collections.sort(apiChangeList);
+    return apiChangeList;
   }
 
   /**
-   * Tweak this for the log output.
+   * Each whiteList element is an {@link ApiElement} and
+   * {@link ApiChange.Status} separated by space. For example,
+   * "java.util.ArrayList::size() MISSING". The {@code ApiElement} is
+   * represented as the string obtained by invoking the getRelativeSignature()
+   * method on {@link ApiElement}.
+   * 
+   * @param fileName
+   * @return
    */
-  private static AbstractTreeLogger createTreeLogger() {
-    AbstractTreeLogger logger = new PrintWriterTreeLogger();
-    int choice = 3; // 1, 2, 3
-    switch (choice) {
-      case 1:
-        logger.setMaxDetail(TreeLogger.ALL);
-        break;
-      case 2:
-        logger.setMaxDetail(null);
-        break;
-      default:
-        logger.setMaxDetail(TreeLogger.ERROR);
-    }
-    return logger;
-  }
-
-  private static HashSet<String> readStringFromFile(String fileName)
+  private static Set<String> readWhiteListFromFile(String fileName)
       throws IOException {
     if (fileName == null) {
       throw new IllegalArgumentException("fileName is null");
     }
-    HashSet<String> hashSet = new HashSet<String>();
+    Set<String> hashSet = new HashSet<String>();
     FileReader fr = new FileReader(fileName);
     BufferedReader br = new BufferedReader(fr);
     String str = null;
@@ -182,34 +252,12 @@
         continue;
       }
       String splits[] = str.split(" ");
-      if (splits.length > 1) {
-        hashSet.add(splits[0] + " " + splits[1]);
+      if (splits.length > 1 && ApiChange.contains(splits[1])) {
+        String identifier = splits[0] + ApiDiffGenerator.DELIMITER + splits[1];
+        hashSet.add(identifier.trim());
       }
     }
     return hashSet;
   }
 
-  private static String removeWhiteListMatches(String apiDifferences,
-      HashSet<String> whiteList) {
-    String apiDifferencesArray[] = apiDifferences.split("\n");
-    String whiteListArray[] = whiteList.toArray(new String[0]);
-    for (int i = 0; i < apiDifferencesArray.length; i++) {
-      String temp = apiDifferencesArray[i].trim();
-      for (String whiteListElement : whiteListArray) {
-        if (temp.startsWith(whiteListElement)) {
-          apiDifferencesArray[i] = "";
-        }
-      }
-    }
-
-    StringBuffer sb = new StringBuffer();
-    for (String temp : apiDifferencesArray) {
-      if (temp.length() > 0) {
-        sb.append(temp);
-        sb.append("\n");
-      }
-    }
-    return sb.toString();
-  }
-
-}
+}
\ No newline at end of file
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiConstructor.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiConstructor.java
index fd57b57..8390145 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiConstructor.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiConstructor.java
@@ -17,26 +17,28 @@
 
 import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
 
-import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Encapsulates an API constructor.
  */
-public class ApiConstructor extends ApiAbstractMethod {
+final class ApiConstructor extends ApiAbstractMethod {
 
-  public ApiConstructor(JAbstractMethod method, ApiClass apiClass) {
+  ApiConstructor(JAbstractMethod method, ApiClass apiClass) {
     super(method, apiClass);
   }
 
   @Override
-  public ApiChange checkReturnTypeCompatibility(ApiAbstractMethod newMethod) {
+  ApiChange checkReturnTypeCompatibility(ApiAbstractMethod newMethod) {
     return null;
   }
 
+  /**
+   * returns an immutable List.
+   */
   @Override
-  public ArrayList<ApiChange.Status> getModifierChanges(
-      ApiAbstractMethod newMethod) {
-    return new ArrayList<ApiChange.Status>(0);
+  List<ApiChange.Status> getModifierChanges(ApiAbstractMethod newMethod) {
+    return Collections.emptyList();
   }
-
 }
\ No newline at end of file
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java
index 2a89cb9..47c8112 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java
@@ -18,6 +18,8 @@
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JConstructor;
 import com.google.gwt.core.ext.typeinfo.JPackage;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
@@ -33,75 +35,119 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.net.MalformedURLException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.Vector;
 
 /**
- * Encapsulates an API.
+ * {@link ApiContainer} Encapsulates an API.
  * 
  */
-public class ApiContainer {
+public final class ApiContainer {
 
-  private HashMap<String, ApiPackage> apiPackages = new HashMap<String, ApiPackage>();
-  private HashMap<String, String> excludedFiles = null;
+  private Map<JClassType, Boolean> apiClassCache =
+      new HashMap<JClassType, Boolean>();
+  private Map<String, ApiPackage> apiPackages =
+      new HashMap<String, ApiPackage>();
+
+  private Map<String, String> excludedFiles = null;
   private TreeLogger logger = null;
+
   private String name = null;
+
   private int numFilesCount = 0;
   private Collection<File> sourceTrees = null;
   private TypeOracle typeOracle = null;
 
+  /**
+   * A public constructor to create an {@code ApiContainer} from a config file.
+   * 
+   * @param fileName the config file
+   * @param suffix The code looks for the values of the properties: dirRoot,
+   *          name, sourceFiles, excludedFiles, ending in "suffix"
+   * @param logger The logger for the code.
+   * @throws IllegalArgumentException if one of the arguments is illegal
+   * @throws UnableToCompleteException if there is a TypeOracle exception
+   */
   public ApiContainer(String fileName, String suffix, TreeLogger logger)
-      throws IllegalArgumentException, MalformedURLException,
-      FileNotFoundException, IOException, NotFoundException,
-      UnableToCompleteException {
+      throws IllegalArgumentException, UnableToCompleteException {
     this.logger = logger;
     if (fileName == null) {
       throw new IllegalArgumentException("fileName is null");
     }
-    FileInputStream fis = new FileInputStream(fileName);
-    Properties config = new Properties();
-    config.load(fis);
-    String apiName = config.getProperty("name" + suffix);
-    String allSourceFiles = config.getProperty("sourceFiles" + suffix);
-    String allExcludedFiles = config.getProperty("excludedFiles" + suffix);
+    try {
+      FileInputStream fis = new FileInputStream(fileName);
+      Properties config = new Properties();
+      config.load(fis);
+      String apiName = config.getProperty("name" + suffix);
+      String allSourceFiles = config.getProperty("sourceFiles" + suffix);
+      String allExcludedFiles = config.getProperty("excludedFiles" + suffix);
 
-    if (allExcludedFiles == null) {
-      allExcludedFiles = "";
-    }
-    if (apiName == null || allSourceFiles == null) {
-      throw new IllegalArgumentException(
-          "in apiContainer constructor, either name (" + apiName
-              + ") or sourceFiles (" + allSourceFiles + ") is null");
-    }
-    logger.log(TreeLogger.DEBUG, "read from config file " + fileName
-        + ", name = " + apiName + ", allSourceFiles = " + allSourceFiles
-        + ", allExcludedFiles = " + allExcludedFiles, null);
+      if (allExcludedFiles == null) {
+        allExcludedFiles = "";
+      }
+      if (apiName == null || allSourceFiles == null) {
+        throw new IllegalArgumentException(
+            "in apiContainer constructor, either name (" + apiName
+                + ") or sourceFiles (" + allSourceFiles + ") is null");
+      }
+      logger.log(TreeLogger.DEBUG, "read from config file " + fileName
+          + ", name = " + apiName + ", allSourceFiles = " + allSourceFiles
+          + ", allExcludedFiles = " + allExcludedFiles, null);
 
-    String sourceFilesArray[] = allSourceFiles.split(":");
-    Collection<File> fileCollection = new Vector<File>();
-    for (String tempStr : sourceFilesArray) {
-      tempStr = tempStr.trim();
-      fileCollection.add(new File(tempStr));
+      String dirRoot = config.getProperty("dirRoot" + suffix);
+      if (dirRoot == null) {
+        dirRoot = "";
+      }
+      String sourceFilesArray[] = allSourceFiles.split(":");
+      Collection<File> fileCollection = new Vector<File>();
+      for (String tempStr : sourceFilesArray) {
+        tempStr = tempStr.trim();
+        checkFileExistence("source file: ", dirRoot + tempStr);
+        fileCollection.add(new File(dirRoot + tempStr));
+      }
+      logger.log(TreeLogger.DEBUG, "fileCollection " + fileCollection, null);
+      this.sourceTrees = fileCollection;
+      if (allExcludedFiles.equals("")) {
+        this.excludedFiles = generateCanonicalHashmap(new String[0], dirRoot);
+      } else {
+        String excludedFilesArray[] = allExcludedFiles.split(":");
+        for (String excludedFile : excludedFilesArray) {
+          checkFileExistence("excluded file: ", dirRoot + excludedFile);
+        }
+        this.excludedFiles =
+            generateCanonicalHashmap(excludedFilesArray, dirRoot);
+      }
+      this.name = apiName;
+      createTypeOracleFromSources();
+      initializeApiPackages();
+    } catch (MalformedURLException e1) {
+      throw new IllegalArgumentException(e1);
+    } catch (FileNotFoundException e2) {
+      throw new IllegalArgumentException(e2);
+    } catch (IOException e3) {
+      throw new IllegalArgumentException(e3);
+    } catch (NotFoundException e4) {
+      logger.log(TreeLogger.ERROR, "logged a NotFoundException", e4);
+      throw new UnableToCompleteException();
     }
-    this.sourceTrees = fileCollection;
-    if (allExcludedFiles.equals("")) {
-      this.excludedFiles = generateCanonicalHashmap(new String[0]);
-    } else {
-      String excludedFilesArray[] = allExcludedFiles.split(":");
-      this.excludedFiles = generateCanonicalHashmap(excludedFilesArray);
-    }
-    this.name = apiName;
-    createTypeOracleFromSources();
-    initializeApiPackages();
   }
 
-  // constructor is used while testing
+  /**
+   * Another public constructor. Used for programmatic invocation and testing.
+   * 
+   * @param name
+   * @param logger
+   * @param typeOracle
+   */
   ApiContainer(String name, TreeLogger logger, TypeOracle typeOracle) {
     this.name = name;
     this.logger = logger;
@@ -109,21 +155,83 @@
     initializeApiPackages();
   }
 
-  public ApiPackage getApiPackage(String packageName) {
+  /**
+   * Get all the API members as String.
+   * 
+   * @return the string value
+   */
+  public String getApiAsString() {
+    StringBuffer sb = new StringBuffer();
+    sb.append("Api: " + name + "\n\n");
+    List<ApiPackage> sortedApiPackages =
+        new ArrayList<ApiPackage>(apiPackages.values());
+    Collections.sort(sortedApiPackages);
+    for (ApiPackage apiPackage : apiPackages.values()) {
+      sb.append(apiPackage.getApiAsString());
+    }
+    return sb.toString();
+  }
+
+  ApiPackage getApiPackage(String packageName) {
     return apiPackages.get(packageName);
   }
 
-  public HashSet<String> getApiPackageNames() {
+  HashSet<String> getApiPackageNames() {
     return new HashSet<String>(apiPackages.keySet());
   }
 
-  public TreeLogger getLogger() {
+  Set<ApiPackage> getApiPackagesBySet(Set<String> names) {
+    Set<ApiPackage> ret = new HashSet<ApiPackage>();
+    for (String packageName : names) {
+      ret.add(apiPackages.get(packageName));
+    }
+    return ret;
+  }
+
+  TreeLogger getLogger() {
     return logger;
   }
 
+  boolean isApiClass(JClassType classType) {
+    Boolean ret = apiClassCache.get(classType);
+    if (ret != null) {
+      return ret.booleanValue();
+    }
+    // to avoid infinite recursion for isApiClass("BAR", ..) when:
+    // class FOO {
+    // public class BAR extends FOO {
+    // }
+    // }
+    apiClassCache.put(classType, Boolean.FALSE);
+    boolean bool = computeIsApiClass(classType);
+    if (bool) {
+      apiClassCache.put(classType, Boolean.TRUE);
+    } else {
+      apiClassCache.put(classType, Boolean.FALSE);
+    }
+    // container.getLogger().log(TreeLogger.SPAM, "computed isApiClass for " +
+    // classType + " as " + bool, null);
+    return bool;
+  }
+
+  boolean isInstantiableApiClass(JClassType classType) {
+    return !classType.isAbstract() && isApiClass(classType)
+        && hasPublicOrProtectedConstructor(classType);
+  }
+
+  boolean isNotsubclassableApiClass(JClassType classType) {
+    return isApiClass(classType) && !isSubclassable(classType);
+  }
+
+  boolean isSubclassableApiClass(JClassType classType) {
+    return isApiClass(classType) && isSubclassable(classType);
+  }
+
   private void addCompilationUnitsInPath(Set<CompilationUnit> units,
       File sourcePathEntry) throws NotFoundException, IOException,
       UnableToCompleteException {
+    logger.log(TreeLogger.SPAM, "entering addCompilationUnitsInPath, file = "
+        + sourcePathEntry, null);
     File[] files = sourcePathEntry.listFiles();
     if (files == null) {
       // No files found.
@@ -132,6 +240,7 @@
 
     for (int i = 0; i < files.length; i++) {
       final File file = files[i];
+      logger.log(TreeLogger.SPAM, "deciding the fate of file " + file, null);
       // Ignore files like .svn and .cvs
       if (file.getName().startsWith(".") || file.getName().equals("CVS")) {
         continue;
@@ -146,8 +255,8 @@
         String pkgName = null;
         if (file.getName().endsWith("java")) {
           pkgName = extractPackageNameFromFile(file);
-          logger.log(TreeLogger.DEBUG, "pkgName = " + pkgName + ", file = "
-              + file.toString(), null);
+          logger.log(TreeLogger.DEBUG, "adding pkgName = " + pkgName
+              + ", file = " + file.toString(), null);
         }
         if (isValidPackage(pkgName, sourcePathEntry.toURL().toString())) {
           // Add if it's a source file and the package and fileNames are okay
@@ -164,14 +273,48 @@
     }
   }
 
+  private void checkFileExistence(String tag, String pathName)
+      throws FileNotFoundException {
+    File tempFile = new File(pathName);
+    if (!tempFile.exists()) {
+      throw new FileNotFoundException(tag + "file " + pathName + " not found");
+    }
+  }
+
+  /**
+   * Assumption: Clients may subclass an API class, but they will not add their
+   * class to the package.
+   * 
+   * Notes: -- A class with only private constructors can be an API class.
+   */
+  private boolean computeIsApiClass(JClassType classType) {
+    // check for outer classes
+    if (isPublicOuterClass(classType)) {
+      return true;
+    }
+    // if classType is not a member type, return false
+    if (!classType.isMemberType()) {
+      return false;
+    }
+    JClassType enclosingType = classType.getEnclosingType();
+    if (classType.isPublic()) {
+      return isApiClass(enclosingType) || isAnySubtypeAnApiClass(enclosingType);
+    }
+    if (classType.isProtected()) {
+      return isSubclassableApiClass(enclosingType)
+          || isAnySubtypeASubclassableApiClass(enclosingType);
+    }
+    return false;
+  }
+
   private void createTypeOracleFromSources() throws NotFoundException,
       IOException, UnableToCompleteException {
 
     numFilesCount = 0;
     TypeOracleMediator mediator = new TypeOracleMediator();
     Set<CompilationUnit> units = new HashSet<CompilationUnit>();
-    for (Iterator<File> i = sourceTrees.iterator(); i.hasNext();) {
-      addCompilationUnitsInPath(units, i.next());
+    for (File tempFile : sourceTrees) {
+      addCompilationUnitsInPath(units, tempFile);
     }
     JdtCompiler.compile(units);
     mediator.refresh(logger, units);
@@ -212,32 +355,40 @@
   /**
    * Convert a set into a HashMap for faster lookups.
    */
-  private HashMap<String, String> generateCanonicalHashmap(String strArray[])
-      throws IOException {
+  private HashMap<String, String> generateCanonicalHashmap(String strArray[],
+      String dirRoot) throws IOException {
     HashMap<String, String> tempMap = new HashMap<String, String>();
     if (strArray == null) {
       return tempMap;
     }
     for (String str : strArray) {
       str = str.trim();
-      File tempFile = new File(str);
+      File tempFile = new File(dirRoot + str);
       str = tempFile.getCanonicalPath();
       tempMap.put(str, str);
     }
     return tempMap;
   }
 
+  private boolean hasPublicOrProtectedConstructor(JClassType classType) {
+    JConstructor[] constructors = classType.getConstructors();
+    for (JConstructor constructor : constructors) {
+      if (constructor.isPublic() || constructor.isProtected()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   /**
    * Purge non API packages.
    */
   private void initializeApiPackages() {
-    HashSet<JPackage> allPackages = new HashSet<JPackage>(
-        Arrays.asList(typeOracle.getPackages()));
-    Iterator<JPackage> packagesIterator = allPackages.iterator();
-    HashSet<String> packagesNotAdded = new HashSet<String>();
-    while (packagesIterator.hasNext()) {
-      JPackage packageObject = packagesIterator.next();
-      if (ApiPackage.isApiPackage(packageObject)) {
+    Set<JPackage> allPackages =
+        new HashSet<JPackage>(Arrays.asList(typeOracle.getPackages()));
+    Set<String> packagesNotAdded = new HashSet<String>();
+    for (JPackage packageObject : allPackages) {
+      if (isApiPackage(packageObject)) {
         ApiPackage apiPackageObj = new ApiPackage(packageObject, this);
         apiPackages.put(apiPackageObj.getName(), apiPackageObj);
       } else {
@@ -249,11 +400,49 @@
           + packagesNotAdded.size() + " packages: " + packagesNotAdded, null);
     }
     if (apiPackages.size() > 0) {
-      logger.log(TreeLogger.INFO, "API " + name + apiPackages.size()
+      logger.log(TreeLogger.INFO, "API " + name + " " + apiPackages.size()
           + " Api packages: " + apiPackages.keySet(), null);
     }
   }
 
+  private boolean isAnySubtypeAnApiClass(JClassType classType) {
+    JClassType subTypes[] = classType.getSubtypes();
+    for (JClassType tempType : subTypes) {
+      if (isApiClass(tempType)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean isAnySubtypeASubclassableApiClass(JClassType classType) {
+    JClassType subTypes[] = classType.getSubtypes();
+    for (JClassType tempType : subTypes) {
+      if (isSubclassableApiClass(tempType)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * A package is an API package if it contains at least one API class. Refer
+   * http://wiki.eclipse.org/index.php/Evolving_Java-based_APIs This definition
+   * boils down to "a package is an API package iff it contains at least one API
+   * class that is not enclosed in any other class."
+   * 
+   * @return return true if and only if the packageObject is an apiPackage
+   */
+  private boolean isApiPackage(JPackage packageObject) {
+    JClassType classTypes[] = packageObject.getTypes();
+    for (JClassType classType : classTypes) {
+      if (isPublicOuterClass(classType)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   private boolean isExcludedFile(String fileName) {
     String pattern = "file:";
     if (fileName.indexOf(pattern) == 0) {
@@ -262,6 +451,17 @@
     return (excludedFiles.get(fileName) != null);
   }
 
+  /**
+   * @return returns true if classType is public AND an outer class
+   */
+  private boolean isPublicOuterClass(JClassType classType) {
+    return classType.isPublic() && !classType.isMemberType();
+  }
+
+  private boolean isSubclassable(JClassType classType) {
+    return !classType.isFinal() && hasPublicOrProtectedConstructor(classType);
+  }
+
   private boolean isValidPackage(String packageName, String filePath) {
     logger.log(TreeLogger.SPAM, "packageName = " + packageName
         + ", filePath = " + filePath, null);
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiDiffGenerator.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiDiffGenerator.java
index d108370..0593887 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiDiffGenerator.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiDiffGenerator.java
@@ -21,32 +21,25 @@
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
 
 /**
- * encapsulates a class that produces the diff between two api's.
+ * {@link ApiDiffGenerator} encapsulates a class that produces the diff between
+ * two api's.
  */
-public class ApiDiffGenerator {
+public final class ApiDiffGenerator {
 
   public static final String DELIMITER = " ";
 
-  @SuppressWarnings("unchecked")
-  public static HashSet<String> extractCommonElements(HashSet<String> s1,
-      HashSet<String> s2) {
-    HashSet<String> intersection = (HashSet<String>) s1.clone();
-    intersection.retainAll(s2);
-    s1.removeAll(intersection);
-    s2.removeAll(intersection);
-    return intersection;
-  }
-
   /**
    * The two types might belong to different typeOracles.
    */
-  public static boolean isFirstTypeAssignableToSecond(JType firstType,
-      JType secondType) {
+  static boolean isFirstTypeAssignableToSecond(JType firstType, JType secondType) {
 
     // getJNISignature() does TypeErasure
     if (firstType.getJNISignature().equals(secondType.getJNISignature())) {
@@ -59,8 +52,10 @@
     }
     TypeOracle newApiTypeOracle = classType2.getOracle();
     // get the appropriate classObject in the newApi
-    JClassType firstClassType = newApiTypeOracle.findType(classType1.getQualifiedSourceName());
-    JClassType secondClassType = newApiTypeOracle.findType(classType2.getQualifiedSourceName());
+    JClassType firstClassType =
+        newApiTypeOracle.findType(classType1.getQualifiedSourceName());
+    JClassType secondClassType =
+        newApiTypeOracle.findType(classType2.getQualifiedSourceName());
     // The types might not necessarily exist in the newApi
     if (firstClassType == null || secondClassType == null) {
       return false;
@@ -68,49 +63,45 @@
     return firstClassType.isAssignableTo(secondClassType);
   }
 
-  HashMap<String, ApiPackageDiffGenerator> intersectingPackages = new HashMap<String, ApiPackageDiffGenerator>();
+  @SuppressWarnings("unchecked")
+  static Set<String> removeIntersection(Set<String> s1, Set<String> s2) {
+    Set<String> intersection = new HashSet<String>(s1);
+    intersection.retainAll(s2);
+    s1.removeAll(intersection);
+    s2.removeAll(intersection);
+    return intersection;
+  }
 
-  HashSet<String> missingPackageNames = null;
-  ApiContainer newApi = null;
-
-  ApiContainer oldApi = null;
+  Map<String, ApiPackageDiffGenerator> intersectingPackages =
+      new HashMap<String, ApiPackageDiffGenerator>();
+  Set<String> missingPackageNames;
+  final ApiContainer newApi;
+  final ApiContainer oldApi;
 
   ApiDiffGenerator(ApiContainer newApi, ApiContainer oldApi) {
     this.newApi = newApi;
     this.oldApi = oldApi;
   }
 
-  public void cleanApiDiff() {
-    Iterator<ApiPackageDiffGenerator> tempIterator = intersectingPackages.values().iterator();
-    while (tempIterator.hasNext()) {
-      tempIterator.next().cleanApiDiff();
+  Collection<ApiChange> getApiDiff(boolean removeDuplicates)
+      throws NotFoundException {
+    computeApiDiff();
+    if (removeDuplicates) {
+      cleanApiDiff();
     }
+    Collection<ApiChange> collection = new ArrayList<ApiChange>();
+    Set<ApiPackage> missingPackages =
+        oldApi.getApiPackagesBySet(missingPackageNames);
+    for (ApiPackage missingPackage : missingPackages) {
+      collection.add(new ApiChange(missingPackage, ApiChange.Status.MISSING));
+    }
+    for (ApiPackageDiffGenerator intersectingPackage : intersectingPackages.values()) {
+      collection.addAll(intersectingPackage.getApiDiff());
+    }
+    return collection;
   }
 
-  /**
-   * Compares 2 APIs for source compatibility. Algorithm: First find packages
-   * that are in one but not in another. Then, look at at classes in the common
-   * packages. Look at public classes.
-   * 
-   */
-  public void computeApiDiff() throws NotFoundException {
-    HashSet<String> newApiPackageNames = newApi.getApiPackageNames();
-    missingPackageNames = oldApi.getApiPackageNames();
-    HashSet<String> intersection = extractCommonElements(newApiPackageNames,
-        missingPackageNames);
-    // Inspect each of the classes in each of the packages in the intersection
-    Iterator<String> tempIterator = intersection.iterator();
-    while (tempIterator.hasNext()) {
-      String packageName = tempIterator.next();
-      ApiPackageDiffGenerator temp = new ApiPackageDiffGenerator(packageName,
-          this);
-      intersectingPackages.put(packageName, temp);
-      temp.computeApiDiff();
-    }
-  }
-
-  public ApiClassDiffGenerator findApiClassDiffGenerator(JClassType classType) {
-    String className = classType.getQualifiedSourceName();
+  ApiClassDiffGenerator findApiClassDiffGenerator(String className) {
     int i = className.length() - 1;
     while (i >= 0) {
       int dot = className.lastIndexOf('.', i);
@@ -123,8 +114,8 @@
       } else {
         i = -1;
       }
-      ApiClassDiffGenerator result = findApiClassDiffGenerator(pkgName,
-          typeName);
+      ApiClassDiffGenerator result =
+          findApiClassDiffGenerator(pkgName, typeName);
       if (result != null) {
         return result;
       }
@@ -140,12 +131,12 @@
    * 
    * @return <code>null</code> if the type is not found
    */
-  public ApiClassDiffGenerator findApiClassDiffGenerator(String pkgName,
+  ApiClassDiffGenerator findApiClassDiffGenerator(String pkgName,
       String typeName) {
     ApiPackageDiffGenerator pkg = findApiPackageDiffGenerator(pkgName);
     if (pkg != null) {
-      ApiClassDiffGenerator type = pkg.findApiClassDiffGenerator(pkgName + "."
-          + typeName);
+      ApiClassDiffGenerator type =
+          pkg.findApiClassDiffGenerator(pkgName + "." + typeName);
       if (type != null) {
         return type;
       }
@@ -153,30 +144,42 @@
     return null;
   }
 
-  public ApiPackageDiffGenerator findApiPackageDiffGenerator(String key) {
+  ApiPackageDiffGenerator findApiPackageDiffGenerator(String key) {
     return intersectingPackages.get(key);
   }
 
-  public ApiContainer getNewApiContainer() {
+  ApiContainer getNewApiContainer() {
     return newApi;
   }
 
-  public ApiContainer getOldApiContainer() {
+  ApiContainer getOldApiContainer() {
     return oldApi;
   }
 
-  public String printApiDiff() {
-    StringBuffer sb = new StringBuffer();
-    Iterator<String> missingPackagesIterator = missingPackageNames.iterator();
-    while (missingPackagesIterator.hasNext()) {
-      sb.append(missingPackagesIterator.next() + DELIMITER
-          + ApiChange.Status.MISSING + "\n");
+  private void cleanApiDiff() {
+    for (ApiPackageDiffGenerator intersectingPackage : intersectingPackages.values()) {
+      intersectingPackage.cleanApiDiff();
     }
-    Iterator<ApiPackageDiffGenerator> tempIterator = intersectingPackages.values().iterator();
-    while (tempIterator.hasNext()) {
-      sb.append(tempIterator.next().printApiDiff());
+  }
+
+  /**
+   * Compares 2 APIs for source compatibility. Algorithm: First find packages
+   * that are in one but not in another. Then, look at at classes in the common
+   * packages. Look at public classes.
+   * 
+   */
+  private void computeApiDiff() throws NotFoundException {
+    Set<String> newApiPackageNames = newApi.getApiPackageNames();
+    missingPackageNames = oldApi.getApiPackageNames();
+    Set<String> intersection =
+        removeIntersection(newApiPackageNames, missingPackageNames);
+    // Inspect each of the classes in each of the packages in the intersection
+    for (String packageName : intersection) {
+      ApiPackageDiffGenerator tempPackageDiffGenerator =
+          new ApiPackageDiffGenerator(packageName, this);
+      intersectingPackages.put(packageName, tempPackageDiffGenerator);
+      tempPackageDiffGenerator.computeApiDiff();
     }
-    return sb.toString();
   }
 
 }
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiElement.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiElement.java
new file mode 100644
index 0000000..c7654b1
--- /dev/null
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiElement.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2008 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.tools.apichecker;
+
+/**
+ * An interface encapsulating any API elements.
+ */
+public interface ApiElement {
+  String getRelativeSignature();
+}
\ No newline at end of file
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiField.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiField.java
new file mode 100644
index 0000000..9884ebc
--- /dev/null
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiField.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2008 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.tools.apichecker;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JField;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Immutable class that encapsulates an API Field. Useful for set-operations.
+ */
+final class ApiField implements Comparable<ApiField>, ApiElement {
+
+  static String computeApiSignature(JField tempField) {
+    return tempField.getEnclosingType().getQualifiedSourceName() + "::"
+        + tempField.getName();
+  }
+
+  private final ApiClass apiClass;
+  private volatile String apiSignature = null; // cached, lazily initialized
+  private final JField field;
+  private volatile String relativeSignature = null; // cached, lazily
+
+  // initialized
+
+  ApiField(JField field, ApiClass apiClass) {
+    this.field = field;
+    this.apiClass = apiClass;
+  }
+
+  public int compareTo(ApiField other) {
+    return getRelativeSignature().compareTo(other.getRelativeSignature());
+  }
+
+  /**
+   * Used during set operations.
+   */
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof ApiField) {
+      ApiField other = (ApiField) o;
+      return getApiSignature().equals(other.getApiSignature());
+    }
+    return false;
+  }
+
+  public String getRelativeSignature() {
+    if (relativeSignature == null) {
+      relativeSignature = computeRelativeSignature();
+    }
+    return relativeSignature;
+  }
+
+  @Override
+  public int hashCode() {
+    return getApiSignature().hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return field.toString();
+  }
+
+  String getApiSignature() {
+    if (apiSignature == null) {
+      apiSignature = computeApiSignature();
+    }
+    return apiSignature;
+  }
+
+  JField getField() {
+    return field;
+  }
+
+  Set<ApiChange.Status> getModifierChanges(ApiField newField) {
+    Set<ApiChange.Status> statuses = new HashSet<ApiChange.Status>();
+    if (!field.isFinal() && newField.getField().isFinal()) {
+      statuses.add(ApiChange.Status.FINAL_ADDED);
+    }
+    if ((field.isStatic() && !newField.getField().isStatic())) {
+      statuses.add(ApiChange.Status.STATIC_REMOVED);
+    }
+    return statuses;
+  }
+
+  private String computeApiSignature() {
+    return computeApiSignature(field);
+  }
+
+  private String computeRelativeSignature() {
+    String signature = field.getName();
+    if (ApiCompatibilityChecker.DEBUG) {
+      JClassType enclosingType = field.getEnclosingType();
+      return apiClass.getClassObject().getQualifiedSourceName()
+          + "::"
+          + signature
+          + " defined in "
+          + (enclosingType == null ? "null enclosing type "
+              : enclosingType.getQualifiedSourceName());
+    }
+    return apiClass.getClassObject().getQualifiedSourceName() + "::"
+        + signature;
+  }
+
+}
\ No newline at end of file
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiMethod.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiMethod.java
index 677099f..c3df50e 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiMethod.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiMethod.java
@@ -20,36 +20,36 @@
 import com.google.gwt.core.ext.typeinfo.JType;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Encapsulates an API method.
+ * Encapsulates an API method. Useful for set-operations.
  */
-public class ApiMethod extends ApiAbstractMethod {
+final class ApiMethod extends ApiAbstractMethod {
 
-  public ApiMethod(JAbstractMethod method, ApiClass apiClass) {
+  ApiMethod(JAbstractMethod method, ApiClass apiClass) {
     super(method, apiClass);
   }
 
   @Override
-  public ApiChange checkReturnTypeCompatibility(ApiAbstractMethod newMethod)
+  ApiChange checkReturnTypeCompatibility(ApiAbstractMethod newMethod)
       throws TypeNotPresentException {
     JType firstType, secondType;
-    if (newMethod.getMethodObject() instanceof JMethod
-        && method instanceof JMethod) {
+    if (newMethod.getMethod() instanceof JMethod && method instanceof JMethod) {
       firstType = ((JMethod) method).getReturnType();
-      secondType = ((JMethod) newMethod.getMethodObject()).getReturnType();
+      secondType = ((JMethod) newMethod.getMethod()).getReturnType();
     } else {
       throw new AssertionError("Different types for method = "
           + method.getClass() + ", and newMethodObject = "
-          + newMethod.getMethodObject().getClass() + ", signature = "
+          + newMethod.getMethod().getClass() + ", signature = "
           + getApiSignature());
     }
     StringBuffer sb = new StringBuffer();
     if (firstType.getSimpleSourceName().indexOf("void") != -1) {
       return null;
     }
-    boolean compatible = ApiDiffGenerator.isFirstTypeAssignableToSecond(
-        secondType, firstType);
+    boolean compatible =
+        ApiDiffGenerator.isFirstTypeAssignableToSecond(secondType, firstType);
     if (compatible) {
       return null;
     }
@@ -57,33 +57,32 @@
     sb.append(firstType.getQualifiedSourceName());
     sb.append(" to ");
     sb.append(secondType.getQualifiedSourceName());
-    return new ApiChange(ApiChange.Status.RETURN_TYPE_ERROR, sb.toString());
+    return new ApiChange(this, ApiChange.Status.RETURN_TYPE_ERROR,
+        sb.toString());
   }
 
   /*
-   * check for: (i) added 'final' or 'abstract', (ii) removed 'static' adding
+   * check for: (i) added 'final' or 'abstract', (ii) removed 'static', adding
    * the 'static' keyword is fine.
    * 
    * A private, static, or final method can't be made 'abstract' (Java language
    * specification).
    */
   @Override
-  public ArrayList<ApiChange.Status> getModifierChanges(
-      final ApiAbstractMethod newMethod) {
+  List<ApiChange.Status> getModifierChanges(final ApiAbstractMethod newMethod) {
     JMethod newjmethod = null;
     JMethod oldjmethod = null;
 
-    if (newMethod.getMethodObject() instanceof JMethod
-        && method instanceof JMethod) {
-      newjmethod = (JMethod) newMethod.getMethodObject();
+    if (newMethod.getMethod() instanceof JMethod && method instanceof JMethod) {
+      newjmethod = (JMethod) newMethod.getMethod();
       oldjmethod = (JMethod) method;
     } else {
       throw new AssertionError("Different types for method = "
           + method.getClass() + " and newMethod = "
-          + newMethod.getMethodObject().getClass() + ", signature = "
+          + newMethod.getMethod().getClass() + ", signature = "
           + getApiSignature());
     }
-    ArrayList<ApiChange.Status> statuses = new ArrayList<ApiChange.Status>();
+    List<ApiChange.Status> statuses = new ArrayList<ApiChange.Status>();
     if (!oldjmethod.isFinal() && newjmethod.isFinal()) {
       statuses.add(ApiChange.Status.FINAL_ADDED);
     }
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiPackage.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiPackage.java
index ca8a224..f1af3ac 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiPackage.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiPackage.java
@@ -22,53 +22,48 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
- * Encapsulates an API package.
+ * An immutable class that encapsulates an API package.
  */
-public class ApiPackage {
-  /**
-   * A package is an API package if it contains at least one API class. Refer
-   * http://wiki.eclipse.org/index.php/Evolving_Java-based_APIs This definition
-   * boils down to "a package is an API package iff it contains at least one API
-   * class that is not enclosed in any other class."
-   * 
-   * @return return true if and only if the packageObject is an apiPackage
-   */
-  public static boolean isApiPackage(JPackage packageObject) {
-    JClassType classTypes[] = packageObject.getTypes();
-    for (JClassType classType : classTypes) {
-      if (ApiClass.isPublicOuterClass(classType)) {
-        return true;
-      }
-    }
-    return false;
-  }
+final class ApiPackage implements Comparable<ApiPackage>, ApiElement {
 
-  private HashMap<String, ApiClass> apiClasses = new HashMap<String, ApiClass>();
-  private ApiContainer container = null;
+  private Map<String, ApiClass> apiClasses = new HashMap<String, ApiClass>();
+  private final ApiContainer apiContainer;
+  private final TreeLogger logger;
+  private final String name;
+  private final JPackage packageObject;
 
-  private TreeLogger logger = null;
-  private String name = null;
-
-  private JPackage packageObject = null;
-
-  public ApiPackage(JPackage obj, ApiContainer container) {
+  ApiPackage(JPackage obj, ApiContainer container) {
     packageObject = obj;
-    this.container = container;
-    if (logger == null) {
-      logger = container.getLogger();
-    }
+    this.apiContainer = container;
+    logger = container.getLogger();
     name = obj.getName();
     initialize();
   }
 
-  public ArrayList<JClassType> getAllClasses() {
-    ArrayList<JClassType> allClasses = new ArrayList<JClassType>(
-        Arrays.asList(packageObject.getTypes()));
+  public int compareTo(ApiPackage other) {
+    return this.getName().compareTo(other.getName());
+  }
+
+  public String getRelativeSignature() {
+    return name;
+  }
+
+  @Override
+  public String toString() {
+    return name;
+  }
+
+  List<JClassType> getAllClasses() {
+    List<JClassType> allClasses =
+        new ArrayList<JClassType>(Arrays.asList(packageObject.getTypes()));
     logger.log(TreeLogger.SPAM, "API " + packageObject + " has "
         + allClasses.size() + " outer classes", null);
     int index = 0;
@@ -82,33 +77,46 @@
     return allClasses;
   }
 
-  public ApiClass getApiClass(String className) {
+  String getApiAsString() {
+    StringBuffer sb = new StringBuffer();
+    sb.append(name + "\n");
+    ArrayList<ApiClass> apiClassesList =
+        new ArrayList<ApiClass>(apiClasses.values());
+    Collections.sort(apiClassesList);
+    for (ApiClass apiClass : apiClassesList) {
+      sb.append(apiClass.getApiAsString());
+    }
+    return sb.toString();
+  }
+
+  ApiClass getApiClass(String className) {
     return apiClasses.get(className);
   }
 
-  public HashSet<String> getApiClassNames() {
+  Set<ApiClass> getApiClassesBySet(Set<String> classNames) {
+    Set<ApiClass> set = new HashSet<ApiClass>();
+    for (String className : classNames) {
+      set.add(getApiClass(className));
+    }
+    return set;
+  }
+
+  Set<String> getApiClassNames() {
     return new HashSet<String>(apiClasses.keySet());
   }
 
-  public ApiContainer getApiContainer() {
-    return container;
+  ApiContainer getApiContainer() {
+    return apiContainer;
   }
 
-  public String getName() {
-    return name;
-  }
-
-  @Override
-  public String toString() {
+  String getName() {
     return name;
   }
 
   private void initialize() {
-    Iterator<JClassType> allClassesIterator = getAllClasses().iterator();
     ArrayList<String> notAddedClassNames = new ArrayList<String>();
-    while (allClassesIterator.hasNext()) {
-      JClassType classType = allClassesIterator.next();
-      if (ApiClass.isApiClass(classType)) {
+    for (JClassType classType : getAllClasses()) {
+      if (apiContainer.isApiClass(classType)) {
         ApiClass apiClass = new ApiClass(classType, this);
         apiClasses.put(classType.getQualifiedSourceName(), apiClass);
       } else {
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiPackageDiffGenerator.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiPackageDiffGenerator.java
index 964bcab..c2353cf 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiPackageDiffGenerator.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiPackageDiffGenerator.java
@@ -18,93 +18,100 @@
 
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * encapsulates a class that produces the diff between the api of two packages.
  */
-public class ApiPackageDiffGenerator {
-  ApiDiffGenerator apiDiffGenerator = null;
-  HashMap<String, ApiClassDiffGenerator> intersectingClasses = new HashMap<String, ApiClassDiffGenerator>();
-  HashSet<String> missingClassNames = null;
-  String name = null;
-  ApiPackage newPackage = null;
-  ApiPackage oldPackage = null;
+final class ApiPackageDiffGenerator implements
+    Comparable<ApiPackageDiffGenerator> {
+  private final ApiDiffGenerator apiDiffGenerator;
+  private Map<String, ApiClassDiffGenerator> intersectingClasses =
+      new HashMap<String, ApiClassDiffGenerator>();
+  private Set<String> missingClassNames = null;
+  private final String name;
+  private final ApiPackage newPackage;
+  private final ApiPackage oldPackage;
 
-  public ApiPackageDiffGenerator(String packageName,
-      ApiDiffGenerator apiDiffGenerator) throws NotFoundException {
+  ApiPackageDiffGenerator(String packageName, ApiDiffGenerator apiDiffGenerator)
+      throws NotFoundException {
     this.apiDiffGenerator = apiDiffGenerator;
     name = packageName;
-    newPackage = apiDiffGenerator.getNewApiContainer().getApiPackage(
-        packageName);
-    oldPackage = apiDiffGenerator.getOldApiContainer().getApiPackage(
-        packageName);
+    newPackage =
+        apiDiffGenerator.getNewApiContainer().getApiPackage(packageName);
+    oldPackage =
+        apiDiffGenerator.getOldApiContainer().getApiPackage(packageName);
     if (newPackage == null || oldPackage == null) {
       throw new NotFoundException("for package " + packageName
           + ", one of the package objects is null");
     }
   }
 
-  public void cleanApiDiff() {
-    Iterator<ApiClassDiffGenerator> tempIterator = intersectingClasses.values().iterator();
-    while (tempIterator.hasNext()) {
-      tempIterator.next().cleanApiDiff();
+  public int compareTo(ApiPackageDiffGenerator other) {
+    return this.getName().compareTo(other.getName());
+  }
+
+  void cleanApiDiff() {
+    for (ApiClassDiffGenerator intersectingClass : intersectingClasses.values()) {
+      intersectingClass.cleanApiDiff();
     }
   }
 
-  public void computeApiDiff() throws NotFoundException {
-    HashSet<String> newClassNames = newPackage.getApiClassNames();
+  void computeApiDiff() throws NotFoundException {
+    Set<String> newClassNames = newPackage.getApiClassNames();
     missingClassNames = oldPackage.getApiClassNames();
-    HashSet<String> intersection = ApiDiffGenerator.extractCommonElements(
-        newClassNames, missingClassNames);
+    Set<String> intersection =
+        ApiDiffGenerator.removeIntersection(newClassNames, missingClassNames);
 
     /* Inspect each of the classes in each of the packages in the intersection */
-    Iterator<String> tempIterator = intersection.iterator();
-    while (tempIterator.hasNext()) {
-      String className = tempIterator.next();
-      ApiClassDiffGenerator temp = new ApiClassDiffGenerator(className, this);
-      intersectingClasses.put(className, temp);
-      temp.computeApiDiff();
+    for (String className : intersection) {
+      ApiClassDiffGenerator tempClassDiffGenerator =
+          new ApiClassDiffGenerator(className, this);
+      intersectingClasses.put(className, tempClassDiffGenerator);
+      tempClassDiffGenerator.computeApiDiff();
     }
   }
 
-  public ApiClassDiffGenerator findApiClassDiffGenerator(String key) {
+  ApiClassDiffGenerator findApiClassDiffGenerator(String key) {
     return intersectingClasses.get(key);
   }
 
-  public ApiDiffGenerator getApiDiffGenerator() {
+  Collection<ApiChange> getApiDiff() {
+    Collection<ApiChange> collection = new ArrayList<ApiChange>();
+    Collection<ApiClass> missingClasses =
+        oldPackage.getApiClassesBySet(missingClassNames);
+    for (ApiClass missingClass : missingClasses) {
+      collection.add(new ApiChange(missingClass, ApiChange.Status.MISSING));
+    }
+    List<ApiClassDiffGenerator> intersectingClassesList =
+        new ArrayList<ApiClassDiffGenerator>(intersectingClasses.values());
+    Collections.sort(intersectingClassesList);
+    for (ApiClassDiffGenerator intersectingClass : intersectingClasses.values()) {
+      collection.addAll(intersectingClass.getApiDiff());
+    }
+    return collection;
+  }
+
+  ApiDiffGenerator getApiDiffGenerator() {
     return apiDiffGenerator;
   }
 
-  public ApiPackage getNewApiPackage() {
+  String getName() {
+    return name;
+  }
+
+  ApiPackage getNewApiPackage() {
     return newPackage;
   }
 
-  public ApiPackage getOldApiPackage() {
+  ApiPackage getOldApiPackage() {
     return oldPackage;
   }
 
-  public String printApiDiff() {
-    int totalSize = missingClassNames.size() + intersectingClasses.size();
-    if (totalSize == 0) {
-      return "";
-    }
-    StringBuffer sb = new StringBuffer();
-    Iterator<String> missingClassesIterator = missingClassNames.iterator();
-    while (missingClassesIterator.hasNext()) {
-      sb.append("\t\t" + missingClassesIterator.next()
-          + ApiDiffGenerator.DELIMITER + ApiChange.Status.MISSING + "\n");
-    }
-    Iterator<ApiClassDiffGenerator> tempIterator = intersectingClasses.values().iterator();
-    while (tempIterator.hasNext()) {
-      sb.append(tempIterator.next().printApiDiff());
-    }
-    if (sb.length() == 0) {
-      return "";
-    }
-    return "\tpackage " + name + "\n" + sb.toString() + "\n";
-  }
-
 }
diff --git a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java
index b643388..514bfa6 100644
--- a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java
+++ b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java
@@ -18,17 +18,14 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.dev.javac.CompilationUnit;
-import com.google.gwt.dev.javac.JdtCompiler;
-import com.google.gwt.dev.javac.TypeOracleMediator;
 import com.google.gwt.dev.util.log.AbstractTreeLogger;
 import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+import com.google.gwt.tools.apichecker.ApiContainerTest.StaticCompilationUnit;
 
 import junit.framework.TestCase;
 
+import java.util.Collection;
 import java.util.HashSet;
-import java.util.Set;
 
 /**
  * 
@@ -42,48 +39,20 @@
  */
 public class ApiCompatibilityTest extends TestCase {
 
-  static class StaticCompilationUnit extends CompilationUnit {
-
-    private final char[] source;
-    private final String typeName;
-
-    public StaticCompilationUnit(String typeName, char[] source) {
-      this.typeName = typeName;
-      this.source = source;
-    }
-
-    @Override
-    public String getDisplayLocation() {
-      return "/mock/" + typeName;
-    }
-
-    @Override
-    public String getSource() {
-      return String.valueOf(source);
-    }
-
-    @Override
-    public String getTypeName() {
-      return typeName;
-    }
-
-    @Override
-    public boolean isGenerated() {
-      return false;
-    }
-  }
-
   // These cups are slightly different from the cups in ApiContainerTest
-  static StaticCompilationUnit cuApiClass = new StaticCompilationUnit(
-      "test.apicontainer.ApiClass", getSourceForApiClass());
-  static StaticCompilationUnit cuNonApiClass = new StaticCompilationUnit(
-      "test.apicontainer.NonApiClass", getSourceForNonApiClass());
-  static StaticCompilationUnit cuNonApiPackage = new StaticCompilationUnit(
-      "test.nonapipackage.TestClass", getSourceForTestClass());
-  static StaticCompilationUnit cuObject = new StaticCompilationUnit(
-      "java.lang.Object", getSourceForObject());
-  static StaticCompilationUnit cuThrowable = new StaticCompilationUnit(
-      "java.lang.Throwable", getSourceForThrowable());
+
+  private static StaticCompilationUnit[] getScuArray() {
+    return new StaticCompilationUnit[] {
+        new StaticCompilationUnit("test.apicontainer.ApiClass",
+            getSourceForApiClass()),
+        new StaticCompilationUnit("test.apicontainer.NonApiClass",
+            getSourceForNonApiClass()),
+        new StaticCompilationUnit("test.nonapipackage.TestClass",
+            getSourceForTestClass()),
+        new StaticCompilationUnit("java.lang.Object", getSourceForObject()),
+        new StaticCompilationUnit("java.lang.Throwable",
+            getSourceForThrowable()),};
+  }
 
   private static char[] getSourceForApiClass() {
     StringBuffer sb = new StringBuffer();
@@ -142,49 +111,46 @@
 
   ApiContainer api1 = null;
   ApiContainer api2 = null;
-
   ApiContainer apiSameAs1 = null;
 
-  public TypeOracle getNewTypeOracleWithCompilationUnitsAdded(
-      AbstractTreeLogger logger) throws UnableToCompleteException {
-    TypeOracleMediator mediator = new TypeOracleMediator();
-    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
-    units.add(cuObject);
-    units.add(cuNonApiClass);
-    units.add(cuApiClass);
-    units.add(cuNonApiPackage);
-    units.add(cuThrowable);
-    JdtCompiler.compile(units);
-    mediator.refresh(logger, units);
-    return mediator.getTypeOracle();
-  }
-
   @Override
-  public void setUp() {
+  public void setUp() throws UnableToCompleteException {
     AbstractTreeLogger logger = new PrintWriterTreeLogger();
     logger.setMaxDetail(TreeLogger.ERROR);
-    try {
-      api1 = new ApiContainer("Api1", logger,
-          new ApiContainerTest().getNewTypeOracleWithCompilationUnitsAdded());
-      apiSameAs1 = new ApiContainer("Api2", logger,
-          new ApiContainerTest().getNewTypeOracleWithCompilationUnitsAdded());
-      api2 = new ApiContainer("Api2", logger,
-          getNewTypeOracleWithCompilationUnitsAdded(logger));
-    } catch (Exception ex) {
-      assertEquals("JSNI checks are probably active", "failed");
-    }
+
+    api1 =
+        new ApiContainer("Api1", logger,
+            ApiContainerTest.getNewTypeOracleFromCompilationUnits(
+                ApiContainerTest.getScuArray(), logger));
+    apiSameAs1 =
+        new ApiContainer("Api2", logger,
+            ApiContainerTest.getNewTypeOracleFromCompilationUnits(
+                ApiContainerTest.getScuArray(), logger));
+    api2 =
+        new ApiContainer("Api2", logger,
+            ApiContainerTest.getNewTypeOracleFromCompilationUnits(
+                getScuArray(), logger));
   }
 
-  public void testBasicStuff() throws NotFoundException {
+  // setup is called before every test*. To avoid the overhead of setUp() each
+  // time, test everything together.
+  public void testEverything() throws NotFoundException {
+    checkBasicStuff();
+    checkWhiteList();
+  }
+
+  private void checkBasicStuff() throws NotFoundException {
     HashSet<String> hashSet = new HashSet<String>();
-    assertEquals("", ApiCompatibilityChecker.getApiDiff(new ApiDiffGenerator(
-        api1, apiSameAs1), hashSet, false));
+    assertEquals(0, ApiCompatibilityChecker.getApiDiff(api1, apiSameAs1,
+        hashSet).size());
     ApiDiffGenerator apiDiff = new ApiDiffGenerator(api2, api1);
     boolean removeDuplicates = false;
-    String strWithDuplicates = ApiCompatibilityChecker.getApiDiff(apiDiff,
-        hashSet, removeDuplicates);
-    String strWithoutDuplicates = ApiCompatibilityChecker.getApiDiff(apiDiff,
-        hashSet, !removeDuplicates);
+    String strWithDuplicates =
+        getStringRepresentation(ApiCompatibilityChecker.getApiDiff(apiDiff,
+            hashSet, removeDuplicates));
+    String strWithoutDuplicates =
+        getStringRepresentation(ApiCompatibilityChecker.getApiDiff(apiDiff,
+            hashSet, !removeDuplicates));
 
     String delimiter = ApiDiffGenerator.DELIMITER;
     // test if missing packages are reported correctly
@@ -204,15 +170,14 @@
         "test.apicontainer.NonApiClass.ApiClassInNonApiClass" + delimiter
             + ApiChange.Status.ABSTRACT_ADDED, strWithoutDuplicates));
 
-    // test if methods are reported as missing due to class becoming abstract
-    if (ApiCompatibilityChecker.REMOVE_ABSTRACT_CLASS_FROM_API) {
-      assertEquals(1, countPresence(
-          "test.apicontainer.NonApiClass.ApiClassInNonApiClass::ApiClassInNonApiClass()"
-              + delimiter + ApiChange.Status.MISSING, strWithoutDuplicates));
-      assertEquals(1, countPresence(
-          "test.apicontainer.NonApiClass.ApiClassInNonApiClass::protectedMethod()"
-              + delimiter + ApiChange.Status.MISSING, strWithoutDuplicates));
-    }
+    // test if methods are staill reported even if class becomes abstract (as
+    // long as it is sub-classable)
+    assertEquals(0, countPresence(
+        "test.apicontainer.NonApiClass.ApiClassInNonApiClass::ApiClassInNonApiClass()"
+            + delimiter + ApiChange.Status.MISSING, strWithoutDuplicates));
+    assertEquals(0, countPresence(
+        "test.apicontainer.NonApiClass.ApiClassInNonApiClass::protectedMethod()"
+            + delimiter + ApiChange.Status.MISSING, strWithoutDuplicates));
 
     // test if modifier changes of fields and methods are reported
     assertEquals(1, countPresence("java.lang.Object::apiField" + delimiter
@@ -220,21 +185,26 @@
     assertEquals(1, countPresence("java.lang.Object::protectedMethod()"
         + delimiter + ApiChange.Status.FINAL_ADDED, strWithoutDuplicates));
 
-    // test if duplicates are weeded out.
-    if (ApiCompatibilityChecker.REMOVE_ABSTRACT_CLASS_FROM_API) {
-      assertEquals(2, countPresence("protectedMethod()" + delimiter
-          + ApiChange.Status.FINAL_ADDED, strWithDuplicates));
-    } else {
-      assertEquals(3, countPresence("protectedMethod()" + delimiter
-          + ApiChange.Status.FINAL_ADDED, strWithDuplicates));
-    }
+    // test if duplicates are weeded out from intersecting methods
+    assertEquals(3, countPresence("protectedMethod()" + delimiter
+        + ApiChange.Status.FINAL_ADDED, strWithDuplicates));
+    assertEquals(1, countPresence("protectedMethod()" + delimiter
+        + ApiChange.Status.FINAL_ADDED, strWithoutDuplicates));
+
+    // test if duplicates are weeded out from missing fields
+    assertEquals(3, countPresence("apiFieldWillBeMissing" + delimiter
+        + ApiChange.Status.MISSING, strWithDuplicates));
+    assertEquals(1, countPresence("apiFieldWillBeMissing" + delimiter
+        + ApiChange.Status.MISSING, strWithoutDuplicates));
+
     // test returnType error
-    String methodSignature = "checkParametersAndReturnTypes(Ltest/apicontainer/ApiClass;)";
+    String methodSignature =
+        "checkParametersAndReturnTypes(Ltest/apicontainer/ApiClass;)";
     assertEquals(1, countPresence(methodSignature + delimiter
         + ApiChange.Status.RETURN_TYPE_ERROR, strWithoutDuplicates));
     // test method exceptions
     assertEquals(1, countPresence(methodSignature + delimiter
-        + ApiChange.Status.EXCEPTIONS_ERROR, strWithoutDuplicates));
+        + ApiChange.Status.EXCEPTION_TYPE_ERROR, strWithoutDuplicates));
 
     // checking if changes in parameter types were okay
     assertEquals(2, countPresence(methodSignature, strWithoutDuplicates));
@@ -242,18 +212,20 @@
     // test method_overloading
     methodSignature = "methodInNonApiClass(Ljava/lang/Object;)";
     assertEquals(1, countPresence(methodSignature + delimiter
-        + ApiChange.Status.OVERLOADED, strWithoutDuplicates));
+        + ApiChange.Status.OVERLOADED_METHOD_CALL, strWithoutDuplicates));
   }
 
-  public void testWhiteList() throws NotFoundException {
+  private void checkWhiteList() throws NotFoundException {
     ApiDiffGenerator apiDiff = new ApiDiffGenerator(api2, api1);
     boolean removeDuplicates = false;
-    String whiteList = "java.newpackage" + ApiDiffGenerator.DELIMITER
-        + ApiChange.Status.MISSING;
+    String whiteList =
+        "java.newpackage" + ApiDiffGenerator.DELIMITER
+            + ApiChange.Status.MISSING;
     HashSet<String> hashSet = new HashSet<String>();
     hashSet.add(whiteList);
-    String strWithoutDuplicates = ApiCompatibilityChecker.getApiDiff(apiDiff,
-        hashSet, !removeDuplicates);
+    String strWithoutDuplicates =
+        getStringRepresentation(ApiCompatibilityChecker.getApiDiff(apiDiff,
+            hashSet, !removeDuplicates));
 
     // test if missing packages are reported correctly
     assertEquals(0, countPresence(whiteList, strWithoutDuplicates));
@@ -269,10 +241,20 @@
     return count;
   }
 
+  private String getStringRepresentation(Collection<ApiChange> collection) {
+    StringBuffer sb = new StringBuffer();
+    for (ApiChange apiChange : collection) {
+      sb.append(apiChange.getApiElement().getRelativeSignature());
+      sb.append(ApiDiffGenerator.DELIMITER);
+      sb.append(apiChange.getStatus());
+      sb.append("\n");
+    }
+    return sb.toString();
+  }
+
 }
 
 // abstract methods can't be static
-
 class TestAA {
   static int j = 10;
 
diff --git a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java
index a390b08..c2d17fa 100644
--- a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java
+++ b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java
@@ -15,7 +15,6 @@
  */
 package com.google.gwt.tools.apichecker;
 
-import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
@@ -24,10 +23,10 @@
 import com.google.gwt.dev.javac.TypeOracleMediator;
 import com.google.gwt.dev.util.log.AbstractTreeLogger;
 import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
-import com.google.gwt.tools.apichecker.ApiCompatibilityTest.StaticCompilationUnit;
 
 import junit.framework.TestCase;
 
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -35,6 +34,37 @@
  * Test ApiContainer.
  */
 public class ApiContainerTest extends TestCase {
+  static class StaticCompilationUnit extends CompilationUnit {
+
+    private final char[] source;
+    private final String typeName;
+
+    public StaticCompilationUnit(String typeName, char[] source) {
+      this.typeName = typeName;
+      this.source = source;
+    }
+
+    @Override
+    public String getDisplayLocation() {
+      return "/mock/" + typeName;
+    }
+
+    @Override
+    public String getSource() {
+      return String.valueOf(source);
+    }
+
+    @Override
+    public String getTypeName() {
+      return typeName;
+    }
+
+    @Override
+    public boolean isGenerated() {
+      return false;
+    }
+  }
+
   @SuppressWarnings("unused")
   class TestA {
     public TestA(String args) {
@@ -51,7 +81,6 @@
       return "";
     }
   }
-
   class TestB extends TestA {
     public TestB(TestA a) {
       super(a);
@@ -65,20 +94,34 @@
     }
   }
 
-  static StaticCompilationUnit cuApiClass = new StaticCompilationUnit(
-      "test.apicontainer.ApiClass", getSourceForApiClass());
-  static StaticCompilationUnit cuNonApiClass = new StaticCompilationUnit(
-      "test.apicontainer.NonApiClass", getSourceForNonApiClass());
-  static StaticCompilationUnit cuNonApiPackage = new StaticCompilationUnit(
-      "test.nonapipackage.TestClass", getSourceForTestClass());
-  static StaticCompilationUnit cuObject = new StaticCompilationUnit(
-      "java.lang.Object", getSourceForObject());
-  static StaticCompilationUnit cuNewPackage = new StaticCompilationUnit(
-      "java.newpackage.Test", getSourceForTest());
+  // TODO (amitmanjhi): Try using UnitTestTreeLogger to capture log messages
+  public static TypeOracle getNewTypeOracleFromCompilationUnits(
+      StaticCompilationUnit tempScuArray[], AbstractTreeLogger logger)
+      throws UnableToCompleteException {
+
+    TypeOracleMediator mediator = new TypeOracleMediator();
+    Set<CompilationUnit> units =
+        new HashSet<CompilationUnit>(Arrays.asList(tempScuArray));
+    JdtCompiler.compile(units);
+    mediator.refresh(logger, units);
+    return mediator.getTypeOracle();
+  }
+
+  public static StaticCompilationUnit[] getScuArray() {
+    return new StaticCompilationUnit[] {
+        new StaticCompilationUnit("test.apicontainer.ApiClass",
+            getSourceForApiClass()),
+        new StaticCompilationUnit("test.apicontainer.NonApiClass",
+            getSourceForNonApiClass()),
+        new StaticCompilationUnit("test.nonapipackage.TestClass",
+            getSourceForTestClass()),
+        new StaticCompilationUnit("java.lang.Object", getSourceForObject()),
+        new StaticCompilationUnit("java.newpackage.Test", getSourceForTest()),};
+  }
 
   private static JAbstractMethod getMethodByName(String name, ApiClass apiClass) {
     return (apiClass.getApiMethodsByName(name, ApiClass.MethodType.METHOD).toArray(
-        new ApiAbstractMethod[0])[0]).getMethodObject();
+        new ApiAbstractMethod[0])[0]).getMethod();
   }
 
   private static char[] getSourceForApiClass() {
@@ -91,6 +134,18 @@
     return sb.toString().toCharArray();
   }
 
+  private static char[] getSourceForNewObject() {
+    StringBuffer sb = new StringBuffer();
+    sb.append("package java.lang;\n");
+    sb.append("public class Object {\n");
+    sb.append("\tpublic static class Foo extends Object{\n");
+    sb.append("\t}\n");
+    sb.append("}\n");
+    sb.append("class Temp {\n");
+    sb.append("}");
+    return sb.toString().toCharArray();
+  }
+
   private static char[] getSourceForNonApiClass() {
     StringBuffer sb = new StringBuffer();
     sb.append("package test.apicontainer;\n");
@@ -115,6 +170,7 @@
     sb.append("\tprivate void internalMethod() { }\n");
     sb.append("\tprotected native long protectedMethod();\n");
     sb.append("\tpublic int apiField = 0;\n");
+    sb.append("\tprotected transient int apiFieldWillBeMissing = 1;\n");
     sb.append("\tprivate int internalField = 0;\n");
     sb.append("\tprotected int protectedField=2;\n");
     sb.append("}\n");
@@ -137,27 +193,8 @@
     return sb.toString().toCharArray();
   }
 
-  ApiContainer api1 = null;
-
-  // TODO (amitmanjhi): Try using UnitTestTreeLogger to capture log messages
-  public TypeOracle getNewTypeOracleWithCompilationUnitsAdded()
-      throws UnableToCompleteException {
-
-    AbstractTreeLogger logger = new PrintWriterTreeLogger();
-    logger.setMaxDetail(TreeLogger.ERROR);
-
-    // Build onto an empty type oracle.
-    TypeOracleMediator mediator = new TypeOracleMediator();
-    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
-    units.add(cuObject);
-    units.add(cuNonApiClass);
-    units.add(cuApiClass);
-    units.add(cuNonApiPackage);
-    units.add(cuNewPackage);
-    JdtCompiler.compile(units);
-    mediator.refresh(logger, units);
-    return mediator.getTypeOracle();
-  }
+  ApiContainer apiCheck = null;
+  ApiContainer apiCheckLoop = null;
 
   /**
    * Class hierarchy. public java.lang.Object -- test.apicontainer.NonApiClass
@@ -165,26 +202,34 @@
    * test.apicontainer.ApiClass -- test.nonapipackage.TestClass
    */
   @Override
-  public void setUp() {
-    AbstractTreeLogger logger1 = new PrintWriterTreeLogger();
-    logger1.setMaxDetail(TreeLogger.ERROR);
+  public void setUp() throws UnableToCompleteException {
+    AbstractTreeLogger logger = new PrintWriterTreeLogger();
+    logger.setMaxDetail(com.google.gwt.core.ext.TreeLogger.ERROR);
+    apiCheckLoop =
+        new ApiContainer("ApiClassTest", logger,
+            getNewTypeOracleFromCompilationUnits(
+                new StaticCompilationUnit[] {new StaticCompilationUnit(
+                    "java.lang.Object", getSourceForNewObject()),}, logger));
 
-    try {
-      api1 = new ApiContainer("ApiContainerTest", logger1,
-          getNewTypeOracleWithCompilationUnitsAdded());
-    } catch (Exception ex) {
-      // JSNI checks are active.
-      assertEquals("jsni checks are probably active", "failed");
-    }
+    apiCheck =
+        new ApiContainer("ApiContainerTest", logger,
+            getNewTypeOracleFromCompilationUnits(getScuArray(), logger));
+  }
+
+  public void testEverything() {
+    checkApiClass();
+    checkApiMembers();
+    checkApiPackages();
+    checkInfiniteLoopInApiClass();
   }
 
   /**
    * Check if apiClasses are determined correctly. Check if inner classes are
    * classified correctly as api classes.
    */
-  public void testApiClass() {
-    ApiPackage package1 = api1.getApiPackage("java.lang");
-    ApiPackage package2 = api1.getApiPackage("test.apicontainer");
+  void checkApiClass() {
+    ApiPackage package1 = apiCheck.getApiPackage("java.lang");
+    ApiPackage package2 = apiCheck.getApiPackage("test.apicontainer");
     assertNotNull(package1);
     assertNotNull(package2);
 
@@ -205,21 +250,23 @@
    * apiMethods, (b) method overloading is done correctly
    * 
    */
-  public void testApiMembers() {
-    ApiClass object = api1.getApiPackage("java.lang").getApiClass(
-        "java.lang.Object");
-    ApiClass apiClass = api1.getApiPackage("test.apicontainer").getApiClass(
-        "test.apicontainer.ApiClass");
-    ApiClass innerClass = api1.getApiPackage("test.apicontainer").getApiClass(
-        "test.apicontainer.NonApiClass.ApiClassInNonApiClass");
+  void checkApiMembers() {
+    ApiClass object =
+        apiCheck.getApiPackage("java.lang").getApiClass("java.lang.Object");
+    ApiClass apiClass =
+        apiCheck.getApiPackage("test.apicontainer").getApiClass(
+            "test.apicontainer.ApiClass");
+    ApiClass innerClass =
+        apiCheck.getApiPackage("test.apicontainer").getApiClass(
+            "test.apicontainer.NonApiClass.ApiClassInNonApiClass");
 
     // constructors
     assertEquals(1, innerClass.getApiMemberNames(
         ApiClass.MethodType.CONSTRUCTOR).size());
 
     // fields
-    assertEquals(2, object.getApiFieldNames().size());
-    assertEquals(3, apiClass.getApiFieldNames().size());
+    assertEquals(3, object.getApiFieldNames().size());
+    assertEquals(4, apiClass.getApiFieldNames().size());
 
     // methods
     assertEquals(2, object.getApiMemberNames(ApiClass.MethodType.METHOD).size());
@@ -236,10 +283,16 @@
   /**
    * Test if apiPackages are identified correctly.
    */
-  public void testApiPackages() {
-    assertNotNull(api1.getApiPackage("java.lang"));
-    assertNotNull(api1.getApiPackage("test.apicontainer"));
-    assertEquals(3, api1.getApiPackageNames().size());
+  void checkApiPackages() {
+    assertNotNull(apiCheck.getApiPackage("java.lang"));
+    assertNotNull(apiCheck.getApiPackage("test.apicontainer"));
+    assertEquals(3, apiCheck.getApiPackageNames().size());
   }
 
+  void checkInfiniteLoopInApiClass() {
+    ApiPackage tempPackage = apiCheckLoop.getApiPackage("java.lang");
+    assertNotNull(tempPackage);
+    assertNotNull(tempPackage.getApiClass("java.lang.Object"));
+    assertEquals(2, tempPackage.getApiClassNames().size());
+  }
 }