Binder.withSource() added. I'd still like to start using it within the exceptions, and then cleanup their static configuration of SourceProviders.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@524 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/Binder.java b/src/com/google/inject/Binder.java
index 9382b75..579e12a 100644
--- a/src/com/google/inject/Binder.java
+++ b/src/com/google/inject/Binder.java
@@ -289,4 +289,16 @@
    */
   void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
       TypeConverter converter);
+
+  /**
+   * Returns a binder that uses {@code source} as the reference location for
+   * errors in its configuration. This is typically a {@link StackTraceElement}
+   * for {@code .java} source but it could any binding source, such as the
+   * path to a {@code .properties} file.
+   *
+   * @param source any object representing the source location and has a
+   *     concise {@link Object#toString() toString()} value
+   * @return a binder that shares its configuration with this binder
+   */
+  Binder withSource(Object source);
 }
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index 841c2be..d08e66c 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -215,7 +215,7 @@
     }
   }
 
-  private static class BuiltInModule extends AbstractModule {
+  private static class BuiltInModule implements Module {
     final Injector injector;
     final Stage stage;
 
@@ -224,18 +224,15 @@
       this.stage = checkNotNull(stage, "stage");
     }
 
-    protected void configure() {
-      SourceProviders.withDefault(SourceProviders.UNKNOWN_SOURCE, new Runnable() {
-        public void run() {
-          bind(Stage.class).toInstance(stage);
-          bindScope(Singleton.class, SINGLETON);
-          // Create default bindings.
-          // We use toProvider() instead of toInstance() to avoid infinite recursion
-          // in toString().
-          bind(Injector.class).toProvider(new InjectorProvider(injector));
+    public void configure(Binder binder) {
+      binder = binder.withSource(SourceProviders.UNKNOWN_SOURCE);
 
-        }
-      });
+      binder.bind(Stage.class).toInstance(stage);
+      binder.bindScope(Singleton.class, SINGLETON);
+      // Create default bindings.
+      // We use toProvider() instead of toInstance() to avoid infinite recursion
+      // in toString().
+      binder.bind(Injector.class).toProvider(new InjectorProvider(injector));
     }
 
     class InjectorProvider implements Provider<Injector> {
diff --git a/src/com/google/inject/commands/CommandRecorder.java b/src/com/google/inject/commands/CommandRecorder.java
index c9c17f0..312428e 100644
--- a/src/com/google/inject/commands/CommandRecorder.java
+++ b/src/com/google/inject/commands/CommandRecorder.java
@@ -16,6 +16,7 @@
 
 package com.google.inject.commands;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.inject.Binder;
@@ -30,7 +31,6 @@
 import com.google.inject.matcher.Matcher;
 import com.google.inject.spi.Message;
 import com.google.inject.spi.SourceProviders;
-import static com.google.inject.spi.SourceProviders.defaultSource;
 import com.google.inject.spi.TypeConverter;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
@@ -89,23 +89,36 @@
   }
 
   private class RecordingBinder implements Binder {
-    private final Set<Module> modules = Sets.newHashSet();
-    private final List<Command> commands = Lists.newArrayList();
+    private final Set<Module> modules;
+    private final List<Command> commands;
+
+    private RecordingBinder() {
+      modules = Sets.newHashSet();
+      commands = Lists.newArrayList();
+    }
+
+    /**
+     * Creates a recording binder that's backed by the same configuration as
+     * {@code backingBinder}.
+     */
+    private RecordingBinder(RecordingBinder backingBinder) {
+      modules = backingBinder.modules;
+      commands = backingBinder.commands;
+    }
 
     public void bindInterceptor(
         Matcher<? super Class<?>> classMatcher,
         Matcher<? super Method> methodMatcher,
         MethodInterceptor... interceptors) {
-      commands.add(new BindInterceptorCommand(
-          defaultSource(), classMatcher, methodMatcher, interceptors));
+      commands.add(new BindInterceptorCommand(getSource(), classMatcher, methodMatcher, interceptors));
     }
 
     public void bindScope(Class<? extends Annotation> annotationType, Scope scope) {
-      commands.add(new BindScopeCommand(defaultSource(), annotationType, scope));
+      commands.add(new BindScopeCommand(getSource(), annotationType, scope));
     }
 
     public void requestStaticInjection(Class<?>... types) {
-      commands.add(new RequestStaticInjectionCommand(defaultSource(), types));
+      commands.add(new RequestStaticInjectionCommand(getSource(), types));
     }
 
     public void install(Module module) {
@@ -119,11 +132,11 @@
     }
 
     public void addError(String message, Object... arguments) {
-      commands.add(new AddMessageErrorCommand(defaultSource(), message, arguments));
+      commands.add(new AddMessageErrorCommand(getSource(), message, arguments));
     }
 
     public void addError(Throwable t) {
-      commands.add(new AddThrowableErrorCommand(defaultSource(), t));
+      commands.add(new AddThrowableErrorCommand(getSource(), t));
     }
 
     public void addError(Message message) {
@@ -131,7 +144,7 @@
     }
 
     public <T> BindCommand<T>.BindingBuilder bind(Key<T> key) {
-      BindCommand<T> bindCommand = new BindCommand<T>(defaultSource(), key);
+      BindCommand<T> bindCommand = new BindCommand<T>(getSource(), key);
       commands.add(bindCommand);
       return bindCommand.bindingBuilder(RecordingBinder.this);
     }
@@ -145,13 +158,13 @@
     }
 
     public AnnotatedConstantBindingBuilder bindConstant() {
-      BindConstantCommand bindConstantCommand = new BindConstantCommand(defaultSource());
+      BindConstantCommand bindConstantCommand = new BindConstantCommand(getSource());
       commands.add(bindConstantCommand);
       return bindConstantCommand.bindingBuilder(RecordingBinder.this);
     }
 
     public <T> Provider<T> getProvider(final Key<T> key) {
-      commands.add(new GetProviderCommand<T>(defaultSource(), key, earlyRequestsProvider));
+      commands.add(new GetProviderCommand<T>(getSource(), key, earlyRequestsProvider));
       return new Provider<T>() {
         public T get() {
           return earlyRequestsProvider.get(key);
@@ -169,7 +182,21 @@
 
     public void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
         TypeConverter converter) {
-      commands.add(new ConvertToTypesCommand(defaultSource(), typeMatcher, converter));
+      commands.add(new ConvertToTypesCommand(getSource(), typeMatcher, converter));
+    }
+
+    public Binder withSource(final Object source) {
+      checkNotNull(source, "source");
+
+      return new RecordingBinder(this) {
+        @Override protected Object getSource() {
+          return source;
+        }
+      };
+    }
+
+    protected Object getSource() {
+      return SourceProviders.defaultSource();
     }
 
     @Override public String toString() {
diff --git a/src/com/google/inject/commands/CommandReplayer.java b/src/com/google/inject/commands/CommandReplayer.java
index 96f3f7a..b40f793 100644
--- a/src/com/google/inject/commands/CommandReplayer.java
+++ b/src/com/google/inject/commands/CommandReplayer.java
@@ -16,6 +16,7 @@
 
 package com.google.inject.commands;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import com.google.inject.Binder;
 import com.google.inject.Key;
 import com.google.inject.Module;
@@ -23,11 +24,8 @@
 import com.google.inject.binder.ConstantBindingBuilder;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.binder.ScopedBindingBuilder;
-import com.google.inject.spi.SourceProviders;
-import static com.google.common.base.Preconditions.checkNotNull;
-import org.aopalliance.intercept.MethodInterceptor;
-
 import java.util.List;
+import org.aopalliance.intercept.MethodInterceptor;
 
 /**
  * Executes commands against a binder.
@@ -108,95 +106,65 @@
   }
 
   public void replayAddMessageError(final Binder binder, final AddMessageErrorCommand command) {
-    SourceProviders.withDefault(command.getSource(), new Runnable() {
-      public void run() {
-        binder.addError(command.getMessage(), command.getArguments().toArray());
-      }
-    });
+    binder.withSource(command.getSource())
+        .addError(command.getMessage(), command.getArguments().toArray());
   }
 
   public void replayAddError(final Binder binder, final AddThrowableErrorCommand command) {
-    SourceProviders.withDefault(command.getSource(), new Runnable() {
-      public void run() {
-        binder.addError(command.getThrowable());
-      }
-    });
+    binder.withSource(command.getSource()).addError(command.getThrowable());
   }
 
   public void replayBindInterceptor(final Binder binder, final BindInterceptorCommand command) {
-    SourceProviders.withDefault(command.getSource(), new Runnable() {
-      public void run() {
-        List<MethodInterceptor> interceptors = command.getInterceptors();
-        binder.bindInterceptor(command.getClassMatcher(), command.getMethodMatcher(),
-            interceptors.toArray(new MethodInterceptor[interceptors.size()]));
-      }
-    });
+    List<MethodInterceptor> interceptors = command.getInterceptors();
+    binder.withSource(command.getSource()).bindInterceptor(
+        command.getClassMatcher(), command.getMethodMatcher(),
+        interceptors.toArray(new MethodInterceptor[interceptors.size()]));
   }
 
   public void replayBindScope(final Binder binder, final BindScopeCommand command) {
-    SourceProviders.withDefault(command.getSource(), new Runnable() {
-      public void run() {
-        binder.bindScope(command.getAnnotationType(), command.getScope());
-      }
-    });
+    binder.withSource(command.getSource()).bindScope(
+        command.getAnnotationType(), command.getScope());
   }
 
   public void replayRequestStaticInjection(final Binder binder,
       final RequestStaticInjectionCommand command) {
-    SourceProviders.withDefault(command.getSource(), new Runnable() {
-      public void run() {
-        List<Class> types = command.getTypes();
-        binder.requestStaticInjection(types.toArray(new Class[types.size()]));
-      }
-    });
+    List<Class> types = command.getTypes();
+    binder.withSource(command.getSource())
+        .requestStaticInjection(types.toArray(new Class[types.size()]));
   }
 
   public void replayBindConstant(final Binder binder, final BindConstantCommand command) {
-    SourceProviders.withDefault(command.getSource(), new Runnable() {
-      public void run() {
-        AnnotatedConstantBindingBuilder constantBindingBuilder = binder.bindConstant();
+    AnnotatedConstantBindingBuilder constantBindingBuilder
+        = binder.withSource(command.getSource()).bindConstant();
 
-        Key<Object> key = command.getKey();
-        ConstantBindingBuilder builder = key.getAnnotation() != null
-            ? constantBindingBuilder.annotatedWith(key.getAnnotation())
-            : constantBindingBuilder.annotatedWith(key.getAnnotationType());
+    Key<Object> key = command.getKey();
+    ConstantBindingBuilder builder = key.getAnnotation() != null
+        ? constantBindingBuilder.annotatedWith(key.getAnnotation())
+        : constantBindingBuilder.annotatedWith(key.getAnnotationType());
 
-        command.getTarget().execute(builder);
-      }
-    });
+    command.getTarget().execute(builder);
   }
 
   public void replayConvertToTypes(final Binder binder, final ConvertToTypesCommand command) {
-    SourceProviders.withDefault(command.getSource(), new Runnable() {
-      public void run() {
-        binder.convertToTypes(command.getTypeMatcher(), command.getTypeConverter());
-      }
-    });
+    binder.withSource(command.getSource())
+        .convertToTypes(command.getTypeMatcher(), command.getTypeConverter());
   }
 
   public <T> void replayBind(final Binder binder, final BindCommand<T> command) {
-    SourceProviders.withDefault(command.getSource(), new Runnable() {
-      public void run() {
-        LinkedBindingBuilder<T> lbb = binder.bind(command.getKey());
+    LinkedBindingBuilder<T> lbb = binder.withSource(command.getSource()).bind(command.getKey());
 
-        BindTarget<T> bindTarget = command.getTarget();
-        ScopedBindingBuilder sbb = bindTarget != null
-            ? bindTarget.execute(lbb)
-            : lbb;
+    BindTarget<T> bindTarget = command.getTarget();
+    ScopedBindingBuilder sbb = bindTarget != null
+        ? bindTarget.execute(lbb)
+        : lbb;
 
-        BindScoping scoping = command.getScoping();
-        if (scoping != null) {
-          scoping.execute(sbb);
-        }
-      }
-    });
+    BindScoping scoping = command.getScoping();
+    if (scoping != null) {
+      scoping.execute(sbb);
+    }
   }
 
   public <T> void replayGetProvider(final Binder binder, final GetProviderCommand<T> command) {
-    SourceProviders.withDefault(command.getSource(), new Runnable() {
-      public void run() {
-        binder.getProvider(command.getKey());
-      }
-    });
+    binder.withSource(command.getSource()).getProvider(command.getKey());
   }
 }
diff --git a/src/com/google/inject/name/Names.java b/src/com/google/inject/name/Names.java
index f68117e..c3c1a1a 100644
--- a/src/com/google/inject/name/Names.java
+++ b/src/com/google/inject/name/Names.java
@@ -47,19 +47,13 @@
    * Creates a constant binding to {@code @Named(key)} for each entry in
    * {@code properties}.
    */
-  public static void bindProperties(final Binder binder,
-      final Map<String, String> properties) {
-    SourceProviders.withDefault(
-        SourceProviders.defaultSource(),
-        new Runnable() {
-          public void run() {
-            for (Map.Entry<String, String> entry : properties.entrySet()) {
-              String key = entry.getKey();
-              String value = entry.getValue();
-              binder.bind(Key.get(String.class, new NamedImpl(key))).toInstance(value);
-            }
-          }
-        });
+  public static void bindProperties(Binder binder, Map<String, String> properties) {
+    binder = binder.withSource(getSource());
+    for (Map.Entry<String, String> entry : properties.entrySet()) {
+      String key = entry.getKey();
+      String value = entry.getValue();
+      binder.bind(Key.get(String.class, new NamedImpl(key))).toInstance(value);
+    }
   }
 
   /**
@@ -67,19 +61,18 @@
    * method binds all properties including those inherited from 
    * {@link Properties#defaults defaults}.
    */
-  public static void bindProperties(final Binder binder,
-      final Properties properties) {
-    SourceProviders.withDefault(
-        SourceProviders.defaultSource(),
-        new Runnable() {
-          public void run() {
-            // use enumeration to include the default properties
-            for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements(); ) {
-              String propertyName = (String) e.nextElement();
-              String value = properties.getProperty(propertyName);
-              binder.bind(Key.get(String.class, new NamedImpl(propertyName))).toInstance(value);
-            }
-          }
-        });
+  public static void bindProperties(Binder binder, Properties properties) {
+    binder = binder.withSource(getSource());
+
+    // use enumeration to include the default properties
+    for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements(); ) {
+      String propertyName = (String) e.nextElement();
+      String value = properties.getProperty(propertyName);
+      binder.bind(Key.get(String.class, new NamedImpl(propertyName))).toInstance(value);
+    }
+  }
+
+  private static Object getSource() {
+    return SourceProviders.defaultSource();
   }
 }
diff --git a/src/com/google/inject/spi/SourceProviders.java b/src/com/google/inject/spi/SourceProviders.java
index 1f566c4..e57c8bf 100644
--- a/src/com/google/inject/spi/SourceProviders.java
+++ b/src/com/google/inject/spi/SourceProviders.java
@@ -82,38 +82,6 @@
     return localSourceProvider.get()[0].source();
   }
 
-  /**
-   * Sets the default source provider, runs the given command, and then
-   * restores the previous default source provider.
-   */
-  private static void withDefault(SourceProvider sourceProvider, Runnable r) {
-    // We use a holder so we perform only 1 thread local access instead of 3.
-    SourceProvider[] holder = localSourceProvider.get();
-    SourceProvider previous = holder[0];
-    try {
-      holder[0] = sourceProvider;
-      r.run();
-    } finally {
-      holder[0] = previous;
-    }
-  }
-
-  /**
-   * Sets the default source, runs the given command, and then
-   * restores the previous default source provider.
-   */
-  public static void withDefault(final Object source, Runnable r) {
-    withDefault(sourceProviderFor(source), r);
-  }
-
-  private static SourceProvider sourceProviderFor(final Object source) {
-    return new SourceProvider() {
-      public Object source() {
-        return source;
-      }
-    };
-  }
-
   static class StacktraceSourceProvider implements SourceProvider {
     public Object source() {
       // Search up the stack until we find a class outside of this one.