Allow arbitrary Files to be injected into the ClientBundle source lookup mechanism.

Patch by: bobv
Review by: rjrjr

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6017 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java b/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java
index 0c92052..d2efb20 100644
--- a/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java
+++ b/user/src/com/google/gwt/resources/ext/ResourceGeneratorUtil.java
@@ -25,9 +25,12 @@
 import com.google.gwt.core.ext.typeinfo.JPackage;
 import com.google.gwt.dev.resource.Resource;
 import com.google.gwt.dev.resource.ResourceOracle;
+import com.google.gwt.dev.util.collect.Maps;
 import com.google.gwt.resources.client.ClientBundle.Source;
 
+import java.io.File;
 import java.lang.annotation.Annotation;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -52,6 +55,30 @@
   }
 
   /**
+   * A locator which will use files published via
+   * {@link ResourceGeneratorUtil#addNamedFile(String, File)}.
+   */
+  private static class FileLocator implements Locator {
+    public static final FileLocator INSTANCE = new FileLocator();
+
+    private FileLocator() {
+    }
+
+    public URL locate(String resourceName) {
+      File f = namedFiles.get(resourceName);
+      if (f != null && f.isFile() && f.canRead()) {
+        try {
+          return f.toURI().toURL();
+        } catch (MalformedURLException e) {
+          throw new RuntimeException("Unable to make a URL for file "
+              + f.getName());
+        }
+      }
+      return null;
+    }
+  }
+
+  /**
    * Wrapper interface around different strategies for loading resource data.
    */
   private interface Locator {
@@ -71,6 +98,8 @@
     }
   }
 
+  private static Map<String, File> namedFiles = Maps.create();
+
   /**
    * These are type names from previous APIs or from APIs with similar
    * functionality that might be confusing.
@@ -112,6 +141,25 @@
   }
 
   /**
+   * Publish or override resources named by {@link Source} annotations. This
+   * method is intended to be called by Generators that create ClientBundle
+   * instances and need to pass source data to the ClientBundle system that is
+   * not accessible through the classpath.
+   * 
+   * @param resourceName the path at which the contents of <code>file</code>
+   *          should be made available
+   * @param file the File whose contents are to be provided to the ClientBundle
+   *          system
+   */
+  public static void addNamedFile(String resourceName, File file) {
+    assert resourceName != null : "resourceName";
+    assert file != null : "file";
+    assert file.isFile() && file.canRead() : "file does not exist or cannot be read";
+
+    namedFiles = Maps.put(namedFiles, resourceName, file);
+  }
+
+  /**
    * Return the base filename of a resource. The behavior is similar to the unix
    * command <code>basename</code>.
    * 
@@ -233,14 +281,24 @@
   public static URL[] findResources(TreeLogger logger, ResourceContext context,
       JMethod method, String[] defaultSuffixes)
       throws UnableToCompleteException {
+    URL[] toReturn = null;
+    Locator locator;
 
-    // Try to find the resources with ResourceOracle
-    Locator locator = new ResourceOracleLocator(
-        context.getGeneratorContext().getResourcesOracle());
+    // If we have named files, attempt them first
+    if (!namedFiles.isEmpty()) {
+      toReturn = findResources(logger, FileLocator.INSTANCE, context, method,
+          defaultSuffixes, false);
+    }
 
-    // Don't report errors since we have a fallback mechanism
-    URL[] toReturn = findResources(logger, locator, context, method,
-        defaultSuffixes, false);
+    if (toReturn == null) {
+      // Try to find the resources with ResourceOracle
+      locator = new ResourceOracleLocator(
+          context.getGeneratorContext().getResourcesOracle());
+
+      // Don't report errors since we have a fallback mechanism
+      toReturn = findResources(logger, locator, context, method,
+          defaultSuffixes, false);
+    }
 
     if (toReturn == null) {
       // Since not all resources were found, try with ClassLoader