com.google.inject.internal.Errors Maven / Gradle / Ivy
/*
* 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.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Ordering;
import com.google.inject.Binding;
import com.google.inject.ConfigurationException;
import com.google.inject.CreationException;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.ProvisionException;
import com.google.inject.Scope;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.SourceProvider;
import com.google.inject.spi.ElementSource;
import com.google.inject.spi.InterceptorBinding;
import com.google.inject.spi.Message;
import com.google.inject.spi.ScopeBinding;
import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.spi.TypeListenerBinding;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A collection of error messages. If this type is passed as a method parameter, the method is
* considered to have executed successfully only if new errors were not added to this collection.
*
* Errors can be chained to provide additional context. To add context, call {@link #withSource}
* to create a new Errors instance that contains additional context. All messages added to the
* returned instance will contain full context.
*
*
To avoid messages with redundant context, {@link #withSource} should be added sparingly. A
* good rule of thumb is to assume a method's caller has already specified enough context to
* identify that method. When calling a method that's defined in a different context, call that
* method with an errors object that includes its context.
*
* @author [email protected] (Jesse Wilson)
*/
public final class Errors implements Serializable {
/**
* Throws a ConfigurationException with an NullPointerExceptions as the cause if the given
* reference is {@code null}.
*/
static T checkNotNull(T reference, String name) {
if (reference != null) {
return reference;
}
NullPointerException npe = new NullPointerException(name);
throw new ConfigurationException(ImmutableSet.of(new Message(npe.toString(), npe)));
}
/**
* Throws a ConfigurationException with a formatted {@link Message} if this condition is {@code
* false}.
*/
static void checkConfiguration(boolean condition, String format, Object... args) {
if (condition) {
return;
}
throw new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
}
/** The root errors object. Used to access the list of error messages. */
private final Errors root;
/** The parent errors object. Used to obtain the chain of source objects. */
private final Errors parent;
/** The leaf source for errors added here. */
private final Object source;
/** null unless (root == this) and error messages exist. Never an empty list. */
private List errors; // lazy, use getErrorsForAdd()
public Errors() {
this.root = this;
this.parent = null;
this.source = SourceProvider.UNKNOWN_SOURCE;
}
public Errors(Object source) {
this.root = this;
this.parent = null;
this.source = source;
}
private Errors(Errors parent, Object source) {
this.root = parent.root;
this.parent = parent;
this.source = source;
}
/** Returns an instance that uses {@code source} as a reference point for newly added errors. */
public Errors withSource(Object source) {
return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
? this
: new Errors(this, source);
}
public Errors aopDisabled(InterceptorBinding binding) {
return addMessage(
ErrorId.AOP_DISABLED,
"Binding interceptor is not supported when bytecode generation is disabled. \nInterceptor"
+ " bound at: %s",
binding.getSource());
}
/**
* We use a fairly generic error message here. The motivation is to share the same message for
* both bind time errors:
*
* Guice.createInjector(new AbstractModule() {
* public void configure() {
* bind(Runnable.class);
* }
* }
*
* ...and at provide-time errors:
*
* Guice.createInjector().getInstance(Runnable.class);
*
* Otherwise we need to know who's calling when resolving a just-in-time binding, which makes
* things unnecessarily complex.
*/
public Errors missingImplementation(Key> key) {
return addMessage(ErrorId.MISSING_IMPLEMENTATION, "No implementation for %s was bound.", key);
}
/** Within guice's core, allow for better missing binding messages */
Errors missingImplementationWithHint(Key key, Injector injector) {
MissingImplementationError error =
new MissingImplementationError(key, injector, getSources());
return addMessage(
new Message(GuiceInternal.GUICE_INTERNAL, ErrorId.MISSING_IMPLEMENTATION, error));
}
public Errors jitDisabled(Key> key) {
return addMessage(
ErrorId.JIT_DISABLED,
"Explicit bindings are required and %s is not explicitly bound.",
key);
}
public Errors jitDisabledInParent(Key> key) {
return addMessage(
ErrorId.JIT_DISABLED_IN_PARENT,
"Explicit bindings are required and %s would be bound in a parent injector.\n"
+ "Please add an explicit binding for it, either in the child or the parent.",
key);
}
public Errors atInjectRequired(TypeLiteral> type) {
return addMessage(
new Message(
GuiceInternal.GUICE_INTERNAL,
ErrorId.MISSING_CONSTRUCTOR,
new MissingConstructorError(type, /* atInjectRequired= */ true, getSources())));
}
public Errors converterReturnedNull(
String stringValue,
Object source,
TypeLiteral> type,
TypeConverterBinding typeConverterBinding) {
return addMessage(
ErrorId.CONVERTER_RETURNED_NULL,
"Received null converting '%s' (bound at %s) to %s\n using %s.",
stringValue,
convert(source),
type,
typeConverterBinding);
}
public Errors conversionTypeError(
String stringValue,
Object source,
TypeLiteral> type,
TypeConverterBinding typeConverterBinding,
Object converted) {
return addMessage(
ErrorId.CONVERSION_TYPE_ERROR,
"Type mismatch converting '%s' (bound at %s) to %s\n"
+ " using %s.\n"
+ " Converter returned %s.",
stringValue,
convert(source),
type,
typeConverterBinding,
converted);
}
public Errors conversionError(
String stringValue,
Object source,
TypeLiteral> type,
TypeConverterBinding typeConverterBinding,
RuntimeException cause) {
return errorInUserCode(
cause,
"Error converting '%s' (bound at %s) to %s\n using %s.\n Reason: %s",
stringValue,
convert(source),
type,
typeConverterBinding,
cause);
}
public Errors ambiguousTypeConversion(
String stringValue,
Object source,
TypeLiteral> type,
TypeConverterBinding a,
TypeConverterBinding b) {
return addMessage(
ErrorId.AMBIGUOUS_TYPE_CONVERSION,
"Multiple converters can convert '%s' (bound at %s) to %s:\n"
+ " %s and\n"
+ " %s.\n"
+ " Please adjust your type converter configuration to avoid overlapping matches.",
stringValue,
convert(source),
type,
a,
b);
}
public Errors bindingToProvider() {
return addMessage(ErrorId.BINDING_TO_PROVIDER, "Binding to Provider is not allowed.");
}
public Errors notASubtype(Class> implementationType, Class> type) {
return addMessage(ErrorId.NOT_A_SUBTYPE, "%s doesn't extend %s.", implementationType, type);
}
public Errors recursiveImplementationType() {
return addMessage(
ErrorId.RECURSIVE_IMPLEMENTATION_TYPE,
"@ImplementedBy points to the same class it annotates.");
}
public Errors recursiveProviderType() {
return addMessage(
ErrorId.RECURSIVE_PROVIDER_TYPE, "@ProvidedBy points to the same class it annotates.");
}
public Errors missingRuntimeRetention(Class extends Annotation> annotation) {
return addMessage(
ErrorId.MISSING_RUNTIME_RETENTION,
format("Please annotate %s with @Retention(RUNTIME).", annotation));
}
public Errors missingScopeAnnotation(Class extends Annotation> annotation) {
return addMessage(
ErrorId.MISSING_SCOPE_ANNOTATION,
format("Please annotate %s with @ScopeAnnotation.", annotation));
}
public Errors optionalConstructor(Constructor> constructor) {
return addMessage(
ErrorId.OPTIONAL_CONSTRUCTOR,
"%s is annotated @Inject(optional=true), but constructors cannot be optional.",
constructor);
}
public Errors cannotBindToGuiceType(String simpleName) {
return addMessage(
ErrorId.BINDING_TO_GUICE_TYPE,
"Binding to core guice framework type is not allowed: %s.",
simpleName);
}
public Errors scopeNotFound(Class extends Annotation> scopeAnnotation) {
return addMessage(
new Message(
GuiceInternal.GUICE_INTERNAL,
ErrorId.SCOPE_NOT_FOUND,
new ScopeNotFoundError(scopeAnnotation, getSources())));
}
public Errors scopeAnnotationOnAbstractType(
Class extends Annotation> scopeAnnotation, Class> type, Object source) {
return addMessage(
ErrorId.SCOPE_ANNOTATION_ON_ABSTRACT_TYPE,
"%s is annotated with %s, but scope annotations are not supported "
+ "for abstract types.\n Bound at %s.",
type,
scopeAnnotation,
convert(source));
}
public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
return addMessage(
ErrorId.MISPLACED_BINDING_ANNOTATION,
"%s is annotated with %s, but binding annotations should be applied "
+ "to its parameters instead.",
member,
bindingAnnotation);
}
// TODO(diamondm) don't mention zero-arg constructors if requireAtInjectOnConstructors is true
private static final String CONSTRUCTOR_RULES =
"Injectable classes must have either one (and only one) constructor annotated with @Inject"
+ " or a zero-argument constructor that is not private.";
public Errors missingConstructor(TypeLiteral> type) {
return addMessage(
new Message(
GuiceInternal.GUICE_INTERNAL,
ErrorId.MISSING_CONSTRUCTOR,
new MissingConstructorError(type, /* atInjectRequired= */ false, getSources())));
}
public Errors tooManyConstructors(Class> implementation) {
return addMessage(
ErrorId.TOO_MANY_CONSTRUCTORS,
"%s has more than one constructor annotated with @Inject. %s",
implementation,
CONSTRUCTOR_RULES);
}
public Errors constructorNotDefinedByType(Constructor> constructor, TypeLiteral> type) {
return addMessage(
ErrorId.CONSTRUCTOR_NOT_DEFINED_BY_TYPE, "%s does not define %s", type, constructor);
}
public Errors duplicateMapKey(Key