Exposed source provider functionality through spi.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@74 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/guice.iws b/guice.iws
index b3befed..37079b9 100644
--- a/guice.iws
+++ b/guice.iws
@@ -18,13 +18,17 @@
   </component>
   <component name="ChangeListManager">
     <list default="true" name="Default" comment="">
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/com/google/inject/spi/SourceConsumer.java" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/com/google/inject/ContainerCreationException.java" afterPath="$PROJECT_DIR$/src/com/google/inject/ContainerCreationException.java" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/guice.iml" afterPath="$PROJECT_DIR$/guice.iml" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/com/google/inject/intercept/ProxyFactory.java" afterPath="$PROJECT_DIR$/src/com/google/inject/intercept/ProxyFactory.java" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/guice.iws" afterPath="$PROJECT_DIR$/guice.iws" />
       <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lib/build/cglib-nodep-2.1_3.jar" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/com/google/inject/intercept/Queries.java" afterPath="$PROJECT_DIR$/src/com/google/inject/intercept/Queries.java" />
+      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/com/google/inject/ContainerBuilder.java" afterPath="$PROJECT_DIR$/src/com/google/inject/ContainerBuilder.java" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/com/google/inject/spi/DefaultSourceProvider.java" />
       <change type="DELETED" beforePath="$PROJECT_DIR$/lib/cglib-nodep-2.1_3.jar" afterPath="" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/com/google/inject/spi/SourceProvider.java" />
     </list>
   </component>
   <component name="ChangeListSynchronizer" />
@@ -214,15 +218,6 @@
           </provider>
         </entry>
       </file>
-      <file leaf-file-name="package-info.java" pinned="false" current="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/src/com/google/inject/intercept/package-info.java">
-          <provider selected="true" editor-type-id="text-editor">
-            <state line="23" column="28" selection-start="863" selection-end="863" vertical-scroll-proportion="0.6337115">
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
       <file leaf-file-name="ErrorHandlingTest.java" pinned="false" current="false" current-in-tab="false">
         <entry file="file://$PROJECT_DIR$/test/com/google/inject/ErrorHandlingTest.java">
           <provider selected="true" editor-type-id="text-editor">
@@ -235,7 +230,47 @@
       <file leaf-file-name="ContainerBuilder.java" pinned="false" current="false" current-in-tab="false">
         <entry file="file://$PROJECT_DIR$/src/com/google/inject/ContainerBuilder.java">
           <provider selected="true" editor-type-id="text-editor">
-            <state line="58" column="19" selection-start="1914" selection-end="1914" vertical-scroll-proportion="0.33225283">
+            <state line="907" column="0" selection-start="25259" selection-end="25259" vertical-scroll-proportion="24.061588">
+              <folding>
+                <element signature="method#call#0;class#26208:26447" expanded="false" />
+              </folding>
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="SourceProvider.java" pinned="false" current="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/src/com/google/inject/spi/SourceProvider.java">
+          <provider selected="true" editor-type-id="text-editor">
+            <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
+              <folding />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="DefaultSourceProvider.java" pinned="false" current="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/src/com/google/inject/spi/DefaultSourceProvider.java">
+          <provider selected="true" editor-type-id="text-editor">
+            <state line="62" column="0" selection-start="2005" selection-end="2005" vertical-scroll-proportion="1.5316045">
+              <folding>
+                <element signature="imports" expanded="true" />
+              </folding>
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="SourceConsumer.java" pinned="false" current="true" current-in-tab="true">
+        <entry file="file://$PROJECT_DIR$/src/com/google/inject/spi/SourceConsumer.java">
+          <provider selected="true" editor-type-id="text-editor">
+            <state line="19" column="11" selection-start="644" selection-end="644" vertical-scroll-proportion="0.5235008">
+              <folding />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file leaf-file-name="Message.java" pinned="false" current="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/src/com/google/inject/spi/Message.java">
+          <provider selected="true" editor-type-id="text-editor">
+            <state line="14" column="3" selection-start="0" selection-end="595" vertical-scroll-proportion="0.38573745">
               <folding />
             </state>
           </provider>
@@ -250,24 +285,6 @@
           </provider>
         </entry>
       </file>
-      <file leaf-file-name="Exception.java" pinned="false" current="false" current-in-tab="false">
-        <entry file="jar:///usr/local/src.zip!/java/lang/Exception.java">
-          <provider selected="true" editor-type-id="text-editor">
-            <state line="39" column="11" selection-start="1206" selection-end="1206" vertical-scroll-proportion="0.910859">
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="MethodInterceptor.class" pinned="false" current="false" current-in-tab="false">
-        <entry file="jar://$PROJECT_DIR$/lib/aopalliance.jar!/org/aopalliance/intercept/MethodInterceptor.class">
-          <provider selected="true" editor-type-id="text-editor">
-            <state line="5" column="17" selection-start="167" selection-end="167" vertical-scroll-proportion="0.110210694">
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
       <file leaf-file-name="InterceptorStackCallback.java" pinned="false" current="false" current-in-tab="false">
         <entry file="file://$PROJECT_DIR$/src/com/google/inject/intercept/InterceptorStackCallback.java">
           <provider selected="true" editor-type-id="text-editor">
@@ -277,15 +294,6 @@
           </provider>
         </entry>
       </file>
-      <file leaf-file-name="MethodProxy.java" pinned="false" current="false" current-in-tab="false">
-        <entry file="jar://$PROJECT_DIR$/lib/build/cglib-src-2.1_3.jar!/net/sf/cglib/proxy/MethodProxy.java">
-          <provider selected="true" editor-type-id="text-editor">
-            <state line="164" column="18" selection-start="6352" selection-end="6352" vertical-scroll-proportion="1.3111831">
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
       <file leaf-file-name="Joinpoint.class" pinned="false" current="false" current-in-tab="false">
         <entry file="jar://$PROJECT_DIR$/lib/aopalliance.jar!/org/aopalliance/intercept/Joinpoint.class">
           <provider selected="true" editor-type-id="text-editor">
@@ -384,10 +392,10 @@
           </provider>
         </entry>
       </file>
-      <file leaf-file-name="Queries.java" pinned="false" current="true" current-in-tab="true">
+      <file leaf-file-name="Queries.java" pinned="false" current="false" current-in-tab="false">
         <entry file="file://$PROJECT_DIR$/src/com/google/inject/intercept/Queries.java">
           <provider selected="true" editor-type-id="text-editor">
-            <state line="34" column="18" selection-start="1040" selection-end="1040" vertical-scroll-proportion="0.027552674">
+            <state line="34" column="18" selection-start="1040" selection-end="1040" vertical-scroll-proportion="0.46839547">
               <folding />
             </state>
           </provider>
@@ -416,7 +424,7 @@
       <file leaf-file-name="guice.iws" pinned="false" current="false" current-in-tab="false">
         <entry file="file://$PROJECT_DIR$/guice.iws">
           <provider selected="true" editor-type-id="text-editor">
-            <state line="961" column="26" selection-start="47825" selection-end="47825" vertical-scroll-proportion="0.3936877">
+            <state line="954" column="53" selection-start="47825" selection-end="47825" vertical-scroll-proportion="0.3936877">
               <folding />
             </state>
           </provider>
@@ -750,6 +758,7 @@
       <recent name="com.google.inject.Key" />
     </key>
     <key name="CopyClassDialog.RECENTS_KEY">
+      <recent name="com.google.inject.spi" />
       <recent name="com.google.inject" />
     </key>
     <key name="IntroduceConstantDialog.RECENTS_KEY">
@@ -1098,38 +1107,6 @@
     <option name="myLastEditedConfigurable" value="Default" />
   </component>
   <component name="editorHistoryManager">
-    <entry file="file://$PROJECT_DIR$/src/com/google/inject/ErrorMessages.java">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="42" column="25" selection-start="1437" selection-end="1437" vertical-scroll-proportion="0.7714749">
-          <folding>
-            <element signature="imports" expanded="true" />
-          </folding>
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/src/com/google/inject/Container.java">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="100" column="23" selection-start="2670" selection-end="2670" vertical-scroll-proportion="1.4764992">
-          <folding>
-            <element signature="imports" expanded="true" />
-          </folding>
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/src/com/google/inject/ContainerImpl.java">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="0" column="0" selection-start="0" selection-end="595" vertical-scroll-proportion="0.0">
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/guice.iws">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="961" column="26" selection-start="47825" selection-end="47825" vertical-scroll-proportion="0.3936877">
-          <folding />
-        </state>
-      </provider>
-    </entry>
     <entry file="file://$PROJECT_DIR$/test/com/google/inject/intercept/IntegrationTest.java">
       <provider selected="true" editor-type-id="text-editor">
         <state line="15" column="0" selection-start="596" selection-end="596" vertical-scroll-proportion="0.4132901">
@@ -1187,20 +1164,6 @@
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/test/com/google/inject/ErrorHandlingTest.java">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="65" column="15" selection-start="1732" selection-end="1732" vertical-scroll-proportion="1.4051864">
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/src/com/google/inject/ContainerBuilder.java">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="58" column="19" selection-start="1914" selection-end="1914" vertical-scroll-proportion="0.33225283">
-          <folding />
-        </state>
-      </provider>
-    </entry>
     <entry file="file://$PROJECT_DIR$/src/com/google/inject/ContainerCreationException.java">
       <provider selected="true" editor-type-id="text-editor">
         <state line="37" column="18" selection-start="1151" selection-end="1151" vertical-scroll-proportion="0.52512157">
@@ -1210,7 +1173,53 @@
     </entry>
     <entry file="file://$PROJECT_DIR$/src/com/google/inject/intercept/Queries.java">
       <provider selected="true" editor-type-id="text-editor">
-        <state line="34" column="18" selection-start="1040" selection-end="1040" vertical-scroll-proportion="0.027552674">
+        <state line="34" column="18" selection-start="1040" selection-end="1040" vertical-scroll-proportion="0.46839547">
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/test/com/google/inject/ErrorHandlingTest.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="65" column="15" selection-start="1732" selection-end="1732" vertical-scroll-proportion="1.4051864">
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/com/google/inject/spi/Message.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="14" column="3" selection-start="0" selection-end="595" vertical-scroll-proportion="0.38573745">
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/com/google/inject/spi/DefaultSourceProvider.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="62" column="0" selection-start="2005" selection-end="2005" vertical-scroll-proportion="1.5316045">
+          <folding>
+            <element signature="imports" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/com/google/inject/spi/SourceProvider.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/com/google/inject/ContainerBuilder.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="907" column="0" selection-start="25259" selection-end="25259" vertical-scroll-proportion="24.061588">
+          <folding>
+            <element signature="method#call#0;class#26208:26447" expanded="false" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/com/google/inject/spi/SourceConsumer.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="19" column="11" selection-start="644" selection-end="644" vertical-scroll-proportion="0.5235008">
           <folding />
         </state>
       </provider>
diff --git a/src/com/google/inject/ContainerBuilder.java b/src/com/google/inject/ContainerBuilder.java
index 186060a..f1db3f4 100644
--- a/src/com/google/inject/ContainerBuilder.java
+++ b/src/com/google/inject/ContainerBuilder.java
@@ -16,24 +16,23 @@
 
 package com.google.inject;
 
-import com.google.inject.util.Objects;
-import com.google.inject.util.Stopwatch;
-import com.google.inject.util.ToStringBuilder;
-import static com.google.inject.util.Objects.nonNull;
 import com.google.inject.spi.ConstructionProxyFactory;
 import com.google.inject.spi.DefaultConstructionProxyFactory;
 import com.google.inject.spi.Message;
+import com.google.inject.spi.SourceConsumer;
+import com.google.inject.util.Objects;
+import static com.google.inject.util.Objects.nonNull;
+import com.google.inject.util.Stopwatch;
+import com.google.inject.util.ToStringBuilder;
 
 import java.lang.reflect.Member;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
-import java.util.HashSet;
 import java.util.logging.Logger;
 
 /**
@@ -56,7 +55,7 @@
  *
  * @author crazybob@google.com (Bob Lee)
  */
-public final class ContainerBuilder {
+public final class ContainerBuilder extends SourceConsumer {
 
   private static final Logger logger =
       Logger.getLogger(ContainerBuilder.class.getName());
@@ -906,41 +905,6 @@
     }
   }
 
-  final Set<String> skippedClassNames = new HashSet<String>(Arrays.asList(
-      ContainerBuilder.class.getName(),
-      AbstractModule.class.getName()
-  ));
-
-  /**
-   * Instructs the builder to skip the given class in the stack trace when
-   * determining the source of a binding. Use this to keep the container
-   * builder 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.
-   */
-  void skipSource(Class<?> clazz) {
-    skippedClassNames.add(clazz.getName());
-  }
-
-  /**
-   * 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. The default
-   * implementation returns {@code ContainerBuilder}'s caller's {@code
-   * StackTraceElement}.
-   */
-  Object source() {
-    // Search up the stack until we find a class outside of this one.
-    for (final StackTraceElement element : new Throwable().getStackTrace()) {
-      String className = element.getClassName();
-      if (!skippedClassNames.contains(className)) {
-        return element;
-      }
-    }
-    throw new AssertionError();
-  }
-
   /**
    * A requested static injection.
    */
diff --git a/src/com/google/inject/spi/DefaultSourceProvider.java b/src/com/google/inject/spi/DefaultSourceProvider.java
new file mode 100644
index 0000000..a39479d
--- /dev/null
+++ b/src/com/google/inject/spi/DefaultSourceProvider.java
@@ -0,0 +1,62 @@
+/**
+ * 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.inject.AbstractModule;
+import com.google.inject.ContainerBuilder;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Arrays;
+
+/**
+ * A source provider which returns {@code ContainerBuilder}'s caller's {@code
+ * StackTraceElement}.
+ * 
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class DefaultSourceProvider implements SourceProvider {
+
+  final Set<String> skippedClassNames = new HashSet<String>(Arrays.asList(
+      ContainerBuilder.class.getName(),
+      AbstractModule.class.getName(),
+      DefaultSourceProvider.class.getName()
+  ));
+
+  /**
+   * Instructs the provider to skip the given class in the stack trace when
+   * determining the source. Use this to keep the container builder 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 void skipSource(Class<?> clazz) {
+    skippedClassNames.add(clazz.getName());
+  }
+
+  public Object source() {
+    // Search up the stack until we find a class outside of this one.
+    for (final StackTraceElement element : new Throwable().getStackTrace()) {
+      String className = element.getClassName();
+      if (!skippedClassNames.contains(className)) {
+        return element;
+      }
+    }
+    throw new AssertionError();
+  }
+}
diff --git a/src/com/google/inject/spi/SourceConsumer.java b/src/com/google/inject/spi/SourceConsumer.java
new file mode 100644
index 0000000..90d0283
--- /dev/null
+++ b/src/com/google/inject/spi/SourceConsumer.java
@@ -0,0 +1,62 @@
+/**
+ * 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;
+
+/**
+ * Support for classes which use source objects.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class SourceConsumer {
+
+  SourceProvider sourceProvider = new DefaultSourceProvider();
+
+  /**
+   * Returns the current source.
+   */
+  protected Object source() {
+    return sourceProvider.source();
+  }
+
+  /**
+   * Gets the current source provider.
+   */
+  public SourceProvider getSourceProvider() {
+    return sourceProvider;
+  }
+
+  /**
+   * Sets the current source provider.
+   */
+  public void setSourceProvider(SourceProvider sourceProvider) {
+    this.sourceProvider = sourceProvider;
+  }
+
+  /**
+   * Sets the source provider, runs the given command, and restores the
+   * previous source provider.
+   */
+  public void withSourceProvider(SourceProvider sourceProvider, Runnable r) {
+    SourceProvider previous = this.sourceProvider;
+    try {
+      this.sourceProvider = sourceProvider;
+      r.run();
+    } finally {
+      this.sourceProvider = previous;
+    }
+  }
+}
diff --git a/src/com/google/inject/spi/SourceProvider.java b/src/com/google/inject/spi/SourceProvider.java
new file mode 100644
index 0000000..067006e
--- /dev/null
+++ b/src/com/google/inject/spi/SourceProvider.java
@@ -0,0 +1,35 @@
+/**
+ * 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;
+
+/**
+ * Provides source objects to the {@link com.google.inject.ContainerBuilder}.
+ * 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.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public interface 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();
+}