master moved from 57aad211bb85 to f1abba38c7f5

5 new revisions:

Revision: a5b2b1fc374c
Author:   Sam Berlin <[email protected]>
Date:     Thu Apr 10 23:17:11 2014 UTC
Log: Suppress compiler errors reported by an error-prone checker ([]) for t...
http://code.google.com/p/google-guice/source/detail?r=a5b2b1fc374c

Revision: c7ae433fc9d5
Author:   Sam Berlin <[email protected]>
Date:     Thu Apr 10 23:17:37 2014 UTC
Log: Add BoundFieldModule to automatically bind @Bind annotated fields to t...
http://code.google.com/p/google-guice/source/detail?r=c7ae433fc9d5

Revision: c35ebc2ce88f
Author:   Sam Berlin <[email protected]>
Date:     Thu Apr 10 23:18:01 2014 UTC
Log: Fix external issue 372 -- only scrub the pathInfo if the servletPath a...
http://code.google.com/p/google-guice/source/detail?r=c35ebc2ce88f

Revision: 647b24f7f1b5
Author:   Sam Berlin <[email protected]>
Date:     Thu Apr 10 23:18:26 2014 UTC
Log:      Fix external issue 755.  Grapher config was wrong....
http://code.google.com/p/google-guice/source/detail?r=647b24f7f1b5

Revision: f1abba38c7f5
Author:   Sam Berlin <[email protected]>
Date:     Thu Apr 10 23:18:51 2014 UTC
Log: On second thought, according to the javadocs @ http://docs.oracle.com/...
http://code.google.com/p/google-guice/source/detail?r=f1abba38c7f5

==============================================================================
Revision: a5b2b1fc374c
Author:   Sam Berlin <[email protected]>
Date:     Thu Apr 10 23:17:11 2014 UTC
Log: Suppress compiler errors reported by an error-prone checker ([]) for tests.

I wrote an error-prone checker([]) to check that injected constructors are not annotated with @Inject(optional=true) or with binding annotations. This CL annotates the intentional tests for such cases with @SuppressWarnings.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=64575706

http://code.google.com/p/google-guice/source/detail?r=a5b2b1fc374c

Modified:
 /core/test/com/google/inject/OptionalBindingTest.java
 /core/test/com/google/inject/ProvisionExceptionTest.java

=======================================
--- /core/test/com/google/inject/OptionalBindingTest.java Fri Jul 8 00:34:16 2011 UTC +++ /core/test/com/google/inject/OptionalBindingTest.java Thu Apr 10 23:17:11 2014 UTC
@@ -255,6 +255,9 @@
   }

   static class HasOptionalConstructor {
+ // Suppress compiler errors by the error-prone checker InjectedConstructorAnnotations,
+    // which catches optional injected constructors.
+    @SuppressWarnings("InjectedConstructorAnnotations")
     @Inject(optional=true)
     HasOptionalConstructor() {}
   }
=======================================
--- /core/test/com/google/inject/ProvisionExceptionTest.java Sat Oct 5 21:05:39 2013 UTC +++ /core/test/com/google/inject/ProvisionExceptionTest.java Thu Apr 10 23:17:11 2014 UTC
@@ -295,6 +295,9 @@
   }

   static class ConstructorWithBindingAnnotation {
+ // Suppress compiler errors by the error-prone checker InjectedConstructorAnnotations,
+    // which catches injected constructors with binding annotations.
+    @SuppressWarnings("InjectedConstructorAnnotations")
     @Inject @Green ConstructorWithBindingAnnotation(String greenString) {}
   }


==============================================================================
Revision: c7ae433fc9d5
Author:   Sam Berlin <[email protected]>
Date:     Thu Apr 10 23:17:37 2014 UTC
Log: Add BoundFieldModule to automatically bind @Bind annotated fields to their
values, reducing test boilerplate.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=64668801

http://code.google.com/p/google-guice/source/detail?r=c7ae433fc9d5

Added:
 /extensions/testlib/build.properties
 /extensions/testlib/build.xml
 /extensions/testlib/pom.xml
 /extensions/testlib/src/com/google/inject/testing/fieldbinder/Bind.java
/extensions/testlib/src/com/google/inject/testing/fieldbinder/BoundFieldModule.java /extensions/testlib/test/com/google/inject/testing/fieldbinder/BoundFieldModuleTest.java
 /extensions/testlib/testlib.iml
Modified:
 /build.properties
 /build.xml
 /extensions/pom.xml

=======================================
--- /dev/null
+++ /extensions/testlib/build.properties        Thu Apr 10 23:17:37 2014 UTC
@@ -0,0 +1,7 @@
+lib.dir=../../lib
+src.dir=src
+test.dir=test
+build.dir=build
+test.class=com.google.inject.testing.fieldbinder.FieldBindingModuleTest
+module=com.google.inject.testing
+fragment=true
=======================================
--- /dev/null
+++ /extensions/testlib/build.xml       Thu Apr 10 23:17:37 2014 UTC
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+
+<project name="guice-testlib" basedir="." default="jar">
+
+  <import file="../../common.xml"/>
+
+  <path id="compile.classpath">
+    <fileset dir="${lib.dir}" includes="*.jar"/>
+    <fileset dir="${lib.dir}/build" includes="*.jar"/>
+    <pathelement path="../../build/classes"/>
+  </path>
+
+  <target name="jar" depends="compile, manifest" description="Build jar.">
+    <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+        manifest="${build.dir}/META-INF/MANIFEST.MF">
+      <fileset dir="${build.dir}/classes" />
+    </jar>
+  </target>
+
+</project>
=======================================
--- /dev/null
+++ /extensions/testlib/pom.xml Thu Apr 10 23:17:37 2014 UTC
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd";>
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.google.inject.extensions</groupId>
+    <artifactId>extensions-parent</artifactId>
+    <version>4.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>guice-testlib</artifactId>
+
+  <name>Google Guice - Extensions - TestLib</name>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>**/TestScope*</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
=======================================
--- /dev/null
+++ /extensions/testlib/src/com/google/inject/testing/fieldbinder/Bind.java Thu Apr 10 23:17:37 2014 UTC
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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.testing.fieldbinder;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used by {@link BoundFieldModule} to indicate that a field should be bound to its
+ * value using Guice.
+ *
+ * @see BoundFieldModule
+ * @author [email protected] (Russ Harmon)
+ */
+@Retention(RUNTIME)
+@Target({FIELD})
+public @interface Bind {
+  /**
+ * If specified, {@link BoundFieldModule} will bind the annotated field's value to this type,
+   * rather than to the field's actual type.
+   */
+  Class<?> to() default Bind.class;
+}
=======================================
--- /dev/null
+++ /extensions/testlib/src/com/google/inject/testing/fieldbinder/BoundFieldModule.java Thu Apr 10 23:17:37 2014 UTC
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2014 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.testing.fieldbinder;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.inject.Binder;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.binder.AnnotatedBindingBuilder;
+import com.google.inject.binder.LinkedBindingBuilder;
+import com.google.inject.internal.Annotations;
+import com.google.inject.spi.Message;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+/**
+ * Automatically creates Guice bindings for fields in an object annotated with {@link Bind}.
+ *
+ * <p>This module is intended for use in tests to reduce the code needed to bind local fields
+ * (usually mocks) for injection.
+ *
+ * <p>The following rules are followed in determining how fields are bound using this module:
+ *
+ * <ul>
+ * <li>
+ * For each {@link Bind} annotated field of an object and its superclasses, this module will bind + * that field's type to that field's value at injector creation time. This includes both instance
+ * and static fields.
+ * </li>
+ * <li>
+ * If {@link Bind#to} is specified, the field's value will be bound to the class specified by
+ * {@link Bind#to} instead of the field's actual type.
+ * </li>
+ * <li>
+ * If a {@link BindingAnnotation} or {@link Qualifier} is present on the field, that field will be + * bound using that annotation via {@link AnnotatedBindingBuilder#annotatedWith}. For example, + * {@code bind(Foo.class).annotatedWith(BarAnnotation.class).toInstance(theValue)}. It is an error
+ * to supply more than one {@link BindingAnnotation} or {@link Qualifier}.
+ * </li>
+ * <li>
+ * If the field is of type {@link Provider}, the field's value will be bound as a {@link Provider} + * using {@link LinkedBindingBuilder#toProvider} to the provider's parameterized type. For example, + * {@code Provider<Integer>} binds to {@link Integer}. Attempting to bind a non-parameterized
+ * {@link Provider} without a {@link Bind#to} clause is an error.
+ * </li>
+ * </ul>
+ *
+ * <p>Example use:
+ * <pre><code>
+ * public class TestFoo {
+ * // bind(new TypeLiteral{@code <List<Object>>}() {}).toInstance(listOfObjects); + * {@literal @}Bind private List{@code <Object>} listOfObjects = Lists.of();
+ *
+ *   // bind(SuperClass.class).toInstance(aSubClass);
+ * {@literal @}Bind(to = SuperClass.class) private SubClass aSubClass = new SubClass();
+ *
+ * // bind(Object.class).annotatedWith(MyBindingAnnotation.class).toInstance(object2);
+ *   {@literal @}Bind
+ *   {@literal @}MyBindingAnnotation
+ *   private String myString = "hello";
+ *
+ *   // bind(Object.class).toProvider(myProvider);
+ * {@literal @}Bind private Provider{@code <Object>} myProvider = getProvider();
+ *
+ *   {@literal @}Before public void setUp() {
+ *     Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
+ *   }
+ * }
+ * </code></pre>
+ *
+ * @see Bind
+ * @author [email protected] (Russ Harmon)
+ */
+public final class BoundFieldModule implements Module {
+  private final Object instance;
+
+  // Note that binder is not initialized until configure() is called.
+  private Binder binder;
+
+  private BoundFieldModule(Object instance) {
+    this.instance = instance;
+  }
+
+  /**
+ * Create a BoundFieldModule which binds the {@link Bind} annotated fields of {@code instance}.
+   *
+   * @param instance the instance whose fields will be bound.
+ * @return a module which will bind the {@link Bind} annotated fields of {@code instance}.
+   */
+  public static BoundFieldModule of(Object instance) {
+    return new BoundFieldModule(instance);
+  }
+
+  private static class BoundFieldException extends RuntimeException {
+    BoundFieldException(String message) {
+      super(message);
+    }
+  }
+
+  private class BoundFieldInfo {
+    /** The field itself. */
+    final Field field;
+
+    /**
+     * The actual type of the field.
+     *
+ * <p>For example, {@code @Bind(to = Object.class) Number one = new Integer(1);} will be
+     * {@link Number}.
+     */
+    final TypeLiteral<?> type;
+
+    /** The {@link Bind} annotation which is present on the field. */
+    final Bind bindAnnotation;
+
+    /**
+     * The type this field will bind to.
+     *
+ * <p>For example, {@code @Bind(to = Object.class) Number one = new Integer(1);} will be + * {@link Object} and {@code @Bind Number one = new Integer(1);} will be {@link Number}.
+     */
+    final TypeLiteral<?> boundType;
+
+    /**
+     * The "natural" type of this field.
+     *
+ * <p>For example, {@code @Bind(to = Object.class) Number one = new Integer(1);} will be + * {@link Number}, and {@code @Bind(to = Object.class) Provider<Number> one = new Integer(1);}
+     * will be {@link Number}.
+     *
+     * @see getNaturalFieldType
+     */
+    final Optional<TypeLiteral<?>> naturalType;
+
+    BoundFieldInfo(
+        Field field,
+        Bind bindAnnotation,
+        TypeLiteral<?> fieldType) {
+      this.field = field;
+      this.type = fieldType;
+      this.bindAnnotation = bindAnnotation;
+
+      field.setAccessible(true);
+
+      this.naturalType = getNaturalFieldType();
+      this.boundType = getBoundType();
+    }
+
+    private TypeLiteral<?> getBoundType() {
+      Class<?> bindClass = bindAnnotation.to();
+ // Bind#to's default value is Bind.class which is used to represent that no explicit binding
+      // type is requested.
+      if (bindClass == Bind.class) {
+        Preconditions.checkState(naturalType != null);
+        if (!this.naturalType.isPresent()) {
+          addErrorAndThrow(
+              field,
+              "Non parameterized Provider fields must have an explicit "
+              + "binding class via @Bind(to = Foo.class)");
+        }
+        return this.naturalType.get();
+      } else {
+        return TypeLiteral.get(bindClass);
+      }
+    }
+
+    /**
+     * Retrieves the type this field binds to naturally.
+     *
+ * <p>A field's "natural" type specifically ignores the to() method on the @Bind annotation, is + * the parameterized type if the field's actual type is a parameterized {@link Provider}, is + * {@link Optional#absent()} if this field is a non-parameterized {@link Provider} and otherwise
+     * is the field's actual type.
+     *
+ * @return the type this field binds to naturally, or {@link Optional#absent()} if this field is
+     * a non-parameterized {@link Provider}.
+     */
+    private Optional<TypeLiteral<?>> getNaturalFieldType() {
+      if (isTransparentProvider(type.getRawType())) {
+        Type providerType = type.getType();
+        if (providerType instanceof Class) {
+          return Optional.absent();
+        }
+ Preconditions.checkState(providerType instanceof ParameterizedType); + Type[] providerTypeArguments = ((ParameterizedType) providerType).getActualTypeArguments();
+        Preconditions.checkState(providerTypeArguments.length == 1);
+ return Optional.<TypeLiteral<?>>of(TypeLiteral.get(providerTypeArguments[0]));
+      } else {
+        return Optional.<TypeLiteral<?>>of(type);
+      }
+    }
+
+    Object getValue() {
+      try {
+        return field.get(instance);
+      } catch (IllegalAccessException e) {
+ // Since we called setAccessible(true) on this field in the constructor, this is a
+        // programming error if it occurs.
+        throw new AssertionError(e);
+      }
+    }
+  }
+
+  private static boolean hasInject(Field field) {
+    return field.isAnnotationPresent(javax.inject.Inject.class)
+        || field.isAnnotationPresent(com.google.inject.Inject.class);
+  }
+
+  /**
+   * Retrieve a {@link BoundFieldInfo}.
+   *
+ * <p>This returns a {@link BoundFieldInfo} if the field has a {@link Bind} annotation.
+   * Otherwise it returns {@link Optional#absent()}.
+   */
+  private Optional<BoundFieldInfo> getBoundFieldInfo(
+      TypeLiteral<?> containingClassType,
+      Field field) {
+    Bind bindAnnotation = field.getAnnotation(Bind.class);
+    if (bindAnnotation == null) {
+      return Optional.absent();
+    }
+    if (hasInject(field)) {
+      addErrorAndThrow(
+          field,
+          "Fields annotated with both @Bind and @Inject are illegal.");
+    }
+    return Optional.of(new BoundFieldInfo(
+        field,
+        bindAnnotation,
+        containingClassType.getFieldType(field)));
+  }
+
+  private LinkedBindingBuilder<?> verifyBindingAnnotations(
+      Field field,
+      AnnotatedBindingBuilder<?> annotatedBinder) {
+    LinkedBindingBuilder<?> binderRet = annotatedBinder;
+    for (Annotation annotation : field.getAnnotations()) {
+ Class<? extends Annotation> annotationType = annotation.annotationType();
+      if (Annotations.isBindingAnnotation(annotationType)) {
+ // not returning here ensures that annotatedWith will be called multiple times if this field + // has multiple BindingAnnotations, relying on the binder to throw an error in this case.
+        binderRet = annotatedBinder.annotatedWith(annotation);
+      }
+    }
+    return binderRet;
+  }
+
+  /**
+   * Determines if {@code clazz} is a "transparent provider".
+   *
+   * <p>A transparent provider is a {@link com.google.inject.Provider} or
+ * {@link javax.inject.Provider} which binds to it's parameterized type when used as the argument
+   * to {@link Binder#bind}.
+   *
+ * <p>A {@link Provider} is transparent if the base class of that object is {@link Provider}. In + * other words, subclasses of {@link Provider} are not transparent. As a special case, if a + * {@link Provider} has no parameterized type but is otherwise transparent, then it is considered
+   * transparent.
+   */
+  private static boolean isTransparentProvider(Class<?> clazz) {
+ return com.google.inject.Provider.class == clazz || javax.inject.Provider.class == clazz;
+  }
+
+  private void bindField(BoundFieldInfo fieldInfo) {
+    if (fieldInfo.naturalType.isPresent()) {
+      Class<?> naturalRawType = fieldInfo.naturalType.get().getRawType();
+      Class<?> boundRawType = fieldInfo.boundType.getRawType();
+      if (!boundRawType.isAssignableFrom(naturalRawType)) {
+        addErrorAndThrow(
+            fieldInfo.field,
+ "Requested binding type \"%s\" is not assignable from field binding type \"%s\"",
+            boundRawType.getName(),
+            naturalRawType.getName());
+      }
+    }
+
+ AnnotatedBindingBuilder<?> annotatedBinder = binder.bind(fieldInfo.boundType); + LinkedBindingBuilder<?> binder = verifyBindingAnnotations(fieldInfo.field, annotatedBinder);
+
+ // It's unfortunate that Field.get() just returns Object rather than the actual type (although + // that would be impossible) because as a result calling binder.toInstance or binder.toProvider + // is impossible to do without an unchecked cast. This is safe if fieldInfo.naturalType is + // present because compatibility is checked explicitly above, but is _unsafe_ if + // fieldInfo.naturalType is absent which occurrs when a non-parameterized Provider is used with
+    // @Bind(to = ...)
+    @SuppressWarnings("unchecked")
+ AnnotatedBindingBuilder<Object> binderUnsafe = (AnnotatedBindingBuilder<Object>) binder;
+
+    Object fieldValue = fieldInfo.getValue();
+
+    if (fieldValue == null) {
+      addErrorAndThrow(
+          fieldInfo.field,
+          "Binding to null values is not allowed. "
+          + "Use Providers.of(null) if this is your intended behavior.",
+          fieldInfo.field.getName());
+    }
+
+    if (isTransparentProvider(fieldInfo.type.getRawType())) {
+ // This is safe because we checked that the field's type is Provider above.
+      @SuppressWarnings("unchecked")
+      Provider<?> fieldValueUnsafe = (Provider<?>) fieldValue;
+
+      binderUnsafe.toProvider(fieldValueUnsafe);
+    } else {
+      binderUnsafe.toInstance(fieldValue);
+    }
+  }
+
+ private void addErrorAndThrow(Field field, String format, Object... args) {
+    Preconditions.checkNotNull(binder);
+    String source = String.format(
+        "%s field %s",
+        field.getDeclaringClass().getName(),
+        field.getName());
+    Message messageObj = new Message(source, String.format(format, args));
+
+    binder.addError(messageObj);
+    throw new BoundFieldException(messageObj.getMessage());
+  }
+
+  @Override
+  public void configure(Binder binder) {
+    binder = binder.skipSources(BoundFieldModule.class);
+    this.binder = binder;
+
+    TypeLiteral<?> currentClassType = TypeLiteral.get(instance.getClass());
+    while (currentClassType.getRawType() != Object.class) {
+ for (Field field : currentClassType.getRawType().getDeclaredFields()) {
+        try {
+          Optional<BoundFieldInfo> fieldInfoOpt =
+              getBoundFieldInfo(currentClassType, field);
+          if (fieldInfoOpt.isPresent()) {
+            bindField(fieldInfoOpt.get());
+          }
+        } catch (BoundFieldException e) {
+          // addErrorAndThrow already called addError, so do nothing
+        }
+      }
+      currentClassType = currentClassType.getSupertype(
+          currentClassType.getRawType().getSuperclass());
+    }
+  }
+}
=======================================
--- /dev/null
+++ /extensions/testlib/test/com/google/inject/testing/fieldbinder/BoundFieldModuleTest.java Thu Apr 10 23:17:37 2014 UTC
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2014 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.testing.fieldbinder;
+
+import static com.google.inject.Asserts.assertContains;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+import com.google.inject.ConfigurationException;
+import com.google.inject.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Retention;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.inject.Qualifier;
+
+/** Unit tests for {@link BoundFieldModule}. */
+public class BoundFieldModuleTest extends TestCase {
+  public void testBindingNothing() {
+    Object instance = new Object() {};
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Guice.createInjector(module);
+
+    // If we didn't throw an exception, we succeeded.
+  }
+
+  public void testBindingOnePrivate() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+      @Bind private Integer anInt = testValue;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(Integer.class));
+  }
+
+  public void testBindingOnePublic() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+      @Bind public Integer anInt = testValue;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(Integer.class));
+  }
+
+  private static class FieldBindableClass {
+    @Bind Integer anInt;
+
+    FieldBindableClass(Integer anInt) {
+      this.anInt = anInt;
+    }
+  }
+
+  private static class FieldBindableSubclass extends FieldBindableClass {
+    FieldBindableSubclass(Integer anInt) {
+      super(anInt);
+    }
+  }
+
+  public void testSuperTypeBinding() {
+    FieldBindableSubclass instance = new FieldBindableSubclass(1024);
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(instance.anInt, injector.getInstance(Integer.class));
+  }
+
+  public void testBindingTwo() {
+    final Integer testValue = 1024;
+    final String testString = "Hello World!";
+    Object instance = new Object() {
+      @Bind private Integer anInt = testValue;
+      @Bind private String aString = testString;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(Integer.class));
+    assertEquals(testString, injector.getInstance(String.class));
+  }
+
+  public void testBindingSuperType() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+      @Bind(to = Number.class) private Integer anInt = testValue;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(Number.class));
+  }
+
+  public void testBindingIncorrectTypeProviderFails() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+ @Bind(to = String.class) private Provider<Integer> anIntProvider = new Provider<Integer>() {
+        @Override public Integer get() {
+          return testValue;
+        }
+      };
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException e) {
+      assertContains(
+          e.getMessage(),
+          "Requested binding type \"java.lang.String\" is not "
+          + "assignable from field binding type \"java.lang.Integer\"");
+    }
+  }
+
+  @BindingAnnotation
+  @Retention(RUNTIME)
+  private static @interface SomeBindingAnnotation {}
+
+  public void testBindingWithBindingAnnotation() {
+    final Integer testValue1 = 1024, testValue2 = 2048;
+    Object instance = new Object() {
+      @Bind private Integer anInt = testValue1;
+
+      @Bind
+      @SomeBindingAnnotation
+      private Integer anotherInt = testValue2;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue1, injector.getInstance(Integer.class));
+    assertEquals(
+        testValue2,
+ injector.getInstance(Key.get(Integer.class, SomeBindingAnnotation.class)));
+  }
+
+  @Qualifier
+  @Retention(RUNTIME)
+  private static @interface SomeQualifier {}
+
+  public void testBindingWithQualifier() {
+    final Integer testValue1 = 1024, testValue2 = 2048;
+    Object instance = new Object() {
+      @Bind private Integer anInt = testValue1;
+
+      @Bind
+      @SomeQualifier
+      private Integer anotherInt = testValue2;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue1, injector.getInstance(Integer.class));
+    assertEquals(
+        testValue2,
+        injector.getInstance(Key.get(Integer.class, SomeQualifier.class)));
+  }
+
+  public void testCanReuseBindingAnnotationsWithDifferentValues() {
+    final Integer testValue1 = 1024, testValue2 = 2048;
+    final String name1 = "foo", name2 = "bar";
+    Object instance = new Object() {
+      @Bind
+      @Named(name1)
+      private Integer anInt = testValue1;
+
+      @Bind
+      @Named(name2)
+      private Integer anotherInt = testValue2;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(
+        testValue1,
+        injector.getInstance(Key.get(Integer.class, Names.named(name1))));
+    assertEquals(
+        testValue2,
+        injector.getInstance(Key.get(Integer.class, Names.named(name2))));
+  }
+
+  public void testBindingWithValuedBindingAnnotation() {
+    final Integer testValue1 = 1024, testValue2 = 2048;
+    final String name = "foo";
+    Object instance = new Object() {
+      @Bind private Integer anInt = testValue1;
+
+      @Bind
+      @Named(name)
+      private Integer anotherInt = testValue2;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue1, injector.getInstance(Integer.class));
+    assertEquals(
+        testValue2,
+        injector.getInstance(Key.get(Integer.class, Names.named(name))));
+  }
+
+  public void testBindingWithGenerics() {
+ final List<Integer> testIntList = Arrays.asList(new Integer[] {1, 2, 3}); + final List<Boolean> testBoolList = Arrays.asList(new Boolean[] {true, true, false});
+    Object instance = new Object() {
+      @Bind private List<Integer> anIntList = testIntList;
+      @Bind private List<Boolean> aBoolList = testBoolList;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+ assertEquals(testIntList, injector.getInstance(new Key<List<Integer>>() {})); + assertEquals(testBoolList, injector.getInstance(new Key<List<Boolean>>() {}));
+  }
+
+  public void testBoundValueDoesntChange() {
+    Integer testValue = 1024;
+    FieldBindableClass instance = new FieldBindableClass(testValue);
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(Integer.class));
+    instance.anInt++;
+    assertEquals(testValue, injector.getInstance(Integer.class));
+  }
+
+  public void testIncompatibleBindingType() {
+    final Integer testInt = 1024;
+    Object instance = new Object() {
+      @Bind(to = String.class) private Integer anInt = testInt;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException e) {
+      assertContains(e.getMessage(),
+ "Requested binding type \"java.lang.String\" is not assignable from field binding type "
+          + "\"java.lang.Integer\"");
+    }
+  }
+
+  public void testFailureOnMultipleBindingAnnotations() {
+    final Integer testInt = 1024;
+    Object instance = new Object() {
+      @Bind
+      @Named("a")
+      @SomeBindingAnnotation
+      private Integer anInt = testInt;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+
+    try {
+      Guice.createInjector(module);
+    } catch (CreationException e) {
+ assertContains(e.getMessage(), "More than one annotation is specified for this binding.");
+    }
+  }
+
+  public void testBindingSuperTypeAndBindingAnnotation() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+      @Bind(to = Number.class)
+      @Named("foo")
+      private Integer anInt = testValue;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+ assertEquals(testValue, injector.getInstance(Key.get(Number.class, Names.named("foo"))));
+  }
+
+  public void testBindingProvider() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+      @Bind private Provider<Integer> anInt = new Provider<Integer>() {
+        @Override public Integer get() {
+          return testValue;
+        }
+      };
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(Integer.class));
+  }
+
+  public void testBindingNullField() {
+    Object instance = new Object() {
+      @Bind private Integer anInt = null;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException e) {
+      assertContains(e.getMessage(),
+          "Binding to null values is not allowed. "
+          + "Use Providers.of(null) if this is your intended behavior.");
+    }
+  }
+
+  public void testBindingNullProvider() {
+    Object instance = new Object() {
+      @Bind private Provider<Integer> anIntProvider = null;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException e) {
+      assertContains(e.getMessage(),
+          "Binding to null values is not allowed. "
+          + "Use Providers.of(null) if this is your intended behavior.");
+    }
+  }
+
+  private static class IntegerProvider implements Provider<Integer> {
+    private final Integer value;
+
+    IntegerProvider(Integer value) {
+      this.value = value;
+    }
+
+    @Override public Integer get() {
+      return value;
+    }
+  }
+
+  public void testProviderSubclassesBindToTheProviderItself() {
+    final IntegerProvider integerProvider = new IntegerProvider(1024);
+    Object instance = new Object() {
+      @Bind private IntegerProvider anIntProvider = integerProvider;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+ assertEquals(integerProvider, injector.getInstance(IntegerProvider.class));
+  }
+
+  public void testProviderSubclassesDoNotBindParameterizedType() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+ @Bind private IntegerProvider anIntProvider = new IntegerProvider(testValue);
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    try {
+      injector.getInstance(Integer.class);
+      fail();
+    } catch (ConfigurationException e) {
+ assertContains(e.getMessage(), "Could not find a suitable constructor in java.lang.Integer.");
+    }
+  }
+
+  private static class ParameterizedObject<T> {
+    ParameterizedObject(T instance) {
+      this.instance = instance;
+    }
+
+    @Bind private T instance;
+  }
+
+  public void testBindParameterizedTypeFails() {
+ ParameterizedObject<Integer> instance = new ParameterizedObject<Integer>(0);
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException e) {
+ assertContains(e.getMessage(), "T cannot be used as a key; It is not fully specified.");
+    }
+  }
+
+  public void testBindSubclassOfParameterizedTypeSucceeds() {
+    final Integer testValue = 1024;
+ ParameterizedObject<Integer> instance = new ParameterizedObject<Integer>(testValue) {};
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(Integer.class));
+  }
+
+  public void testBindArray() {
+    final Integer[] testArray = new Integer[] { 1024, 2048 };
+    Object instance = new Object() {
+      @Bind private Integer[] anIntArray = testArray;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testArray, injector.getInstance(Integer[].class));
+  }
+
+  public void testRawProviderCannotBeBound() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+      @Bind private Provider anIntProvider = new Provider() {
+        @Override public Object get() {
+          return testValue;
+        }
+      };
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException e) {
+      assertContains(e.getMessage(),
+          "Non parameterized Provider fields must have an "
+          + "explicit binding class via @Bind(to = Foo.class)");
+    }
+  }
+
+  public void testExplicitlyBoundRawProviderCanBeBound() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+ @Bind(to = Integer.class) private Provider anIntProvider = new Provider() {
+        @Override public Object get() {
+          return testValue;
+        }
+      };
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(Integer.class));
+  }
+
+  public void testRawProviderCanBindToIncorrectType() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+ @Bind(to = String.class) private Provider anIntProvider = new Provider() {
+        @Override public Object get() {
+          return testValue;
+        }
+      };
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(String.class));
+  }
+
+  public void testMultipleErrorsAreAggregated() {
+    Object instance = new Object() {
+      @Bind private Provider aProvider;
+      @Bind(to = String.class) private Integer anInt;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    try {
+      Guice.createInjector(module);
+    } catch (CreationException e) {
+      assertEquals(2, e.getErrorMessages().size());
+    }
+  }
+
+  public void testBindingProviderWithProviderSubclassValue() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+ @Bind private Provider<Integer> anIntProvider = new IntegerProvider(testValue);
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(Integer.class));
+  }
+
+  public void testBoundFieldsCannotBeInjected() {
+    Object instance = new Object() {
+      @Bind
+      @Inject
+      Integer anInt = 0;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+
+    try {
+      Guice.createInjector(module);
+    } catch (CreationException e) {
+      assertContains(
+          e.getMessage(),
+          "Fields annotated with both @Bind and @Inject are illegal.");
+    }
+  }
+
+  public void testIncrementingProvider() {
+    final Integer testBaseValue = 1024;
+    Object instance = new Object() {
+ @Bind private Provider<Integer> anIntProvider = new Provider<Integer>() {
+        private int value = testBaseValue;
+
+        @Override public Integer get() {
+          return value++;
+        }
+      };
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testBaseValue, injector.getInstance(Integer.class));
+ assertEquals((Integer) (testBaseValue + 1), injector.getInstance(Integer.class)); + assertEquals((Integer) (testBaseValue + 2), injector.getInstance(Integer.class));
+  }
+
+  public void testProviderDoesNotProvideDuringInjectorConstruction() {
+    Object instance = new Object() {
+ @Bind private Provider<Integer> myIntProvider = new Provider<Integer>() {
+        @Override public Integer get() {
+          throw new UnsupportedOperationException();
+        }
+      };
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Guice.createInjector(module);
+
+    // If we don't throw an exception, we succeeded.
+  }
+
+  private static class InvalidBindableClass {
+    @Bind(to = String.class) Integer anInt;
+  }
+
+  public void testIncompatibleBindingTypeStackTraceHasUserFrame() {
+    Object instance = new InvalidBindableClass();
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException e) {
+ assertContains(e.getMessage(), "at " + InvalidBindableClass.class.getName() + " field anInt");
+    }
+  }
+
+  private static class InjectedNumberProvider implements Provider<Number> {
+    @Inject Integer anInt;
+
+    @Override public Number get() {
+      return anInt;
+    }
+  }
+
+  public void testBoundProvidersAreInjected() {
+    final Integer testValue = 1024;
+    Object instance = new Object() {
+      @Bind private Integer anInt = testValue;
+ @Bind private Provider<Number> aNumberProvider = new InjectedNumberProvider();
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, injector.getInstance(Number.class));
+  }
+
+  public void testBoundInstancesAreInjected() {
+    final Integer testValue = 1024;
+ final InjectedNumberProvider testNumberProvider = new InjectedNumberProvider();
+    Object instance = new Object() {
+      @Bind private Integer anInt = testValue;
+ @Bind private InjectedNumberProvider aNumberProvider = testNumberProvider;
+    };
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue, testNumberProvider.anInt);
+  }
+
+ private static class InvalidBindableSubclass extends InvalidBindableClass {}
+
+  public void testClassIsPrintedInErrorsWhenCauseIsSuperclass() {
+    Object instance = new InvalidBindableSubclass();
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+
+    try {
+      Guice.createInjector(module);
+    } catch (CreationException e) {
+      assertContains(
+          e.getMessage(),
+ "Requested binding type \"java.lang.String\" is not assignable from field binding type "
+          + "\"java.lang.Integer\"");
+    }
+  }
+
+  private static class FieldBindableSubclass2 extends FieldBindableClass {
+    @Bind Number aNumber;
+
+    FieldBindableSubclass2(Integer anInt, Number aNumber) {
+      super(anInt);
+      this.aNumber = aNumber;
+    }
+  }
+
+  public void testFieldsAreBoundFromFullClassHierarchy() {
+    final Integer testValue1 = 1024, testValue2 = 2048;
+ FieldBindableSubclass2 instance = new FieldBindableSubclass2(testValue1, testValue2);
+
+    BoundFieldModule module = BoundFieldModule.of(instance);
+    Injector injector = Guice.createInjector(module);
+
+    assertEquals(testValue1, injector.getInstance(Integer.class));
+    assertEquals(testValue2, injector.getInstance(Number.class));
+  }
+}
=======================================
--- /dev/null
+++ /extensions/testlib/testlib.iml     Thu Apr 10 23:17:37 2014 UTC
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="guice" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../lib/build/junit.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+  </component>
+</module>
+
=======================================
--- /build.properties   Thu Feb 13 18:37:24 2014 UTC
+++ /build.properties   Thu Apr 10 23:17:37 2014 UTC
@@ -13,6 +13,7 @@
 persist.src.dir=extensions/persist/src
 struts2.src.dir=extensions/struts2/src
 grapher.src.dir=extensions/grapher/src
+testlib.src.dir=extensions/testlib/src
 build.dir=build
 javadoc.packagenames=com.google.inject,com.google.inject.spi,\
   com.google.inject.matcher,\
@@ -29,7 +30,9 @@
   com.google.inject.util,\
   com.google.inject.persist,\
   com.google.inject.persist.finder,\
-  com.google.inject.persist.jpa
+  com.google.inject.persist.jpa,\
+  com.google.inject.testing,\
+  com.google.inject.testing.fieldbinder
 test.class=com.google.inject.AllTests
 module=com.google.inject
 imports=!net.sf.cglib.*,!org.objectweb.asm.*
=======================================
--- /build.xml  Tue Apr  8 16:26:24 2014 UTC
+++ /build.xml  Thu Apr 10 23:17:37 2014 UTC
@@ -28,7 +28,8 @@
<ant antfile="extensions/throwingproviders/build.xml" target="distjars" inheritAll="false"/> <ant antfile="extensions/multibindings/build.xml" target="distjars" inheritAll="false"/> <ant antfile="extensions/persist/build.xml" target="distjars" inheritAll="false"/> - <ant antfile="extensions/grapher/build.xml" target="distjars" inheritAll="false"/> + <ant antfile="extensions/grapher/build.xml" target="distjars" inheritAll="false"/> + <ant antfile="extensions/testlib/build.xml" target="distjars" inheritAll="false"/>

     <copy toDir="${build.dir}/dist">
       <fileset dir="extensions/servlet/build" includes="*.jar"/>
@@ -57,9 +58,12 @@
     <copy toDir="${build.dir}/dist">
       <fileset dir="extensions/persist/build" includes="*.jar"/>
     </copy>
-        <copy toDir="${build.dir}/dist">
-          <fileset dir="extensions/grapher/build" includes="*.jar"/>
-        </copy>
+    <copy toDir="${build.dir}/dist">
+      <fileset dir="extensions/grapher/build" includes="*.jar"/>
+    </copy>
+    <copy toDir="${build.dir}/dist">
+      <fileset dir="extensions/testlib/build" includes="*.jar"/>
+    </copy>

     <copy toDir="${build.dir}/dist" file="COPYING"/>
     <copy toDir="${build.dir}/dist">
@@ -153,6 +157,7 @@
       <fileset dir="${persist.src.dir}"/>
       <fileset dir="${struts2.src.dir}"/>
       <fileset dir="${grapher.src.dir}"/>
+      <fileset dir="${testlib.src.dir}"/>

<doclet name="jdiff.JDiff" path="${jdiff.home}/jdiff.jar:${jdiff.home}/xerces.jar">
         <param name="-apidir" value="${jdiff.tmp}" />
@@ -179,7 +184,7 @@
           </doclet>
         </javadoc>
   </target>
-
+
   <target name="javadoc">
     <javadoc packagenames="com.google.*"
              destdir="build/docs"
@@ -200,6 +205,7 @@
       <fileset dir="${multibindings.src.dir}"/>
       <fileset dir="${persist.src.dir}"/>
       <fileset dir="${grapher.src.dir}"/>
+      <fileset dir="${testlib.src.dir}"/>
       <!-- TODO: this breaks Doclava for some reason
       <fileset dir="${struts2.src.dir}"/> -->

@@ -280,6 +286,7 @@
<ant dir="extensions/multibindings" antfile="build.xml" target="clean"/>
     <ant dir="extensions/persist" antfile="build.xml" target="clean"/>
     <ant dir="extensions/grapher" antfile="build.xml" target="clean"/>
+    <ant dir="extensions/testlib" antfile="build.xml" target="clean"/>
   </target>

 </project>
=======================================
--- /extensions/pom.xml Tue Apr  8 16:26:24 2014 UTC
+++ /extensions/pom.xml Thu Apr 10 23:17:37 2014 UTC
@@ -26,6 +26,7 @@
     <module>servlet</module>
     <module>spring</module>
     <module>struts2</module>
+    <module>testlib</module>
     <module>throwingproviders</module>
 <!--
  |  not yet promoted...

==============================================================================
Revision: c35ebc2ce88f
Author:   Sam Berlin <[email protected]>
Date:     Thu Apr 10 23:18:01 2014 UTC
Log: Fix external issue 372 -- only scrub the pathInfo if the servletPath actually
began it (not if it just happens to be longer than the servlet path).
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=64723867

http://code.google.com/p/google-guice/source/detail?r=c35ebc2ce88f

Modified:
 /extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
/extensions/servlet/test/com/google/inject/servlet/ServletDefinitionPathsTest.java

=======================================
--- /extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java Thu Jun 27 00:43:11 2013 UTC +++ /extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java Thu Apr 10 23:18:01 2014 UTC
@@ -205,18 +205,19 @@
       @Override
       public String getPathInfo() {
         if (!isPathInfoComputed()) {
-          int servletPathLength = getServletPath().length();
-          pathInfo = getRequestURI().substring(getContextPath().length())
-              .replaceAll("[/]{2,}", "/");
-          pathInfo = pathInfo.length() > servletPathLength
-              ? pathInfo.substring(servletPathLength) : null;
-
- // Corner case: when servlet path and request path match exactly (without trailing '/'),
-          // then pathinfo is null
-          if ("".equals(pathInfo) && servletPathLength != 0) {
-            pathInfo = null;
+          String servletPath = getServletPath();
+          int servletPathLength = servletPath.length();
+          String requestUri = getRequestURI();
+ pathInfo = requestUri.substring(getContextPath().length()).replaceAll("[/]{2,}", "/"); + // See: http://code.google.com/p/google-guice/issues/detail?id=372
+          if (pathInfo.startsWith(servletPath)) {
+            pathInfo = pathInfo.substring(servletPathLength);
+ // Corner case: when servlet path & request path match exactly (without trailing '/'),
+            // then pathinfo is null.
+            if (pathInfo.isEmpty() && servletPathLength > 0) {
+              pathInfo = null;
+            }
           }
-
           pathInfoComputed = true;
         }

=======================================
--- /extensions/servlet/test/com/google/inject/servlet/ServletDefinitionPathsTest.java Fri Jul 8 00:34:16 2011 UTC +++ /extensions/servlet/test/com/google/inject/servlet/ServletDefinitionPathsTest.java Thu Apr 10 23:18:01 2014 UTC
@@ -124,6 +124,10 @@
         "/thing");
pathInfoWithServletStyleMatching("/path/thing", "/path", "/thing/*", null, "/thing");

+    // see external issue 372
+ pathInfoWithServletStyleMatching("/path/some/path/of.jsp", "/path", "/thing/*",
+        "/some/path/of.jsp", "/some/path/of.jsp");
+
     // *.xx style mapping
pathInfoWithServletStyleMatching("/path/thing.thing", "/path", "*.thing", null, "/thing.thing"); pathInfoWithServletStyleMatching("/path///h.thing", "/path", "*.thing", null, "/h.thing");

==============================================================================
Revision: 647b24f7f1b5
Author:   Sam Berlin <[email protected]>
Date:     Thu Apr 10 23:18:26 2014 UTC
Log:      Fix external issue 755.  Grapher config was wrong.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=64724255

http://code.google.com/p/google-guice/source/detail?r=647b24f7f1b5

Modified:
/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizGrapher.java

=======================================
--- /extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizGrapher.java Thu May 16 14:56:20 2013 UTC +++ /extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizGrapher.java Thu Apr 10 23:18:26 2014 UTC
@@ -116,7 +116,7 @@

     attrs.put("label", getNodeLabel(node));
     // remove most of the margin because the table has internal padding
-    attrs.put("margin", "0.02,0");
+    attrs.put("margin", "\"0.02,0\"");
     attrs.put("shape", node.getShape().toString());
     attrs.put("style", node.getStyle().toString());


==============================================================================
Revision: f1abba38c7f5
Author:   Sam Berlin <[email protected]>
Date:     Thu Apr 10 23:18:51 2014 UTC
Log: On second thought, according to the javadocs @ http://docs.oracle.com/javaee/5/api/javax/servlet/http/HttpServletRequest.html#getPathInfo() .. this is probably more correct, and still better than what it was before.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=64726165

http://code.google.com/p/google-guice/source/detail?r=f1abba38c7f5

Modified:
 /extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
/extensions/servlet/test/com/google/inject/servlet/ServletDefinitionPathsTest.java

=======================================
--- /extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java Thu Apr 10 23:18:01 2014 UTC +++ /extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java Thu Apr 10 23:18:51 2014 UTC
@@ -217,6 +217,8 @@
             if (pathInfo.isEmpty() && servletPathLength > 0) {
               pathInfo = null;
             }
+          } else {
+            pathInfo = null; // we know nothing additional about the URI.
           }
           pathInfoComputed = true;
         }
=======================================
--- /extensions/servlet/test/com/google/inject/servlet/ServletDefinitionPathsTest.java Thu Apr 10 23:18:01 2014 UTC +++ /extensions/servlet/test/com/google/inject/servlet/ServletDefinitionPathsTest.java Thu Apr 10 23:18:51 2014 UTC
@@ -126,7 +126,7 @@

     // see external issue 372
pathInfoWithServletStyleMatching("/path/some/path/of.jsp", "/path", "/thing/*",
-        "/some/path/of.jsp", "/some/path/of.jsp");
+        null, "/some/path/of.jsp");

     // *.xx style mapping
pathInfoWithServletStyleMatching("/path/thing.thing", "/path", "*.thing", null, "/thing.thing");

--
You received this message because you are subscribed to the Google Groups 
"google-guice-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-guice-dev.
For more options, visit https://groups.google.com/d/optout.

Reply via email to