Eliminated static from SourceProviders. The new mechanism to specify the source of a binding is like this:
  Binder myBinder = binder.withSource(source);



git-svn-id: https://google-guice.googlecode.com/svn/trunk@525 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
index 86a76f4..0ca95d9 100755
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
@@ -16,16 +16,21 @@
 
 package com.google.inject.assistedinject;
 
-import com.google.inject.*;
+import com.google.inject.AbstractModule;
 import static com.google.inject.Asserts.assertContains;
+import com.google.inject.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
 import com.google.inject.name.Named;
 import com.google.inject.name.Names;
-import junit.framework.TestCase;
-
-import java.awt.*;
+import java.awt.Color;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Set;
+import junit.framework.TestCase;
 
 /**
  * @author jmourits@google.com (Jerome Mourits)
@@ -508,8 +513,8 @@
         }
       });
       fail();
-    } catch (ProvisionException expected) {
-      assertContains(expected.getCause().getMessage(),
+    } catch (CreationException expected) {
+      assertContains(expected.getMessage(),
           "Parameter of type 'double' is not injectable or annotated with @Assisted");
     }
   }
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java b/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
index 48f6e03..7be7f28 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
@@ -16,13 +16,18 @@
 
 package com.google.inject.multibindings;
 
-import com.google.inject.*;
-import com.google.inject.util.Types;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import com.google.inject.Binder;
+import com.google.inject.Inject;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.multibindings.Multibinder.RealMultibinder;
-import com.google.inject.spi.SourceProviders;
-import static com.google.common.base.Preconditions.checkNotNull;
-
+import com.google.inject.spi.SourceProvider;
+import com.google.inject.util.Types;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.util.Collections;
@@ -88,9 +93,9 @@
  */
 public abstract class MapBinder<K, V> {
   private MapBinder() {}
-  static {
-    SourceProviders.skip(RealMapBinder.class);
-  }
+
+  private static final SourceProvider sourceProvider
+      = new SourceProvider(MapBinder.class, RealMapBinder.class);
 
   /**
    * Returns a new mapbinder that collects entries of {@code keyType}/{@code 
@@ -153,7 +158,7 @@
       Multibinder<Entry<K, Provider<V>>> entrySetBinder) {
     RealMapBinder<K, V> mapBinder = new RealMapBinder<K, V>(binder, 
         valueType, mapKey, providerMapKey, entrySetBinder);
-    binder.install(mapBinder);
+    binder.withSource(sourceProvider.get()).install(mapBinder);
     return mapBinder;
   }
 
@@ -199,7 +204,7 @@
     private final Key<Map<K, V>> mapKey;
     private final Key<Map<K, Provider<V>>> providerMapKey;
     private final RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder;
-    
+
     /* the target injector's binder. non-null until initialization, null afterwards */
     private Binder binder;
 
@@ -219,59 +224,52 @@
      */
     @Override public LinkedBindingBuilder<V> addBinding(K key) {
       checkNotNull(key, "key");
-      if (isInitialized()) {
-        throw new IllegalStateException("MapBinder was already initialized");
-      }
+      checkState(!isInitialized(), "MapBinder was already initialized");
+      Object source = sourceProvider.get();
 
       @SuppressWarnings("unchecked")
       Key<V> valueKey = (Key<V>) Key.get(valueType, new RealElement(entrySetBinder.getSetName()));
-      entrySetBinder.addBinding()
-          .toInstance(new MapEntry<K, Provider<V>>(key, binder.getProvider(valueKey)));
-      return binder.bind(valueKey);
+      entrySetBinder.addBinding().toInstance(new MapEntry<K, Provider<V>>(key,
+          binder.withSource(source).getProvider(valueKey)));
+      return binder.withSource(source).bind(valueKey);
     }
 
     public void configure(Binder binder) {
-      if (isInitialized()) {
-        throw new IllegalStateException("MapBinder was already initialized");
-      }
+      checkState(!isInitialized(), "MapBinder was already initialized");
 
       // binds a Map<K, Provider<V>> from a collection of Map<Entry<K, Provider<V>>
-      final Provider<Set<Entry<K,Provider<V>>>> entrySetProvider
-          = binder.getProvider(entrySetBinder.getSetKey());
+      final Provider<Set<Entry<K, Provider<V>>>> entrySetProvider = binder
+          .getProvider(entrySetBinder.getSetKey());
       binder.bind(providerMapKey).toProvider(new Provider<Map<K, Provider<V>>>() {
         private Map<K, Provider<V>> providerMap;
-    
+
         @SuppressWarnings("unused")
         @Inject void initialize() {
           RealMapBinder.this.binder = null;
-          
+
           Map<K, Provider<V>> providerMapMutable = new LinkedHashMap<K, Provider<V>>();
-          for (Map.Entry<K, Provider<V>> entry : entrySetProvider.get()) {
-            if (providerMapMutable.put(entry.getKey(), entry.getValue()) != null) {
-              throw new IllegalStateException("Map injection failed due to duplicated key \""
-                  + entry.getKey() + "\"");
-            }
+          for (Entry<K, Provider<V>> entry : entrySetProvider.get()) {
+            checkState(providerMapMutable.put(entry.getKey(), entry.getValue()) == null,
+                "Map injection failed due to duplicated key \"%s\"", entry.getKey());
           }
-    
+
           providerMap = Collections.unmodifiableMap(providerMapMutable);
         }
-    
+
         public Map<K, Provider<V>> get() {
           return providerMap;
         }
-       });
+      });
 
       final Provider<Map<K, Provider<V>>> mapProvider = binder.getProvider(providerMapKey);
       binder.bind(mapKey).toProvider(new Provider<Map<K, V>>() {
         public Map<K, V> get() {
           Map<K, V> map = new LinkedHashMap<K, V>();
-          for (Map.Entry<K, Provider<V>> entry : mapProvider.get().entrySet()) {
+          for (Entry<K, Provider<V>> entry : mapProvider.get().entrySet()) {
             V value = entry.getValue().get();
             K key = entry.getKey();
-            if (value == null) {
-              throw new IllegalStateException("Map injection failed due to null value for key \"" 
-                  + key + "\"");
-            }
+            checkState(value != null,
+                "Map injection failed due to null value for key \"%s\"", key);
             map.put(key, value);
           }
           return Collections.unmodifiableMap(map);
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java b/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
index d85d178..9068246 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
@@ -16,15 +16,27 @@
 
 package com.google.inject.multibindings;
 
-import com.google.inject.*;
-import com.google.inject.util.Types;
-import com.google.inject.binder.LinkedBindingBuilder;
-import com.google.inject.spi.SourceProviders;
 import static com.google.common.base.Preconditions.checkNotNull;
-
+import static com.google.common.base.Preconditions.checkState;
+import com.google.inject.Binder;
+import com.google.inject.Binding;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.binder.LinkedBindingBuilder;
+import com.google.inject.spi.SourceProvider;
+import com.google.inject.util.Types;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * An API to bind multiple values separately, only to later inject them as a
@@ -72,9 +84,9 @@
  */
 public abstract class Multibinder<T> {
   private Multibinder() {}
-  static {
-    SourceProviders.skip(RealMultibinder.class);
-  }
+
+  private static final SourceProvider sourceProvider
+      = new SourceProvider(RealMultibinder.class, Multibinder.class);
 
   /**
    * Returns a new multibinder that collects instances of {@code type} in a
@@ -83,7 +95,7 @@
   public static <T> Multibinder<T> newSetBinder(Binder binder, Type type) {
     RealMultibinder<T> result = new RealMultibinder<T>(binder, type, "",
         Key.get(Multibinder.<T>setOf(type)));
-    binder.install(result);
+    binder.withSource(sourceProvider.get()).install(result);
     return result;
   }
 
@@ -94,7 +106,7 @@
   public static <T> Multibinder<T> newSetBinder(Binder binder, Type type, Annotation annotation) {
     RealMultibinder<T> result = new RealMultibinder<T>(binder, type, annotation.toString(),
         Key.get(Multibinder.<T>setOf(type), annotation));
-    binder.install(result);
+    binder.withSource(sourceProvider.get()).install(result);
     return result;
   }
 
@@ -106,7 +118,7 @@
       Class<? extends Annotation> annotationType) {
     RealMultibinder<T> result = new RealMultibinder<T>(binder, type, "@" + annotationType.getName(),
         Key.get(Multibinder.<T>setOf(type), annotationType));
-    binder.install(result);
+    binder.withSource(sourceProvider.get()).install(result);
     return result;
   }
 
@@ -172,20 +184,17 @@
 
     @SuppressWarnings("unchecked")
     public void configure(Binder binder) {
-      if (isInitialized()) {
-        throw new IllegalStateException("Multibinder was already initialized");
-      }
+      checkState(!isInitialized(), "Multibinder was already initialized");
 
       binder.bind(setKey).toProvider(this);
     }
 
     @SuppressWarnings("unchecked")
     @Override public LinkedBindingBuilder<T> addBinding() {
-      if (isInitialized()) {
-        throw new IllegalStateException("Multibinder was already initialized");
-      }
+      checkState(!isInitialized(), "Multibinder was already initialized");
 
-      return binder.bind((Key<T>) Key.get(elementType, new RealElement(setName)));
+      return binder.withSource(sourceProvider.get())
+          .bind((Key<T>) Key.get(elementType, new RealElement(setName)));
     }
 
     /**
@@ -217,20 +226,14 @@
     }
 
     public Set<T> get() {
-      if (!isInitialized()) {
-        throw new IllegalStateException("Multibinder is not initialized");
-      }
+      checkState(isInitialized(), "Multibinder is not initialized");
 
       Set<T> result = new LinkedHashSet<T>();
       for (Provider<T> provider : providers) {
         final T newValue = provider.get();
-        if (newValue == null) {
-          throw new IllegalStateException("Set injection failed due to null element");
-        }
-        if (!result.add(newValue)) {
-          throw new IllegalStateException("Set injection failed due to duplicated element \""
-              + newValue + "\"");
-        }
+        checkState(newValue != null, "Set injection failed due to null element");
+        checkState(result.add(newValue),
+            "Set injection failed due to duplicated element \"%s\"", newValue);
       }
       return Collections.unmodifiableSet(result);
     }
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java b/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java
index 6f4ae79..f9a818b 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java
@@ -16,18 +16,26 @@
 
 package com.google.inject.multibindings;
 
-import com.google.inject.*;
+import com.google.inject.AbstractModule;
 import static com.google.inject.Asserts.assertContains;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.TypeLiteral;
 import com.google.inject.name.Names;
 import static com.google.inject.name.Names.named;
 import com.google.inject.util.Providers;
-import junit.framework.TestCase;
-
 import java.lang.annotation.Retention;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import junit.framework.TestCase;
 
 /**
  * @author dpb@google.com (David P. Baker)
@@ -212,9 +220,9 @@
         }
       });
       fail();
-    } catch(ProvisionException expected) {
-      assertEquals("Map injection failed due to duplicated key \"a\"",
-          expected.getCause().getMessage());
+    } catch(CreationException expected) {
+      assertContains(expected.getMessage(),
+          "Map injection failed due to duplicated key \"a\"");
     }
   }
 
@@ -276,8 +284,8 @@
       });
       fail();
     } catch (CreationException expected) {
-      assertContains(expected.getMessage(), "Error at " + getClass().getName());
-      assertContains(expected.getMessage(), "No implementation was specified.");
+      assertContains(expected.getMessage(), "Error at " + getClass().getName(),
+          "No implementation for java.lang.Integer");
     }
   }
 
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java b/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
index 2ff4400..f80333e 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
@@ -16,19 +16,27 @@
 
 package com.google.inject.multibindings;
 
-import com.google.inject.*;
+import com.google.common.collect.Sets;
+import com.google.inject.AbstractModule;
 import static com.google.inject.Asserts.assertContains;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.TypeLiteral;
 import com.google.inject.name.Names;
 import static com.google.inject.name.Names.named;
 import com.google.inject.util.Providers;
-import junit.framework.TestCase;
-
 import java.lang.annotation.Retention;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Set;
+import junit.framework.TestCase;
 
 /**
  * @author jessewilson@google.com (Jesse Wilson)
@@ -243,7 +251,7 @@
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(), "Error at " + getClass().getName());
-      assertContains(expected.getMessage(), "No implementation was specified.");
+      assertContains(expected.getMessage(), "No implementation for java.lang.Integer");
     }
   }
 
@@ -254,7 +262,7 @@
   @interface De {}
 
   private <T> Set<T> setOf(T... elements) {
-    Set<T> result = new HashSet<T>();
+    Set<T> result = Sets.newHashSet();
     result.addAll(Arrays.asList(elements));
     return result;
   }
diff --git a/spring/src/com/google/inject/spring/SpringIntegration.java b/spring/src/com/google/inject/spring/SpringIntegration.java
index a74ccee..2151c60 100644
--- a/spring/src/com/google/inject/spring/SpringIntegration.java
+++ b/spring/src/com/google/inject/spring/SpringIntegration.java
@@ -16,12 +16,12 @@
 
 package com.google.inject.spring;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import com.google.inject.Binder;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.name.Names;
-import com.google.inject.spi.SourceProviders;
-import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.inject.spi.SourceProvider;
 import org.springframework.beans.factory.BeanFactory;
 import org.springframework.beans.factory.ListableBeanFactory;
 
@@ -31,13 +31,12 @@
  * @author crazybob@google.com (Bob Lee)
  */
 public class SpringIntegration {
-
-  static {
-    SourceProviders.skip(SpringIntegration.class);
-  }
-
   private SpringIntegration() {}
 
+
+  private static final SourceProvider sourceProvider
+      = new SourceProvider(SpringIntegration.class);
+
   /**
    * Creates a provider which looks up objects from Spring using the given name.
    * Expects a binding to {@link
@@ -61,6 +60,8 @@
    * @see com.google.inject.name.Names#named(String) 
    */
   public static void bindAll(Binder binder, ListableBeanFactory beanFactory) {
+    binder = binder.withSource(sourceProvider.get());
+
     for (String name : beanFactory.getBeanDefinitionNames()) {
       Class<?> type = beanFactory.getType(name);
       bindBean(binder, beanFactory, name, type);
diff --git a/src/com/google/inject/AbstractModule.java b/src/com/google/inject/AbstractModule.java
index 87c6491..92cd0ba 100644
--- a/src/com/google/inject/AbstractModule.java
+++ b/src/com/google/inject/AbstractModule.java
@@ -23,7 +23,6 @@
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.matcher.Matcher;
 import com.google.inject.spi.Message;
-import com.google.inject.spi.SourceProviders;
 import com.google.inject.spi.TypeConverter;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
@@ -52,10 +51,6 @@
  */
 public abstract class AbstractModule implements Module {
 
-  static {
-    SourceProviders.skip(AbstractModule.class);
-  }
-
   Binder binder;
 
   public final synchronized void configure(Binder builder) {
diff --git a/src/com/google/inject/ConvertToTypesCommandProcessor.java b/src/com/google/inject/ConvertToTypesCommandProcessor.java
index 45e26a8..d5f5172 100644
--- a/src/com/google/inject/ConvertToTypesCommandProcessor.java
+++ b/src/com/google/inject/ConvertToTypesCommandProcessor.java
@@ -24,7 +24,7 @@
 import com.google.inject.matcher.AbstractMatcher;
 import com.google.inject.matcher.Matcher;
 import com.google.inject.matcher.Matchers;
-import com.google.inject.spi.SourceProviders;
+import com.google.inject.spi.SourceProvider;
 import com.google.inject.spi.TypeConverter;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -162,7 +162,7 @@
 
   private void internalConvertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
       TypeConverter converter) {
-    converters.add(new MatcherAndConverter(typeMatcher, converter, SourceProviders.UNKNOWN_SOURCE));
+    converters.add(new MatcherAndConverter(typeMatcher, converter, SourceProvider.UNKNOWN_SOURCE));
   }
 
   @Override public Boolean visitConvertToTypes(ConvertToTypesCommand command) {
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index d08e66c..74be70f 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -28,7 +28,7 @@
 import com.google.inject.internal.ErrorsException;
 import com.google.inject.internal.Stopwatch;
 import com.google.inject.spi.InjectionPoint;
-import com.google.inject.spi.SourceProviders;
+import com.google.inject.spi.SourceProvider;
 import java.lang.reflect.Member;
 import java.util.List;
 import java.util.logging.Logger;
@@ -171,17 +171,20 @@
     errors.throwCreationExceptionIfErrorsExist();
   }
 
-  /** Inject everything that can be injected. This uses runtime error handling. */
+  /** Inject everything that can be injected. */
   private void fulfillInjectionRequests() {
     futureInjector.initialize(injector);
 
     requestStaticInjectionCommandProcessor.injectMembers(injector);
     stopwatch.resetAndLog("Static member injection");
+
     injector.fulfillOutstandingInjections(errors);
     stopwatch.resetAndLog("Instance injection");
+    errors.throwCreationExceptionIfErrorsExist();
 
     loadEagerSingletons();
     stopwatch.resetAndLog("Preloading");
+    errors.throwCreationExceptionIfErrorsExist();
   }
 
   public void loadEagerSingletons() {
@@ -225,7 +228,7 @@
     }
 
     public void configure(Binder binder) {
-      binder = binder.withSource(SourceProviders.UNKNOWN_SOURCE);
+      binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
 
       binder.bind(Stage.class).toInstance(stage);
       binder.bindScope(Singleton.class, SINGLETON);
@@ -261,7 +264,7 @@
     LoggerFactory loggerFactory = new LoggerFactory();
     injector.explicitBindings.put(key,
         new ProviderInstanceBindingImpl<Logger>(injector, key,
-            SourceProviders.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE,
+            SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE,
             loggerFactory, LoadStrategy.LAZY));
   }
 
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index d6966ba..6f6eb1e 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -37,7 +37,7 @@
 import com.google.inject.spi.ConvertedConstantBinding;
 import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.ProviderBinding;
-import com.google.inject.spi.SourceProviders;
+import com.google.inject.spi.SourceProvider;
 import com.google.inject.util.Providers;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
@@ -294,7 +294,7 @@
       super(
           injector,
           key,
-          SourceProviders.UNKNOWN_SOURCE,
+          SourceProvider.UNKNOWN_SOURCE,
           createInternalFactory(providedBinding),
           Scopes.NO_SCOPE,
           loadStrategy);
@@ -389,7 +389,7 @@
 
     ConvertedConstantBindingImpl(
         InjectorImpl injector, Key<T> key, T value, Binding<String> originalBinding) {
-      super(injector, key, SourceProviders.UNKNOWN_SOURCE, new ConstantFactory<T>(value),
+      super(injector, key, SourceProvider.UNKNOWN_SOURCE, new ConstantFactory<T>(value),
           Scopes.NO_SCOPE, LoadStrategy.LAZY);
       this.value = value;
       provider = Providers.of(value);
@@ -424,7 +424,7 @@
   <T> BindingImpl<T> createBindingFromType(
       Class<T> type, LoadStrategy loadStrategy, Errors errors) throws ErrorsException {
     BindingImpl<T> binding = createUnitializedBinding(
-        type, null, SourceProviders.defaultSource(), loadStrategy, errors);
+        type, null, StackTraceElements.forType(type), loadStrategy, errors);
     initializeBinding(binding, errors);
     return binding;
   }
diff --git a/src/com/google/inject/InternalFactoryToProviderAdapter.java b/src/com/google/inject/InternalFactoryToProviderAdapter.java
index c7cd465..b1424f8 100644
--- a/src/com/google/inject/InternalFactoryToProviderAdapter.java
+++ b/src/com/google/inject/InternalFactoryToProviderAdapter.java
@@ -20,7 +20,7 @@
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
 import com.google.inject.spi.InjectionPoint;
-import com.google.inject.spi.SourceProviders;
+import com.google.inject.spi.SourceProvider;
 
 /**
  * @author crazybob@google.com (Bob Lee)
@@ -31,7 +31,7 @@
   private final Object source;
 
   public InternalFactoryToProviderAdapter(Provider<? extends T> provider) {
-    this(provider, SourceProviders.UNKNOWN_SOURCE);
+    this(provider, SourceProvider.UNKNOWN_SOURCE);
   }
 
   public InternalFactoryToProviderAdapter(
diff --git a/src/com/google/inject/commands/BindCommand.java b/src/com/google/inject/commands/BindCommand.java
index db6cd56..996841b 100644
--- a/src/com/google/inject/commands/BindCommand.java
+++ b/src/com/google/inject/commands/BindCommand.java
@@ -26,7 +26,7 @@
 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 com.google.inject.spi.SourceProvider;
 import java.lang.annotation.Annotation;
 
 /**
@@ -36,9 +36,8 @@
  */
 public final class BindCommand<T> implements Command {
 
-  static {
-    SourceProviders.skip(BindCommand.BindingBuilder.class);
-  }
+  private static final SourceProvider sourceProvider = new SourceProvider(
+      BindCommand.BindingBuilder.class);
 
   private static final BindTarget<Object> EMPTY_BIND_TARGET = new AbstractTarget<Object>() {
     public ScopedBindingBuilder execute(LinkedBindingBuilder<Object> linkedBindingBuilder) {
@@ -317,25 +316,25 @@
 
     private void checkNotTargetted() {
       if (bindTarget != EMPTY_BIND_TARGET) {
-        binder.addError(IMPLEMENTATION_ALREADY_SET);
+        binder.withSource(sourceProvider.get()).addError(IMPLEMENTATION_ALREADY_SET);
       }
     }
 
     private void checkNotAnnotated() {
       if (BindCommand.this.key.getAnnotationType() != null) {
-        binder.addError(ANNOTATION_ALREADY_SPECIFIED);
+        binder.withSource(sourceProvider.get()).addError(ANNOTATION_ALREADY_SPECIFIED);
       }
     }
 
     private void checkNotScoped() {
       // Scoping isn't allowed when we have only one instance.
       if (bindTarget.get() != null) {
-        binder.addError(SINGLE_INSTANCE_AND_SCOPE);
+        binder.withSource(sourceProvider.get()).addError(SINGLE_INSTANCE_AND_SCOPE);
         return;
       }
 
       if (bindScoping != EMPTY_SCOPING) {
-        binder.addError(SCOPE_ALREADY_SET);
+        binder.withSource(sourceProvider.get()).addError(SCOPE_ALREADY_SET);
       }
     }
 
diff --git a/src/com/google/inject/commands/BindConstantCommand.java b/src/com/google/inject/commands/BindConstantCommand.java
index 7e28458..d51e349 100644
--- a/src/com/google/inject/commands/BindConstantCommand.java
+++ b/src/com/google/inject/commands/BindConstantCommand.java
@@ -16,6 +16,8 @@
 
 package com.google.inject.commands;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.inject.AbstractModule;
 import com.google.inject.Binder;
 import com.google.inject.Key;
 import com.google.inject.Provider;
@@ -23,9 +25,7 @@
 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 com.google.inject.spi.SourceProvider;
 import java.lang.annotation.Annotation;
 
 /**
@@ -34,9 +34,8 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class BindConstantCommand implements Command {
-  static {
-    SourceProviders.skip(BindingBuilder.class);
-  }
+  private static final SourceProvider sourceProvider = new SourceProvider(
+      BindingBuilder.class, AbstractModule.class);
 
   private final Object source;
   private BindingAnnotation bindingAnnotation;
@@ -348,13 +347,13 @@
 
     private void assertNoBindingAnnotation() {
       if (bindingAnnotation != null) {
-        binder.addError(ANNOTATION_ALREADY_SPECIFIED);
+        binder.withSource(sourceProvider.get()).addError(ANNOTATION_ALREADY_SPECIFIED);
       }
     }
 
     private void assertNoTarget() {
       if (target != null) {
-        binder.addError(CONSTANT_VALUE_ALREADY_SET);
+        binder.withSource(sourceProvider.get()).addError(CONSTANT_VALUE_ALREADY_SET);
       }
     }
 
diff --git a/src/com/google/inject/commands/CommandRecorder.java b/src/com/google/inject/commands/CommandRecorder.java
index 312428e..d91b01a 100644
--- a/src/com/google/inject/commands/CommandRecorder.java
+++ b/src/com/google/inject/commands/CommandRecorder.java
@@ -19,6 +19,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.google.inject.AbstractModule;
 import com.google.inject.Binder;
 import com.google.inject.Key;
 import com.google.inject.Module;
@@ -30,7 +31,7 @@
 import com.google.inject.binder.AnnotatedConstantBindingBuilder;
 import com.google.inject.matcher.Matcher;
 import com.google.inject.spi.Message;
-import com.google.inject.spi.SourceProviders;
+import com.google.inject.spi.SourceProvider;
 import com.google.inject.spi.TypeConverter;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
@@ -47,13 +48,12 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class CommandRecorder {
+  private static final SourceProvider sourceProvider = new SourceProvider(
+      RecordingBinder.class, AbstractModule.class);
+
   private Stage currentStage = Stage.DEVELOPMENT;
   private final EarlyRequestsProvider earlyRequestsProvider;
 
-  static {
-    SourceProviders.skip(RecordingBinder.class);
-  }
-
   /**
    * @param earlyRequestsProvider satisfies requests to
    *     {@link Binder#getProvider} at module execution time. For modules that
@@ -196,7 +196,7 @@
     }
 
     protected Object getSource() {
-      return SourceProviders.defaultSource();
+      return sourceProvider.get();
     }
 
     @Override public String toString() {
diff --git a/src/com/google/inject/commands/FutureInjector.java b/src/com/google/inject/commands/FutureInjector.java
index ce1b4fa..30f972c 100644
--- a/src/com/google/inject/commands/FutureInjector.java
+++ b/src/com/google/inject/commands/FutureInjector.java
@@ -16,10 +16,10 @@
 
 package com.google.inject.commands;
 
+import static com.google.common.base.Preconditions.checkState;
 import com.google.inject.Injector;
 import com.google.inject.Key;
 
-
 /**
  * Satisfies binding requests using an eventually-created Injector. To use:
  *
@@ -43,18 +43,14 @@
   private Injector injector;
 
   public void initialize(Injector injector) {
-    if (this.injector != null) {
-      throw new IllegalStateException("Already initialized");
-    }
+    checkState(this.injector == null, "Already initialized");
 
     this.injector = injector;
   }
 
   public <T> T get(Key<T> key) {
-    if (injector == null) {
-      throw new IllegalStateException("This provider cannot be used until the"
-          + " Injector has been created.");
-    }
+    checkState(injector != null,
+        "This provider cannot be used until the Injector has been created.");
 
     return injector.getInstance(key);
   }
diff --git a/src/com/google/inject/internal/Errors.java b/src/com/google/inject/internal/Errors.java
index 5e82f4a..0ee4702 100644
--- a/src/com/google/inject/internal/Errors.java
+++ b/src/com/google/inject/internal/Errors.java
@@ -28,7 +28,7 @@
 import com.google.inject.TypeLiteral;
 import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.Message;
-import com.google.inject.spi.SourceProviders;
+import com.google.inject.spi.SourceProvider;
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
@@ -375,7 +375,7 @@
     } else if (!sources.isEmpty()) {
       source = sources.get(sources.size() - 1);
     } else {
-      source = SourceProviders.UNKNOWN_SOURCE;
+      source = SourceProvider.UNKNOWN_SOURCE;
     }
 
     errors.add(new Message(source, message, ImmutableList.copyOf(injectionPoints), cause));
diff --git a/src/com/google/inject/internal/StackTraceElements.java b/src/com/google/inject/internal/StackTraceElements.java
index 56c373e..fcabe40 100644
--- a/src/com/google/inject/internal/StackTraceElements.java
+++ b/src/com/google/inject/internal/StackTraceElements.java
@@ -18,7 +18,7 @@
 
 import static com.google.inject.internal.ReferenceType.SOFT;
 import static com.google.inject.internal.ReferenceType.WEAK;
-import com.google.inject.spi.SourceProviders;
+import com.google.inject.spi.SourceProvider;
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Member;
@@ -45,7 +45,7 @@
 
   public static Object forMember(Member member) {
     if (member == null) {
-      return SourceProviders.UNKNOWN_SOURCE;
+      return SourceProvider.UNKNOWN_SOURCE;
     }
 
     Class declaringClass = member.getDeclaringClass();
diff --git a/src/com/google/inject/name/Names.java b/src/com/google/inject/name/Names.java
index c3c1a1a..53bc7d5 100644
--- a/src/com/google/inject/name/Names.java
+++ b/src/com/google/inject/name/Names.java
@@ -16,9 +16,10 @@
 
 package com.google.inject.name;
 
+import com.google.inject.AbstractModule;
 import com.google.inject.Binder;
 import com.google.inject.Key;
-import com.google.inject.spi.SourceProviders;
+import com.google.inject.spi.SourceProvider;
 import java.util.Enumeration;
 import java.util.Map;
 import java.util.Properties;
@@ -30,11 +31,10 @@
  */
 public class Names {
 
-  private Names() {}
+  private static final SourceProvider sourceProvider = new SourceProvider(
+      Names.class, AbstractModule.class);
 
-  static {
-    SourceProviders.skip(Names.class);
-  }
+  private Names() {}
 
   /**
    * Creates a {@link Named} annotation with {@code name} as the value.
@@ -48,7 +48,7 @@
    * {@code properties}.
    */
   public static void bindProperties(Binder binder, Map<String, String> properties) {
-    binder = binder.withSource(getSource());
+    binder = binder.withSource(sourceProvider.get());
     for (Map.Entry<String, String> entry : properties.entrySet()) {
       String key = entry.getKey();
       String value = entry.getValue();
@@ -62,7 +62,7 @@
    * {@link Properties#defaults defaults}.
    */
   public static void bindProperties(Binder binder, Properties properties) {
-    binder = binder.withSource(getSource());
+    binder = binder.withSource(sourceProvider.get());
 
     // use enumeration to include the default properties
     for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements(); ) {
@@ -71,8 +71,4 @@
       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/Message.java b/src/com/google/inject/spi/Message.java
index 97e4222..5b5a53c 100644
--- a/src/com/google/inject/spi/Message.java
+++ b/src/com/google/inject/spi/Message.java
@@ -47,7 +47,7 @@
   }
 
   public Message(String message) {
-    this(SourceProviders.UNKNOWN_SOURCE, message, ImmutableList.<InjectionPoint>of(), null);
+    this(SourceProvider.UNKNOWN_SOURCE, message, ImmutableList.<InjectionPoint>of(), null);
   }
 
   /**
diff --git a/src/com/google/inject/spi/SourceProvider.java b/src/com/google/inject/spi/SourceProvider.java
index 127f5ba..062a816 100644
--- a/src/com/google/inject/spi/SourceProvider.java
+++ b/src/com/google/inject/spi/SourceProvider.java
@@ -16,20 +16,37 @@
 
 package com.google.inject.spi;
 
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+
 /**
- * Provides source objects to the {@link com.google.inject.Binder}.
- * A source object is any object which points back to the current location
- * within the configuration. Guice uses source objects in error messages
- * and associates them with bindings.
- *
+ * Provides access to the calling line of code.
+ * 
  * @author crazybob@google.com (Bob Lee)
  */
-public interface SourceProvider {
+public class SourceProvider {
 
-  /**
-   * Creates an object pointing to the current location within the
-   * configuration. If we run into a problem later, we'll be able to trace it
-   * back to the original source. Useful for debugging.
-   */
-  Object source();
+  /** Indicates that the source is unknown. */
+  public static final Object UNKNOWN_SOURCE = "[unknown source]";
+
+  private final Set<String> classNamesToSkip;
+
+  public SourceProvider(Class... classesToSkip) {
+    String[] classNamesToSkip = new String[classesToSkip.length + 1];
+    for (int i = 0; i < classesToSkip.length; i++) {
+      classNamesToSkip[i] = classesToSkip[i].getName();
+    }
+    classNamesToSkip[classesToSkip.length] = SourceProvider.class.getName();
+    this.classNamesToSkip = ImmutableSet.of(classNamesToSkip);
+  }
+
+  public Object get() {
+    for (final StackTraceElement element : new Throwable().getStackTrace()) {
+      String className = element.getClassName();
+      if (!classNamesToSkip.contains(className)) {
+        return element;
+      }
+    }
+    throw new AssertionError();
+  }
 }
diff --git a/src/com/google/inject/spi/SourceProviders.java b/src/com/google/inject/spi/SourceProviders.java
deleted file mode 100644
index e57c8bf..0000000
--- a/src/com/google/inject/spi/SourceProviders.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * Copyright (C) 2006 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.inject.spi;
-
-import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * Provides access to the default {@link SourceProvider} implementation and
- * common controls for certain implementations.
- * 
- * @author crazybob@google.com (Bob Lee)
- */
-public class SourceProviders {
-
-  private SourceProviders() {}
-
-  /**
-   * Indicates that the source is unknown.
-   */
-  public static final Object UNKNOWN_SOURCE = "[unknown source]";
-  
-  static final SourceProvider DEFAULT_INSTANCE = new StacktraceSourceProvider();
-
-  static Set<String> skippedClassNames = Collections.emptySet();
-
-  static {
-    skip(SourceProviders.class);
-    skip(StacktraceSourceProvider.class);
-  }
-
-  /**
-   * Instructs stacktrace-based providers to skip the given class in the stack
-   * trace when determining the source. Use this to keep the binder from
-   * logging utility methods as the sources of bindings (i.e. it will skip to
-   * the utility methods' callers instead).
-   *
-   * <p>Skipping only takes place after this method is called.
-   */
-  public synchronized static void skip(Class<?> clazz) {
-    // Poor man's copy-on-write hash set.
-    Set<String> copy = Sets.newHashSet();
-    copy.addAll(skippedClassNames);
-    copy.add(clazz.getName());
-    skippedClassNames = Collections.unmodifiableSet(copy);
-  }
-
-  /**
-   * Gets the set of class names which should be skipped by stacktrace-based
-   * providers.
-   */
-  public synchronized static Set<String> getSkippedClassNames() {
-    return skippedClassNames;
-  }
-
-  static ThreadLocal<SourceProvider[]> localSourceProvider =
-      new ThreadLocal<SourceProvider[]>() {
-    protected SourceProvider[] initialValue() {
-      return new SourceProvider[] { DEFAULT_INSTANCE };
-    }
-  };
-
-  /**
-   * Returns the current source obtained from the default provider.
-   */
-  public static Object defaultSource() {
-    return localSourceProvider.get()[0].source();
-  }
-
-  static class StacktraceSourceProvider implements SourceProvider {
-    public Object source() {
-      // Search up the stack until we find a class outside of this one.
-      Set<String> skippedClassNames = getSkippedClassNames();
-      for (final StackTraceElement element : new Throwable().getStackTrace()) {
-        String className = element.getClassName();
-        if (!skippedClassNames.contains(className)) {
-          return element;
-        }
-      }
-      throw new AssertionError();
-    }
-  }
-}