Merging releases/1.6@4459:4490

svn merge --accept=postpone -r4459:4490 https://google-web-toolkit.googlecode.com/svn/releases/1.6 .

Also:
- Added a checkstyle for OOPHM.
- Resolved Pruner merge issue with TODOs for spoon



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4491 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/build.xml b/dev/core/build.xml
index 2ca8b9a..b13b66d 100755
--- a/dev/core/build.xml
+++ b/dev/core/build.xml
@@ -45,6 +45,7 @@
       <zipfileset src="${gwt.tools.lib}/tomcat/naming-factory-1.0.jar" />
       <zipfileset src="${gwt.tools.lib}/tomcat/naming-java-1.0.jar" />
       <zipfileset src="${gwt.tools.lib}/tomcat/naming-resources-1.0.jar" />
+      <zipfileset src="${gwt.tools.lib}/tomcat/servlet-api-2.5.jar" />
       <zipfileset src="${gwt.tools.lib}/tomcat/servlet-api-2.4.jar" />
       <zipfileset src="${gwt.tools.lib}/tomcat/servlets-common-1.0.jar" />
       <zipfileset src="${gwt.tools.lib}/tomcat/servlets-default-1.0.jar" />
diff --git a/dev/core/src-dummy/com/google/gwt/dev/BootStrapPlatform.java b/dev/core/src-dummy/com/google/gwt/dev/BootStrapPlatform.java
index 9730d1e..3d2fc28 100644
--- a/dev/core/src-dummy/com/google/gwt/dev/BootStrapPlatform.java
+++ b/dev/core/src-dummy/com/google/gwt/dev/BootStrapPlatform.java
@@ -16,9 +16,8 @@
 package com.google.gwt.dev;
 
 /**
- * This is a dummy version of the class.  It only exists so that dev/core can
- * be compiled indepedently.  This class is replaced by a platform-specific
- * version.
+ * This is a dummy version of the class. It only exists so that dev/core can be
+ * compiled indepedently. This class is replaced by a platform-specific version.
  */
 public class BootStrapPlatform {
 
diff --git a/dev/core/src/com/google/gwt/dev/GWTShell.java b/dev/core/src/com/google/gwt/dev/GWTShell.java
index c9955de..d623526 100644
--- a/dev/core/src/com/google/gwt/dev/GWTShell.java
+++ b/dev/core/src/com/google/gwt/dev/GWTShell.java
@@ -36,7 +36,7 @@
  * @deprecated use {@link HostedMode} instead
  */
 @Deprecated
-public class GWTShell extends HostedModeBase {
+public class GWTShell extends SwtHostedModeBase {
 
   /**
    * Handles the list of startup urls that can be passed at the end of the
diff --git a/dev/core/src/com/google/gwt/dev/HostedMode.java b/dev/core/src/com/google/gwt/dev/HostedMode.java
index 90a4920..1d47fc3 100644
--- a/dev/core/src/com/google/gwt/dev/HostedMode.java
+++ b/dev/core/src/com/google/gwt/dev/HostedMode.java
@@ -45,7 +45,7 @@
  * this class is to be determined. Consider this class as having <b>no</b>
  * public API other than {@link #main(String[])}.
  */
-public class HostedMode extends HostedModeBase {
+public class HostedMode extends SwtHostedModeBase {
 
   /**
    * Handles the -server command line flag.
@@ -421,8 +421,8 @@
    * 
    * @param logger the logger to use
    * @param module the module to link
-   * @param includePublicFiles if <code>true</code>, include public files in
-   *          the link, otherwise do not include them
+   * @param includePublicFiles if <code>true</code>, include public files in the
+   *          link, otherwise do not include them
    * @throws UnableToCompleteException
    */
   private void link(TreeLogger logger, ModuleDef module)
diff --git a/dev/core/src/com/google/gwt/dev/HostedModeBase.java b/dev/core/src/com/google/gwt/dev/HostedModeBase.java
index 92e8b13..28741a0e 100644
--- a/dev/core/src/com/google/gwt/dev/HostedModeBase.java
+++ b/dev/core/src/com/google/gwt/dev/HostedModeBase.java
@@ -28,8 +28,6 @@
 import com.google.gwt.dev.shell.BrowserWidgetHostChecker;
 import com.google.gwt.dev.shell.BrowserWindowController;
 import com.google.gwt.dev.shell.ModuleSpaceHost;
-import com.google.gwt.dev.shell.PlatformSpecific;
-import com.google.gwt.dev.shell.ShellMainWindow;
 import com.google.gwt.dev.shell.ShellModuleSpaceHost;
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization;
@@ -39,20 +37,11 @@
 import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle;
 import com.google.gwt.dev.util.arg.OptionGenDir;
 import com.google.gwt.dev.util.arg.OptionLogLevel;
-import com.google.gwt.dev.util.log.AbstractTreeLogger;
 import com.google.gwt.util.tools.ArgHandlerFlag;
 import com.google.gwt.util.tools.ArgHandlerString;
 import com.google.gwt.util.tools.ToolBase;
 
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.events.DisposeListener;
-import org.eclipse.swt.graphics.Cursor;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.internal.Library;
-import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -62,7 +51,11 @@
 import java.util.Set;
 
 /**
- * The main executable class for the hosted mode shell.
+ * The main executable class for the hosted mode shell. This class must not have
+ * any GUI dependencies.
+ * 
+ * TODO: remove BrowserWidget references (which reference SWT via inheritance,
+ * though it doesn't appear to cause any harm currently.
  */
 abstract class HostedModeBase implements BrowserWindowController {
 
@@ -190,6 +183,52 @@
     }
   }
 
+  protected abstract class BrowserWidgetHostImpl implements BrowserWidgetHost {
+
+    public void compile() throws UnableToCompleteException {
+      if (isLegacyMode()) {
+        throw new UnsupportedOperationException();
+      }
+      HostedModeBase.this.compile(getLogger());
+    }
+
+    public void compile(String[] moduleNames) throws UnableToCompleteException {
+      if (!isLegacyMode()) {
+        throw new UnsupportedOperationException();
+      }
+      for (int i = 0; i < moduleNames.length; i++) {
+        String moduleName = moduleNames[i];
+        ModuleDef moduleDef = loadModule(getLogger(), moduleName, true);
+        HostedModeBase.this.compile(getLogger(), moduleDef);
+      }
+    }
+
+    public abstract ModuleSpaceHost createModuleSpaceHost(BrowserWidget widget,
+        final String moduleName) throws UnableToCompleteException;
+
+    public TreeLogger getLogger() {
+      return getTopLogger();
+    }
+
+    public boolean initModule(String moduleName) {
+      return HostedModeBase.this.initModule(moduleName);
+    }
+
+    @Deprecated
+    public boolean isLegacyMode() {
+      return HostedModeBase.this instanceof GWTShell;
+    }
+
+    public String normalizeURL(String whatTheUserTyped) {
+      return HostedModeBase.this.normalizeURL(whatTheUserTyped);
+    }
+
+    public BrowserWidget openNewBrowserWindow()
+        throws UnableToCompleteException {
+      return HostedModeBase.this.openNewBrowserWindow();
+    }
+  }
+
   protected interface HostedModeBaseOptions extends JJSOptions, OptionLogLevel,
       OptionGenDir, OptionNoServer, OptionPort, OptionStartupURLs {
 
@@ -283,75 +322,6 @@
     }
   }
 
-  private class BrowserWidgetHostImpl implements BrowserWidgetHost {
-
-    public void compile() throws UnableToCompleteException {
-      if (isLegacyMode()) {
-        throw new UnsupportedOperationException();
-      }
-      HostedModeBase.this.compile(getLogger());
-    }
-
-    public void compile(String[] moduleNames) throws UnableToCompleteException {
-      if (!isLegacyMode()) {
-        throw new UnsupportedOperationException();
-      }
-      for (int i = 0; i < moduleNames.length; i++) {
-        String moduleName = moduleNames[i];
-        ModuleDef moduleDef = loadModule(getLogger(), moduleName, true);
-        HostedModeBase.this.compile(getLogger(), moduleDef);
-      }
-    }
-
-    public ModuleSpaceHost createModuleSpaceHost(BrowserWidget widget,
-        final String moduleName) throws UnableToCompleteException {
-      TreeLogger logger = getLogger();
-
-      // Switch to a wait cursor.
-      //
-      Shell widgetShell = widget.getShell();
-      try {
-        Cursor waitCursor = display.getSystemCursor(SWT.CURSOR_WAIT);
-        widgetShell.setCursor(waitCursor);
-
-        // Try to find an existing loaded version of the module def.
-        //
-        ModuleDef moduleDef = loadModule(logger, moduleName, true);
-        assert (moduleDef != null);
-
-        TypeOracle typeOracle = moduleDef.getTypeOracle(logger);
-        ShellModuleSpaceHost host = doCreateShellModuleSpaceHost(logger,
-            typeOracle, moduleDef);
-        return host;
-      } finally {
-        Cursor normalCursor = display.getSystemCursor(SWT.CURSOR_ARROW);
-        widgetShell.setCursor(normalCursor);
-      }
-    }
-
-    public TreeLogger getLogger() {
-      return getTopLogger();
-    }
-
-    public boolean initModule(String moduleName) {
-      return HostedModeBase.this.initModule(moduleName);
-    }
-
-    @Deprecated
-    public boolean isLegacyMode() {
-      return HostedModeBase.this instanceof GWTShell;
-    }
-
-    public String normalizeURL(String whatTheUserTyped) {
-      return HostedModeBase.this.normalizeURL(whatTheUserTyped);
-    }
-
-    public BrowserWidget openNewBrowserWindow()
-        throws UnableToCompleteException {
-      return HostedModeBase.this.openNewBrowserWindow();
-    }
-  }
-
   static {
     // Force ToolBase to clinit, which causes SWT stuff to happen.
     new ToolBase() {
@@ -370,20 +340,8 @@
    */
   private Set<String> alreadySeenModules = new HashSet<String>();
 
-  private BrowserWidgetHostImpl browserHost = new BrowserWidgetHostImpl();
-
-  private final List<Shell> browserShells = new ArrayList<Shell>();
-
-  /**
-   * Use the default display; constructing a new one would make instantiating
-   * multiple GWTShells fail with a mysterious exception.
-   */
-  private final Display display = Display.getDefault();
-
   private boolean headlessMode = false;
 
-  private ShellMainWindow mainWnd;
-
   private boolean started;
 
   public HostedModeBase() {
@@ -397,46 +355,20 @@
     options.addStartupURL(url);
   }
 
-  public final void closeAllBrowserWindows() {
-    while (!browserShells.isEmpty()) {
-      browserShells.get(0).dispose();
-    }
-  }
+  public abstract void closeAllBrowserWindows();
 
   public final int getPort() {
     return options.getPort();
   }
 
-  public TreeLogger getTopLogger() {
-    return mainWnd.getLogger();
-  }
+  public abstract TreeLogger getTopLogger();
 
-  public final boolean hasBrowserWindowsOpen() {
-    if (browserShells.isEmpty()) {
-      return false;
-    } else {
-      return true;
-    }
-  }
+  public abstract boolean hasBrowserWindowsOpen();
 
   /**
    * Launch the arguments as Urls in separate windows.
    */
-  public void launchStartupUrls(final TreeLogger logger) {
-    // Launch a browser window for each startup url.
-    String startupURL = "";
-    try {
-      for (String prenormalized : options.getStartupURLs()) {
-        startupURL = normalizeURL(prenormalized);
-        logger.log(TreeLogger.TRACE, "Starting URL: " + startupURL, null);
-        BrowserWidget bw = openNewBrowserWindow();
-        bw.go(startupURL);
-      }
-    } catch (UnableToCompleteException e) {
-      logger.log(TreeLogger.ERROR,
-          "Unable to open new window for startup URL: " + startupURL, null);
-    }
-  }
+  public abstract void launchStartupUrls(final TreeLogger logger);
 
   public final String normalizeURL(String unknownUrlText) {
     if (unknownUrlText.indexOf(":") != -1) {
@@ -462,39 +394,6 @@
   }
 
   /**
-   * Called directly by ShellMainWindow and indirectly via BrowserWidgetHost.
-   */
-  public final BrowserWidget openNewBrowserWindow()
-      throws UnableToCompleteException {
-    boolean succeeded = false;
-    Shell s = createTrackedBrowserShell();
-    try {
-      BrowserWidget bw = PlatformSpecific.createBrowserWidget(getTopLogger(),
-          s, browserHost);
-
-      if (mainWnd != null) {
-        Rectangle r = mainWnd.getShell().getBounds();
-        int n = browserShells.size() + 1;
-        s.setBounds(r.x + n * 50, r.y + n * 50, 800, 600);
-      } else {
-        s.setSize(800, 600);
-      }
-
-      if (!isHeadless()) {
-        s.open();
-      }
-
-      bw.onFirstShown();
-      succeeded = true;
-      return bw;
-    } finally {
-      if (!succeeded) {
-        s.dispose();
-      }
-    }
-  }
-
-  /**
    * Sets up all the major aspects of running the shell graphically, including
    * creating the main window and optionally starting the embedded Tomcat
    * server.
@@ -585,18 +484,11 @@
 
   protected abstract int doStartUpServer();
 
-  protected final BrowserWidgetHost getBrowserHost() {
-    return browserHost;
-  }
-
   protected String getHost() {
     return "localhost";
   }
 
-  protected void initializeLogger() {
-    final AbstractTreeLogger logger = mainWnd.getLogger();
-    logger.setMaxDetail(options.getLogLevel());
-  }
+  protected abstract void initializeLogger();
 
   /**
    * Called from a selection script as it begins to load in hosted mode. This
@@ -638,36 +530,13 @@
     return moduleDef;
   }
 
-  protected boolean notDone() {
-    if (!mainWnd.isDisposed()) {
-      return true;
-    }
-    if (!browserShells.isEmpty()) {
-      return true;
-    }
-    return false;
-  }
+  protected abstract void loadRequiredNativeLibs();
 
-  protected void openAppWindow() {
-    final Shell shell = new Shell(display);
+  protected abstract boolean notDone();
 
-    FillLayout fillLayout = new FillLayout();
-    fillLayout.marginWidth = 0;
-    fillLayout.marginHeight = 0;
-    shell.setLayout(fillLayout);
+  protected abstract void openAppWindow();
 
-    shell.setImages(ShellMainWindow.getIcons());
-
-    boolean checkForUpdates = doShouldCheckForUpdates();
-
-    mainWnd = new ShellMainWindow(this, shell, options.isNoServer() ? 0
-        : getPort(), checkForUpdates);
-
-    shell.setSize(700, 600);
-    if (!isHeadless()) {
-      shell.open();
-    }
-  }
+  protected abstract void processEvents() throws Exception;
 
   protected final void pumpEventLoop() {
     TreeLogger logger = getTopLogger();
@@ -676,9 +545,7 @@
     //
     while (notDone()) {
       try {
-        if (!display.readAndDispatch()) {
-          sleep();
-        }
+        processEvents();
       } catch (Throwable e) {
         String msg = e.getMessage();
         msg = (msg != null ? msg : e.getClass().getName());
@@ -698,10 +565,6 @@
     doShutDownServer();
   }
 
-  protected void sleep() {
-    display.sleep();
-  }
-
   protected final boolean startUp() {
     if (started) {
       throw new IllegalStateException("Startup code has already been run");
@@ -713,8 +576,6 @@
       return false;
     }
 
-    startupHook();
-
     if (!options.isNoServer()) {
       int resultPort = doStartUpServer();
       if (resultPort < 0) {
@@ -725,45 +586,4 @@
 
     return true;
   }
-
-  /**
-   * Hook for subclasses to initialize things after the window and logger are
-   * initialized but before the embedded server is started.
-   */
-  protected void startupHook() {
-  }
-
-  private Shell createTrackedBrowserShell() {
-    final Shell shell = new Shell(display);
-    FillLayout fillLayout = new FillLayout();
-    fillLayout.marginWidth = 0;
-    fillLayout.marginHeight = 0;
-    shell.setLayout(fillLayout);
-    browserShells.add(shell);
-    shell.addDisposeListener(new DisposeListener() {
-      public void widgetDisposed(DisposeEvent e) {
-        if (e.widget == shell) {
-          browserShells.remove(shell);
-        }
-      }
-    });
-
-    shell.setImages(ShellMainWindow.getIcons());
-
-    return shell;
-  }
-
-  private void loadRequiredNativeLibs() {
-    String libName = null;
-    try {
-      libName = "swt";
-      Library.loadLibrary(libName);
-    } catch (UnsatisfiedLinkError e) {
-      StringBuffer sb = new StringBuffer();
-      sb.append("Unable to load required native library '" + libName + "'");
-      sb.append("\n\tPlease specify the JVM startup argument ");
-      sb.append("\"-Djava.library.path\"");
-      throw new RuntimeException(sb.toString(), e);
-    }
-  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java b/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
new file mode 100644
index 0000000..bda5243
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
@@ -0,0 +1,265 @@
+/*
+ * 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.dev;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.shell.BrowserWidget;
+import com.google.gwt.dev.shell.BrowserWidgetHost;
+import com.google.gwt.dev.shell.ModuleSpaceHost;
+import com.google.gwt.dev.shell.PlatformSpecific;
+import com.google.gwt.dev.shell.ShellMainWindow;
+import com.google.gwt.dev.shell.ShellModuleSpaceHost;
+import com.google.gwt.dev.util.log.AbstractTreeLogger;
+import com.google.gwt.util.tools.ToolBase;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.internal.Library;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The main executable class for hosted mode shells based on SWT.
+ */
+abstract class SwtHostedModeBase extends HostedModeBase {
+
+  private class SwtBrowserWidgetHostImpl extends BrowserWidgetHostImpl {
+
+    @Override
+    public ModuleSpaceHost createModuleSpaceHost(BrowserWidget widget,
+        final String moduleName) throws UnableToCompleteException {
+      TreeLogger logger = getLogger();
+
+      // Switch to a wait cursor.
+      //
+      Shell widgetShell = widget.getShell();
+      try {
+        Cursor waitCursor = display.getSystemCursor(SWT.CURSOR_WAIT);
+        widgetShell.setCursor(waitCursor);
+
+        // Try to find an existing loaded version of the module def.
+        //
+        ModuleDef moduleDef = loadModule(logger, moduleName, true);
+        assert (moduleDef != null);
+
+        TypeOracle typeOracle = moduleDef.getTypeOracle(logger);
+        ShellModuleSpaceHost host = doCreateShellModuleSpaceHost(logger,
+            typeOracle, moduleDef);
+        return host;
+      } finally {
+        Cursor normalCursor = display.getSystemCursor(SWT.CURSOR_ARROW);
+        widgetShell.setCursor(normalCursor);
+      }
+    }
+  }
+
+  static {
+    // Force ToolBase to clinit, which causes SWT stuff to happen.
+    new ToolBase() {
+    };
+    // Correct menu on Mac OS X
+    Display.setAppName("GWT");
+  }
+
+  private BrowserWidgetHostImpl browserHost = new SwtBrowserWidgetHostImpl();
+
+  private final List<Shell> browserShells = new ArrayList<Shell>();
+
+  /**
+   * Use the default display; constructing a new one would make instantiating
+   * multiple GWTShells fail with a mysterious exception.
+   */
+  private final Display display = Display.getDefault();
+
+  private ShellMainWindow mainWnd;
+
+  public SwtHostedModeBase() {
+    super();
+  }
+
+  @Override
+  public final void closeAllBrowserWindows() {
+    while (!browserShells.isEmpty()) {
+      browserShells.get(0).dispose();
+    }
+  }
+
+  @Override
+  public TreeLogger getTopLogger() {
+    return mainWnd.getLogger();
+  }
+
+  @Override
+  public final boolean hasBrowserWindowsOpen() {
+    if (browserShells.isEmpty()) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+  /**
+   * Launch the arguments as Urls in separate windows.
+   */
+  @Override
+  public void launchStartupUrls(final TreeLogger logger) {
+    // Launch a browser window for each startup url.
+    String startupURL = "";
+    try {
+      for (String prenormalized : options.getStartupURLs()) {
+        startupURL = normalizeURL(prenormalized);
+        logger.log(TreeLogger.TRACE, "Starting URL: " + startupURL, null);
+        BrowserWidget bw = openNewBrowserWindow();
+        bw.go(startupURL);
+      }
+    } catch (UnableToCompleteException e) {
+      logger.log(TreeLogger.ERROR,
+          "Unable to open new window for startup URL: " + startupURL, null);
+    }
+  }
+
+  /**
+   * Called directly by ShellMainWindow and indirectly via BrowserWidgetHost.
+   */
+  public final BrowserWidget openNewBrowserWindow()
+      throws UnableToCompleteException {
+    boolean succeeded = false;
+    Shell s = createTrackedBrowserShell();
+    try {
+      BrowserWidget bw = PlatformSpecific.createBrowserWidget(getTopLogger(),
+          s, browserHost);
+
+      if (mainWnd != null) {
+        Rectangle r = mainWnd.getShell().getBounds();
+        int n = browserShells.size() + 1;
+        s.setBounds(r.x + n * 50, r.y + n * 50, 800, 600);
+      } else {
+        s.setSize(800, 600);
+      }
+
+      if (!isHeadless()) {
+        s.open();
+      }
+
+      bw.onFirstShown();
+      succeeded = true;
+      return bw;
+    } finally {
+      if (!succeeded) {
+        s.dispose();
+      }
+    }
+  }
+
+  protected final BrowserWidgetHost getBrowserHost() {
+    return browserHost;
+  }
+
+  @Override
+  protected void initializeLogger() {
+    final AbstractTreeLogger logger = mainWnd.getLogger();
+    logger.setMaxDetail(options.getLogLevel());
+  }
+
+  @Override
+  protected void loadRequiredNativeLibs() {
+    String libName = null;
+    try {
+      libName = "swt";
+      Library.loadLibrary(libName);
+    } catch (UnsatisfiedLinkError e) {
+      StringBuffer sb = new StringBuffer();
+      sb.append("Unable to load required native library '" + libName + "'");
+      sb.append("\n\tPlease specify the JVM startup argument ");
+      sb.append("\"-Djava.library.path\"");
+      throw new RuntimeException(sb.toString(), e);
+    }
+  }
+
+  @Override
+  protected boolean notDone() {
+    if (!mainWnd.isDisposed()) {
+      return true;
+    }
+    if (!browserShells.isEmpty()) {
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  protected void openAppWindow() {
+    final Shell shell = new Shell(display);
+
+    FillLayout fillLayout = new FillLayout();
+    fillLayout.marginWidth = 0;
+    fillLayout.marginHeight = 0;
+    shell.setLayout(fillLayout);
+
+    shell.setImages(ShellMainWindow.getIcons());
+
+    boolean checkForUpdates = doShouldCheckForUpdates();
+
+    mainWnd = new ShellMainWindow(this, shell, options.isNoServer() ? 0
+        : getPort(), checkForUpdates);
+
+    shell.setSize(700, 600);
+    if (!isHeadless()) {
+      shell.open();
+    }
+  }
+
+  @Override
+  protected void processEvents() throws Exception {
+    if (!display.readAndDispatch()) {
+      sleep();
+    }
+  }
+
+  protected void sleep() {
+    display.sleep();
+  }
+
+  private Shell createTrackedBrowserShell() {
+    final Shell shell = new Shell(display);
+    FillLayout fillLayout = new FillLayout();
+    fillLayout.marginWidth = 0;
+    fillLayout.marginHeight = 0;
+    shell.setLayout(fillLayout);
+    browserShells.add(shell);
+    shell.addDisposeListener(new DisposeListener() {
+      public void widgetDisposed(DisposeEvent e) {
+        if (e.widget == shell) {
+          browserShells.remove(shell);
+        }
+      }
+    });
+
+    shell.setImages(ShellMainWindow.getIcons());
+
+    return shell;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
index f5a77bd..e2653a1 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
@@ -48,7 +48,7 @@
 
   static class AnonymousClassVisitor extends EmptyVisitor {
     /*
-     * array of classNames of inner clases that aren't synthetic.
+     * array of classNames of inner clases that aren't synthetic classes.
      */
     List<String> classNames = new ArrayList<String>();
 
diff --git a/dev/core/src/com/google/gwt/dev/javac/GeneratedClassnameComparator.java b/dev/core/src/com/google/gwt/dev/javac/GeneratedClassnameComparator.java
index e7280b2..bdd642f 100644
--- a/dev/core/src/com/google/gwt/dev/javac/GeneratedClassnameComparator.java
+++ b/dev/core/src/com/google/gwt/dev/javac/GeneratedClassnameComparator.java
@@ -17,6 +17,16 @@
 
 import java.util.Comparator;
 
+/**
+ * Comparator to sort the compiler-generated classNames so that they can be
+ * correlated. Examples of sorting orders by the comparator:
+ * 
+ * <pre>
+ * <ul>
+ * <li> Foo$1 < Foo$2 < Foo$3 < ... < Foo$10
+ * <li> Foo$1 < Foo$2 < Foo$1$1 < Foo$1$2 < Foo$2$1 < Foo$2$2 < Foo$2$Baz
+ * </pre>
+ */
 class GeneratedClassnameComparator implements Comparator<String> {
 
   public int compare(String arg0, String arg1) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
index d401d5e..5f85a48 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -170,6 +170,9 @@
        * would pull in the clinit of that class, which has initializers for all
        * the class literals, which in turn have all of the strings of all of the
        * class names.
+       * 
+       * TODO: Model ClassLiteral access a different way to avoid special magic.
+       * See Pruner.transformToNullFieldRef()/transformToNullMethodCall().
        */
       JField field = x.getField();
       rescue(field);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
index a948c56..44b85f4 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -34,9 +34,11 @@
 import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JNode;
 import com.google.gwt.dev.jjs.ast.JParameter;
+import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.jjs.ast.JVariable;
 import com.google.gwt.dev.jjs.ast.JVariableRef;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
 import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
@@ -47,8 +49,10 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Stack;
 
 /**
  * Remove globally unreferenced classes, interfaces, methods, parameters, and
@@ -71,12 +75,19 @@
 public class Pruner {
 
   /**
-   * Remove assignments to pruned fields, locals and params. Also nullify the
-   * return type of methods declared to return a globally uninstantiable type.
+   * Remove assignments to pruned fields, locals and params. Nullify the return
+   * type of methods declared to return a globally uninstantiable type. Replace
+   * references to pruned variables and methods by references to the null field
+   * and null method, and drop assignments to pruned variables.
    */
   private class CleanupRefsVisitor extends JModVisitor {
+    private Stack<JExpression> lValues = new Stack<JExpression>();
     private final Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap;
     private final Set<? extends JNode> referencedNonTypes;
+    {
+      // Initialize a sentinel value to avoid having to check for empty stack.
+      lValues.push(null);
+    }
 
     public CleanupRefsVisitor(Set<? extends JNode> referencedNodes,
         Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap) {
@@ -88,10 +99,11 @@
     public void endVisit(JBinaryOperation x, Context ctx) {
       // The LHS of assignments may have been pruned.
       if (x.getOp() == JBinaryOperator.ASG) {
+        lValues.pop();
         JExpression lhs = x.getLhs();
         if (lhs instanceof JVariableRef) {
           JVariableRef variableRef = (JVariableRef) lhs;
-          if (!referencedNonTypes.contains(variableRef.getTarget())) {
+          if (isVariablePruned(variableRef.getTarget())) {
             // TODO: better null tracking; we might be missing some NPEs here.
             JExpression replacement = makeReplacementForAssignment(
                 x.getSourceInfo(), variableRef, x.getRhs());
@@ -103,8 +115,9 @@
 
     @Override
     public void endVisit(JDeclarationStatement x, Context ctx) {
+      lValues.pop();
       // The variable may have been pruned.
-      if (!referencedNonTypes.contains(x.getVariableRef().getTarget())) {
+      if (isVariablePruned(x.getVariableRef().getTarget())) {
         JExpression replacement = makeReplacementForAssignment(
             x.getSourceInfo(), x.getVariableRef(), x.getInitializer());
         ctx.replaceMe(replacement.makeStatement());
@@ -112,6 +125,20 @@
     }
 
     @Override
+    public void endVisit(JFieldRef x, Context ctx) {
+      // Handle l-values at a higher level.
+      if (lValues.peek() == x) {
+        return;
+      }
+
+      if (isPruned(x.getField())) {
+        // The field is gone; replace x by a null field reference.
+        JFieldRef fieldRef = transformToNullFieldRef(x, program);
+        ctx.replaceMe(fieldRef);
+      }
+    }
+
+    @Override
     public void endVisit(JMethod x, Context ctx) {
       JType type = x.getType();
       if (type instanceof JReferenceType) {
@@ -125,6 +152,16 @@
     public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
 
+      // Is the method pruned entirely?
+      if (isPruned(method)) {
+        /*
+         * We assert that method must be non-static, otherwise it would have
+         * been rescued.
+         */
+        ctx.replaceMe(transformToNullMethodCall(x, program));
+        return;
+      }
+
       // Did we prune the parameters of the method we're calling?
       if (methodToOriginalParamsMap.containsKey(method)) {
         // This must be a static method
@@ -176,7 +213,7 @@
 
     @Override
     public void endVisit(JsniFieldRef x, Context ctx) {
-      if (isUninstantiable(x.getField())) {
+      if (isPruned(x.getField())) {
         String ident = x.getIdent();
         JField nullField = program.getNullField();
         program.jsniMap.put(ident, nullField);
@@ -190,7 +227,7 @@
     @Override
     public void endVisit(JsniMethodRef x, Context ctx) {
       // Redirect JSNI refs to uninstantiable types to the null method.
-      if (isUninstantiable(x.getTarget())) {
+      if (isPruned(x.getTarget())) {
         String ident = x.getIdent();
         JMethod nullMethod = program.getNullMethod();
         program.jsniMap.put(ident, nullMethod);
@@ -200,13 +237,36 @@
       }
     }
 
-    private <T extends HasEnclosingType & CanBeStatic> boolean isUninstantiable(
-        T node) {
+    @Override
+    public boolean visit(JBinaryOperation x, Context ctx) {
+      if (x.getOp() == JBinaryOperator.ASG) {
+        lValues.push(x.getLhs());
+      }
+      return true;
+    }
+
+    @Override
+    public boolean visit(JDeclarationStatement x, Context ctx) {
+      lValues.push(x.getVariableRef());
+      return true;
+    }
+
+    private <T extends HasEnclosingType & CanBeStatic> boolean isPruned(T node) {
+      if (!referencedNonTypes.contains(node)) {
+        return true;
+      }
       JReferenceType enclosingType = node.getEnclosingType();
       return !node.isStatic() && enclosingType != null
           && !program.typeOracle.isInstantiatedType(enclosingType);
     }
 
+    private boolean isVariablePruned(JVariable variable) {
+      if (variable instanceof JField) {
+        return isPruned((JField) variable);
+      }
+      return !referencedNonTypes.contains(variable);
+    }
+
     private JExpression makeReplacementForAssignment(SourceInfo info,
         JVariableRef variableRef, JExpression rhs) {
       // Replace with a multi, which may wind up empty.
@@ -221,7 +281,7 @@
         }
       }
 
-      // If there is an initializer, evaluate it second.
+      // If there is an rhs, evaluate it second.
       if (rhs != null) {
         multi.exprs.add(rhs);
       }
@@ -439,6 +499,96 @@
     return new Pruner(program, noSpecialTypes).execImpl();
   }
 
+  /**
+   * Transform a reference to a pruned instance field into a reference to the
+   * null field, which will be used to replace <code>x</code>.
+   */
+  public static JFieldRef transformToNullFieldRef(JFieldRef x, JProgram program) {
+    JExpression instance = x.getInstance();
+
+    /*
+     * We assert that field must be non-static if it's an rvalue, otherwise it
+     * would have been rescued.
+     */
+    // assert !x.getField().isStatic();
+    /*
+     * HACK HACK HACK: ControlFlowAnalyzer has special hacks for dealing with
+     * ClassLiterals, which causes the body of ClassLiteralHolder's clinit to
+     * never be rescured. This in turn causes invalid references to static
+     * methods, which violates otherwise good assumptions about compiler
+     * operation.
+     * 
+     * TODO: Remove this when ControlFlowAnalyzer doesn't special-case
+     * CLH.clinit().
+     */
+    if (x.getField().isStatic() && instance == null) {
+      instance = program.getLiteralNull();
+    }
+
+    assert instance != null;
+    if (!instance.hasSideEffects()) {
+      instance = program.getLiteralNull();
+    }
+
+    JFieldRef fieldRef = new JFieldRef(program, x.getSourceInfo(), instance,
+        program.getNullField(), x.getEnclosingType(), primitiveTypeOrNullType(
+            program, x.getType()));
+    return fieldRef;
+  }
+
+  /**
+   * Transform a call to a pruned instance method (or static impl) into a call
+   * to the null method, which will be used to replace <code>x</code>.
+   */
+  public static JMethodCall transformToNullMethodCall(JMethodCall x,
+      JProgram program) {
+    JExpression instance = x.getInstance();
+    List<JExpression> args = x.getArgs();
+    if (program.isStaticImpl(x.getTarget())) {
+      instance = args.get(0);
+      args = args.subList(1, args.size());
+    } else {
+      /*
+       * We assert that method must be non-static, otherwise it would have been
+       * rescued.
+       */
+      // assert !x.getTarget().isStatic();
+      /*
+       * HACK HACK HACK: ControlFlowAnalyzer has special hacks for dealing with
+       * ClassLiterals, which causes the body of ClassLiteralHolder's clinit to
+       * never be rescured. This in turn causes invalid references to static
+       * methods, which violates otherwise good assumptions about compiler
+       * operation.
+       * 
+       * TODO: Remove this when ControlFlowAnalyzer doesn't special-case
+       * CLH.clinit().
+       */
+      if (x.getTarget().isStatic() && instance == null) {
+        instance = program.getLiteralNull();
+      }
+    }
+    assert (instance != null);
+    if (!instance.hasSideEffects()) {
+      instance = program.getLiteralNull();
+    }
+
+    JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(), instance,
+        program.getNullMethod(), primitiveTypeOrNullType(program, x.getType()));
+    // Retain the original arguments, they will be evaluated for side effects.
+    newCall.getArgs().addAll(args);
+    return newCall;
+  }
+
+  /**
+   * Return the smallest type that is is a subtype of the argument.
+   */
+  static JType primitiveTypeOrNullType(JProgram program, JType type) {
+    if (type instanceof JPrimitiveType) {
+      return type;
+    }
+    return program.getTypeNull();
+  }
+
   private final JProgram program;
   private final boolean saveCodeGenTypes;
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 749bd5a..ac9815c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -38,7 +38,6 @@
 import com.google.gwt.dev.jjs.ast.JNullType;
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
-import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
@@ -110,14 +109,13 @@
               x.getField(), x.getEnclosingType());
           ctx.replaceMe(fieldRef);
         }
-      } else if (!isStatic && instance.getType() == typeNull) {
+      } else if (!isStatic && instance.getType() == typeNull
+          && x.getField() != program.getNullField()) {
+        // Change any dereference of null to use the null field
         if (!instance.hasSideEffects()) {
           instance = program.getLiteralNull();
         }
-        JFieldRef fieldRef = new JFieldRef(program, x.getSourceInfo(),
-            instance, program.getNullField(), null,
-            primitiveTypeOrNullType(x.getType()));
-        ctx.replaceMe(fieldRef);
+        ctx.replaceMe(Pruner.transformToNullFieldRef(x, program));
       }
     }
 
@@ -137,38 +135,14 @@
           ctx.replaceMe(newCall);
         }
       } else if (!isStatic && instance.getType() == typeNull) {
-        // bind null instance calls to the null method
-        if (!instance.hasSideEffects()) {
-          instance = program.getLiteralNull();
-        }
-        JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(),
-            instance, program.getNullMethod(),
-            primitiveTypeOrNullType(x.getType()));
-        ctx.replaceMe(newCall);
+        ctx.replaceMe(Pruner.transformToNullMethodCall(x, program));
       } else if (isStaticImpl && method.params.size() > 0
           && method.params.get(0).isThis() && x.getArgs().size() > 0
           && x.getArgs().get(0).getType() == typeNull) {
         // bind null instance calls to the null method for static impls
-        instance = x.getArgs().get(0);
-        if (!instance.hasSideEffects()) {
-          instance = program.getLiteralNull();
-        }
-        JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(),
-            instance, program.getNullMethod(),
-            primitiveTypeOrNullType(x.getType()));
-        ctx.replaceMe(newCall);
+        ctx.replaceMe(Pruner.transformToNullMethodCall(x, program));
       }
     }
-
-    /**
-     * Return the smallest type that is is a subtype of the argument.
-     */
-    private JType primitiveTypeOrNullType(JType type) {
-      if (type instanceof JPrimitiveType) {
-        return type;
-      }
-      return program.getTypeNull();
-    }
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
index 6097117..f963fb0 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
@@ -255,7 +255,6 @@
    * Rescans the associated paths to recompute the available resources.
    * 
    * @param logger status and error details are written here
-   * @throws UnableToCompleteException
    */
   public void refresh(TreeLogger logger) {
     TreeLogger refreshBranch = Messages.REFRESHING_RESOURCES.branch(logger,
@@ -263,7 +262,8 @@
 
     /*
      * Allocate fresh data structures in anticipation of needing to honor the
-     * "new identity for the collections if anything changes" guarantee.
+     * "new identity for the collections if anything changes" guarantee. Use a
+     * LinkedHashMap because we do not want the order to change.
      */
     final Map<String, AbstractResource> newInternalMap = new LinkedHashMap<String, AbstractResource>();
 
diff --git a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
index d5c86f2..90e5ebe 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -791,7 +791,7 @@
        * find it on disk. Typically this is a synthetic class added by the
        * compiler.
        */
-      if (shouldLoadClassFromDisk(className)) {
+      if (typeHasCompilationUnit(className) && isClassnameGenerated(className)) {
         /*
          * modification time = 0 ensures that whatever is on the disk is always
          * loaded.
@@ -825,9 +825,8 @@
   }
 
   /**
-   * Returns the compilationUnit corresponding to the className.
-   * <p>
-   * Not considering classnames where a $ sign appears.
+   * Returns the compilationUnit corresponding to the className. For nested
+   * classes, the unit corresponding to the top level type is returned.
    */
   private CompilationUnit getUnitForClassName(String className) {
     String mainTypeName = className;
@@ -848,14 +847,10 @@
     shellJavaScriptHost.createNativeMethods(logger, unit.getJsniMethods(), this);
   }
 
-  private boolean isBaseClassInGwt(String className) {
+  private boolean typeHasCompilationUnit(String className) {
     return getUnitForClassName(className) != null;
   }
 
-  private boolean shouldLoadClassFromDisk(String className) {
-    return isBaseClassInGwt(className) && isClassnameGenerated(className);
-  }
-
   /**
    * Tricky one, this. Reaches over into this modules's JavaScriptHost class and
    * sets its static 'host' field to our module space.
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java b/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java
index 5871c12..489e571 100644
--- a/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java
+++ b/dev/core/test/com/google/gwt/dev/resource/impl/AbstractResourceOrientedTestBase.java
@@ -132,12 +132,14 @@
   }
 
   protected void assertPathIncluded(Set<AbstractResource> resources, String path) {
-    assertNotNull(findResourceWithPath(resources, path));
+    assertNotNull("path = " + path + " should have been found in resources = "
+        + resources, findResourceWithPath(resources, path));
   }
 
   protected void assertPathNotIncluded(Set<AbstractResource> resources,
       String path) {
-    assertNull(findResourceWithPath(resources, path));
+    assertNull("path = " + path + " should not have been found in resources = "
+        + resources, findResourceWithPath(resources, path));
   }
 
   protected File findJarDirectory(String name) throws URISyntaxException {
diff --git a/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java
index c6fe7e0..46e166d 100644
--- a/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java
+++ b/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java
@@ -25,6 +25,7 @@
 import java.io.InputStreamReader;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -59,9 +60,9 @@
   }
 
   private static class ResourceOracleSnapshot {
-    private final Set<Resource> resources;
-    private final Map<String, Resource> resourceMap;
     private final Set<String> pathNames;
+    private final Map<String, Resource> resourceMap;
+    private final Set<Resource> resources;
 
     public ResourceOracleSnapshot(ResourceOracleImpl oracle) {
       resources = oracle.getResources();
@@ -127,6 +128,34 @@
     }
   }
 
+  /**
+   * Test that ResourceOracleImpl preserves the order in which the same logical
+   * resource is occurs in multiple ClassPathEntries.
+   * 
+   * @throws URISyntaxException
+   * @throws IOException
+   */
+  public void testClassPathOrderIsHonored() throws IOException,
+      URISyntaxException {
+    TreeLogger logger = createTestTreeLogger();
+    ClassPathEntry cpe1jar = getClassPathEntry1AsJar();
+    ClassPathEntry cpe2jar = getClassPathEntry2AsJar();
+
+    ClassPathEntry[] cp12 = new ClassPathEntry[] {cpe1jar, cpe2jar};
+    ClassPathEntry[] cp21 = new ClassPathEntry[] {cpe2jar, cpe1jar};
+    String resKeyNormal = "org/example/bar/client/BarClient2.txt";
+    String resKeyReroot = "/BarClient2.txt";
+    PathPrefix pathPrefixNormal = new PathPrefix("org/example/bar/client",
+        null, false);
+    PathPrefix pathPrefixReroot = new PathPrefix("org/example/bar/client",
+        null, true);
+
+    testClassPathOrderIsHonored(logger, resKeyNormal, cp12, pathPrefixNormal);
+    testClassPathOrderIsHonored(logger, resKeyReroot, cp12, pathPrefixReroot);
+    testClassPathOrderIsHonored(logger, resKeyNormal, cp21, pathPrefixNormal);
+    testClassPathOrderIsHonored(logger, resKeyReroot, cp21, pathPrefixReroot);
+  }
+
   public void testNoClassPathEntries() {
     TreeLogger logger = createTestTreeLogger();
     ResourceOracleImpl oracle = createResourceOracle(new MOCK_CPE0());
@@ -139,7 +168,6 @@
    * 
    * @throws URISyntaxException
    * @throws IOException
-   * @throws UnableToCompleteException
    */
   public void testReadingResource() throws IOException, URISyntaxException {
     ClassPathEntry cpe1jar = getClassPathEntry1AsJar();
@@ -229,9 +257,6 @@
   /**
    * Creates an array of class path entries, setting up each one with a
    * well-known set of client prefixes.
-   * 
-   * @param entries
-   * @return
    */
   private ResourceOracleImpl createResourceOracle(ClassPathEntry... entries) {
     PathPrefixSet pps = new PathPrefixSet();
@@ -255,14 +280,22 @@
     return new ResourceOracleSnapshot(oracle);
   }
 
+  private void testClassPathOrderIsHonored(TreeLogger logger,
+      String resourceKey, ClassPathEntry[] classPath, PathPrefix pathPrefix) {
+    PathPrefixSet pps = new PathPrefixSet();
+    pps.add(pathPrefix);
+    ResourceOracleImpl oracle = new ResourceOracleImpl(Arrays.asList(classPath));
+    oracle.setPathPrefixes(pps);
+    ResourceOracleSnapshot s = refreshAndSnapshot(logger, oracle);
+    s.assertPathIncluded(resourceKey, classPath[0]);
+  }
+
   private void testReadingResource(ClassPathEntry cpe1, ClassPathEntry cpe2)
       throws IOException {
     TreeLogger logger = createTestTreeLogger();
 
     ResourceOracleImpl oracle = createResourceOracle(cpe1, cpe2);
-
-    oracle.refresh(logger);
-    ResourceOracleSnapshot s = new ResourceOracleSnapshot(oracle);
+    ResourceOracleSnapshot s = refreshAndSnapshot(logger, oracle);
     s.assertCollectionsConsistent(9);
     s.assertPathIncluded("com/google/gwt/user/client/Command.java", cpe1);
     s.assertPathIncluded("com/google/gwt/i18n/client/Messages.java", cpe2);
@@ -440,8 +473,7 @@
       /*
        * Baseline assumptions about the set of resources present by default.
        */
-      oracle.refresh(logger);
-      ResourceOracleSnapshot s = new ResourceOracleSnapshot(oracle);
+      ResourceOracleSnapshot s = refreshAndSnapshot(logger, oracle);
       s.assertPathIncluded("com/google/gwt/user/client/Command.java", cpe1);
       s.assertPathIncluded("com/google/gwt/user/client/Timer.java", cpe1);
     }
@@ -454,8 +486,7 @@
       /*
        * Ensure that the dups have the effect we expect.
        */
-      oracle.refresh(logger);
-      ResourceOracleSnapshot s = new ResourceOracleSnapshot(oracle);
+      ResourceOracleSnapshot s = refreshAndSnapshot(logger, oracle);
       s.assertPathIncluded("com/google/gwt/user/client/Command.java", cpe0);
       s.assertPathIncluded("com/google/gwt/user/client/Timer.java", cpe1);
     }