/core/src/com/google/inject/internal/Errors.java
Java | 870 lines | 654 code | 139 blank | 77 comment | 109 complexity | 472e21fb2c18472475a4634e46b01636 MD5 | raw file
- /**
- * 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.base.Equivalence;
- import com.google.common.base.Objects;
- import com.google.common.base.Throwables;
- import com.google.common.collect.ImmutableList;
- import com.google.common.collect.ImmutableSet;
- import com.google.common.collect.Lists;
- import com.google.common.collect.Maps;
- import com.google.common.collect.Ordering;
- import com.google.inject.ConfigurationException;
- import com.google.inject.CreationException;
- import com.google.inject.Guice;
- import com.google.inject.Key;
- import com.google.inject.MembersInjector;
- import com.google.inject.Provider;
- import com.google.inject.Provides;
- import com.google.inject.ProvisionException;
- import com.google.inject.Scope;
- import com.google.inject.TypeLiteral;
- import com.google.inject.internal.util.Classes;
- import com.google.inject.internal.util.SourceProvider;
- import com.google.inject.internal.util.StackTraceElements;
- import com.google.inject.spi.Dependency;
- import com.google.inject.spi.ElementSource;
- import com.google.inject.spi.InjectionListener;
- import com.google.inject.spi.InjectionPoint;
- 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.Arrays;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Formatter;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- /**
- * 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.
- *
- * <p>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.
- *
- * <p>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 jessewilson@google.com (Jesse Wilson)
- */
- public final class Errors implements Serializable {
- private static final Logger logger = Logger.getLogger(Guice.class.getName());
- private static final Set<Dependency<?>> warnedDependencies =
- Collections.newSetFromMap(new ConcurrentHashMap<Dependency<?>, Boolean>());
- /**
- * 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<Message> 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);
- }
- /**
- * We use a fairly generic error message here. The motivation is to share the
- * same message for both bind time errors:
- * <pre><code>Guice.createInjector(new AbstractModule() {
- * public void configure() {
- * bind(Runnable.class);
- * }
- * }</code></pre>
- * ...and at provide-time errors:
- * <pre><code>Guice.createInjector().getInstance(Runnable.class);</code></pre>
- * 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("No implementation for %s was bound.", key);
- }
- public Errors jitDisabled(Key key) {
- return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
- }
- public Errors jitDisabledInParent(Key<?> key) {
- return addMessage(
- "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(Class clazz) {
- return addMessage(
- "Explicit @Inject annotations are required on constructors,"
- + " but %s has no constructors annotated with @Inject.",
- clazz);
- }
- public Errors converterReturnedNull(String stringValue, Object source,
- TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) {
- return addMessage("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("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("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("Binding to Provider is not allowed.");
- }
- public Errors subtypeNotProvided(Class<? extends javax.inject.Provider<?>> providerType,
- Class<?> type) {
- return addMessage("%s doesn't provide instances of %s.", providerType, type);
- }
- public Errors notASubtype(Class<?> implementationType, Class<?> type) {
- return addMessage("%s doesn't extend %s.", implementationType, type);
- }
- public Errors recursiveImplementationType() {
- return addMessage("@ImplementedBy points to the same class it annotates.");
- }
- public Errors recursiveProviderType() {
- return addMessage("@ProvidedBy points to the same class it annotates.");
- }
- public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) {
- return addMessage(format("Please annotate %s with @Retention(RUNTIME).", annotation));
- }
- public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) {
- return addMessage(format("Please annotate %s with @ScopeAnnotation.", annotation));
- }
- public Errors optionalConstructor(Constructor constructor) {
- return addMessage("%s is annotated @Inject(optional=true), "
- + "but constructors cannot be optional.", constructor);
- }
- public Errors cannotBindToGuiceType(String simpleName) {
- return addMessage("Binding to core guice framework type is not allowed: %s.", simpleName);
- }
- public Errors scopeNotFound(Class<? extends Annotation> scopeAnnotation) {
- return addMessage("No scope is bound to %s.", scopeAnnotation);
- }
- public Errors scopeAnnotationOnAbstractType(
- Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
- return addMessage("%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("%s is annotated with %s, but binding annotations should be applied "
- + "to its parameters instead.", member, bindingAnnotation);
- }
- private static final String CONSTRUCTOR_RULES =
- "Classes must have either one (and only one) constructor "
- + "annotated with @Inject or a zero-argument constructor that is not private.";
- public Errors missingConstructor(Class<?> implementation) {
- return addMessage("Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES,
- implementation);
- }
- public Errors tooManyConstructors(Class<?> implementation) {
- return addMessage("%s has more than one constructor annotated with @Inject. "
- + CONSTRUCTOR_RULES, implementation);
- }
- public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) {
- return addMessage("%s does not define %s", type, constructor);
- }
- public Errors duplicateScopes(ScopeBinding existing,
- Class<? extends Annotation> annotationType, Scope scope) {
- return addMessage("Scope %s is already bound to %s at %s.%n Cannot bind %s.",
- existing.getScope(), annotationType, existing.getSource(), scope);
- }
- public Errors voidProviderMethod() {
- return addMessage("Provider methods must return a value. Do not return void.");
- }
- public Errors missingConstantValues() {
- return addMessage("Missing constant value. Please call to(...).");
- }
- public Errors cannotInjectInnerClass(Class<?> type) {
- return addMessage("Injecting into inner classes is not supported. "
- + "Please use a 'static' class (top-level or nested) instead of %s.", type);
- }
- public Errors duplicateBindingAnnotations(Member member,
- Class<? extends Annotation> a, Class<? extends Annotation> b) {
- return addMessage("%s has more than one annotation annotated with @BindingAnnotation: "
- + "%s and %s", member, a, b);
- }
- public Errors staticInjectionOnInterface(Class<?> clazz) {
- return addMessage("%s is an interface, but interfaces have no static injection points.", clazz);
- }
- public Errors cannotInjectFinalField(Field field) {
- return addMessage("Injected field %s cannot be final.", field);
- }
- public Errors cannotInjectAbstractMethod(Method method) {
- return addMessage("Injected method %s cannot be abstract.", method);
- }
- public Errors cannotInjectNonVoidMethod(Method method) {
- return addMessage("Injected method %s must return void.", method);
- }
- public Errors cannotInjectMethodWithTypeParameters(Method method) {
- return addMessage("Injected method %s cannot declare type parameters of its own.", method);
- }
- public Errors duplicateScopeAnnotations(
- Class<? extends Annotation> a, Class<? extends Annotation> b) {
- return addMessage("More than one scope annotation was found: %s and %s.", a, b);
- }
- public Errors recursiveBinding() {
- return addMessage("Binding points to itself.");
- }
- public Errors bindingAlreadySet(Key<?> key, Object source) {
- return addMessage("A binding to %s was already configured at %s.", key, convert(source));
- }
- public Errors jitBindingAlreadySet(Key<?> key) {
- return addMessage("A just-in-time binding to %s was already configured on a parent injector.", key);
- }
- public Errors childBindingAlreadySet(Key<?> key, Set<Object> sources) {
- Formatter allSources = new Formatter();
- for (Object source : sources) {
- if (source == null) {
- allSources.format("%n (bound by a just-in-time binding)");
- } else {
- allSources.format("%n bound at %s", source);
- }
- }
- Errors errors = addMessage(
- "Unable to create binding for %s."
- + " It was already configured on one or more child injectors or private modules"
- + "%s%n"
- + " If it was in a PrivateModule, did you forget to expose the binding?",
- key, allSources.out());
- return errors;
- }
- public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) {
- return addMessage(
- "A binding to %s was already configured at %s and an error was thrown "
- + "while checking duplicate bindings. Error: %s",
- key, convert(source), t);
- }
- public Errors errorInjectingMethod(Throwable cause) {
- return errorInUserCode(cause, "Error injecting method, %s", cause);
- }
- public Errors errorNotifyingTypeListener(TypeListenerBinding listener,
- TypeLiteral<?> type, Throwable cause) {
- return errorInUserCode(cause,
- "Error notifying TypeListener %s (bound at %s) of %s.%n"
- + " Reason: %s",
- listener.getListener(), convert(listener.getSource()), type, cause);
- }
- public Errors errorInjectingConstructor(Throwable cause) {
- return errorInUserCode(cause, "Error injecting constructor, %s", cause);
- }
- public Errors errorInProvider(RuntimeException runtimeException) {
- Throwable unwrapped = unwrap(runtimeException);
- return errorInUserCode(unwrapped, "Error in custom provider, %s", unwrapped);
- }
- public Errors errorInUserInjector(
- MembersInjector<?> listener, TypeLiteral<?> type, RuntimeException cause) {
- return errorInUserCode(cause, "Error injecting %s using %s.%n"
- + " Reason: %s", type, listener, cause);
- }
- public Errors errorNotifyingInjectionListener(
- InjectionListener<?> listener, TypeLiteral<?> type, RuntimeException cause) {
- return errorInUserCode(cause, "Error notifying InjectionListener %s of %s.%n"
- + " Reason: %s", listener, type, cause);
- }
- public Errors exposedButNotBound(Key<?> key) {
- return addMessage("Could not expose() %s, it must be explicitly bound.", key);
- }
- public Errors keyNotFullySpecified(TypeLiteral<?> typeLiteral) {
- return addMessage("%s cannot be used as a key; It is not fully specified.", typeLiteral);
- }
- public Errors errorEnhancingClass(Class<?> clazz, Throwable cause) {
- return errorInUserCode(cause, "Unable to method intercept: %s", clazz);
- }
- public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
- if (throwable instanceof ProvisionException) {
- return ((ProvisionException) throwable).getErrorMessages();
- } else if (throwable instanceof ConfigurationException) {
- return ((ConfigurationException) throwable).getErrorMessages();
- } else if (throwable instanceof CreationException) {
- return ((CreationException) throwable).getErrorMessages();
- } else {
- return ImmutableSet.of();
- }
- }
- public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
- Collection<Message> messages = getMessagesFromThrowable(cause);
- if (!messages.isEmpty()) {
- return merge(messages);
- } else {
- return addMessage(cause, messageFormat, arguments);
- }
- }
- private Throwable unwrap(RuntimeException runtimeException) {
- if(runtimeException instanceof Exceptions.UnhandledCheckedUserException) {
- return runtimeException.getCause();
- } else {
- return runtimeException;
- }
- }
- public Errors cannotInjectRawProvider() {
- return addMessage("Cannot inject a Provider that has no type parameter");
- }
- public Errors cannotInjectRawMembersInjector() {
- return addMessage("Cannot inject a MembersInjector that has no type parameter");
- }
- public Errors cannotInjectTypeLiteralOf(Type unsupportedType) {
- return addMessage("Cannot inject a TypeLiteral of %s", unsupportedType);
- }
- public Errors cannotInjectRawTypeLiteral() {
- return addMessage("Cannot inject a TypeLiteral that has no type parameter");
- }
- public Errors cannotProxyClass(Class<?> expectedType) {
- return addMessage(
- "Tried proxying %s to support a circular dependency, but it is not an interface.",
- expectedType);
- }
- public Errors circularDependenciesDisabled(Class<?> expectedType) {
- return addMessage(
- "Found a circular dependency involving %s, and circular dependencies are disabled.",
- expectedType);
- }
- public void throwCreationExceptionIfErrorsExist() {
- if (!hasErrors()) {
- return;
- }
- throw new CreationException(getMessages());
- }
- public void throwConfigurationExceptionIfErrorsExist() {
- if (!hasErrors()) {
- return;
- }
- throw new ConfigurationException(getMessages());
- }
- public void throwProvisionExceptionIfErrorsExist() {
- if (!hasErrors()) {
- return;
- }
- throw new ProvisionException(getMessages());
- }
- private Message merge(Message message) {
- List<Object> sources = Lists.newArrayList();
- sources.addAll(getSources());
- sources.addAll(message.getSources());
- return new Message(sources, message.getMessage(), message.getCause());
- }
- public Errors merge(Collection<Message> messages) {
- for (Message message : messages) {
- addMessage(merge(message));
- }
- return this;
- }
- public Errors merge(Errors moreErrors) {
- if (moreErrors.root == root || moreErrors.root.errors == null) {
- return this;
- }
- merge(moreErrors.root.errors);
- return this;
- }
- public List<Object> getSources() {
- List<Object> sources = Lists.newArrayList();
- for (Errors e = this; e != null; e = e.parent) {
- if (e.source != SourceProvider.UNKNOWN_SOURCE) {
- sources.add(0, e.source);
- }
- }
- return sources;
- }
- public void throwIfNewErrors(int expectedSize) throws ErrorsException {
- if (size() == expectedSize) {
- return;
- }
- throw toException();
- }
- public ErrorsException toException() {
- return new ErrorsException(this);
- }
- public boolean hasErrors() {
- return root.errors != null;
- }
- public Errors addMessage(String messageFormat, Object... arguments) {
- return addMessage(null, messageFormat, arguments);
- }
- private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
- String message = format(messageFormat, arguments);
- addMessage(new Message(getSources(), message, cause));
- return this;
- }
- public Errors addMessage(Message message) {
- if (root.errors == null) {
- root.errors = Lists.newArrayList();
- }
- root.errors.add(message);
- return this;
- }
- public static String format(String messageFormat, Object... arguments) {
- for (int i = 0; i < arguments.length; i++) {
- arguments[i] = Errors.convert(arguments[i]);
- }
- return String.format(messageFormat, arguments);
- }
- public List<Message> getMessages() {
- if (root.errors == null) {
- return ImmutableList.of();
- }
- return new Ordering<Message>() {
- @Override
- public int compare(Message a, Message b) {
- return a.getSource().compareTo(b.getSource());
- }
- }.sortedCopy(root.errors);
- }
- /** Returns the formatted message for an exception with the specified messages. */
- public static String format(String heading, Collection<Message> errorMessages) {
- Formatter fmt = new Formatter().format(heading).format(":%n%n");
- int index = 1;
- boolean displayCauses = getOnlyCause(errorMessages) == null;
- Map<Equivalence.Wrapper<Throwable>, Integer> causes = Maps.newHashMap();
- for (Message errorMessage : errorMessages) {
- int thisIdx = index++;
- fmt.format("%s) %s%n", thisIdx, errorMessage.getMessage());
- List<Object> dependencies = errorMessage.getSources();
- for (int i = dependencies.size() - 1; i >= 0; i--) {
- Object source = dependencies.get(i);
- formatSource(fmt, source);
- }
- Throwable cause = errorMessage.getCause();
- if (displayCauses && cause != null) {
- Equivalence.Wrapper<Throwable> causeEquivalence = ThrowableEquivalence.INSTANCE.wrap(cause);
- if (!causes.containsKey(causeEquivalence)) {
- causes.put(causeEquivalence, thisIdx);
- fmt.format("Caused by: %s", Throwables.getStackTraceAsString(cause));
- } else {
- int causeIdx = causes.get(causeEquivalence);
- fmt.format("Caused by: %s (same stack trace as error #%s)",
- cause.getClass().getName(), causeIdx);
- }
- }
- fmt.format("%n");
- }
- if (errorMessages.size() == 1) {
- fmt.format("1 error");
- } else {
- fmt.format("%s errors", errorMessages.size());
- }
- return fmt.toString();
- }
- /**
- * Returns {@code value} if it is non-null allowed to be null. Otherwise a message is added and
- * an {@code ErrorsException} is thrown.
- */
- public <T> T checkForNull(T value, Object source, Dependency<?> dependency)
- throws ErrorsException {
- if (value != null || dependency.isNullable() ) {
- return value;
- }
- // Hack to allow null parameters to @Provides methods, for backwards compatibility.
- if (dependency.getInjectionPoint().getMember() instanceof Method) {
- Method annotated = (Method) dependency.getInjectionPoint().getMember();
- if (annotated.isAnnotationPresent(Provides.class)) {
- switch (InternalFlags.getNullableProvidesOption()) {
- case ERROR:
- break; // break out & let the below exception happen
- case IGNORE:
- return value; // user doesn't care about injecting nulls to non-@Nullables.
- case WARN:
- // Warn only once, otherwise we spam logs too much.
- if (!warnedDependencies.add(dependency)) {
- return value;
- }
- logger.log(Level.WARNING,
- "Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable."
- + " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
- + " error.",
- new Object[] {
- dependency.getParameterIndex(),
- convert(dependency.getInjectionPoint().getMember()),
- convert(dependency.getKey())});
- return null; // log & exit.
- }
- }
- }
- int parameterIndex = dependency.getParameterIndex();
- String parameterName = (parameterIndex != -1)
- ? "parameter " + parameterIndex + " of "
- : "";
- addMessage("null returned by binding at %s%n but %s%s is not @Nullable",
- source, parameterName, dependency.getInjectionPoint().getMember());
- throw toException();
- }
- /**
- * Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
- * zero or multiple messages with causes, null is returned.
- */
- public static Throwable getOnlyCause(Collection<Message> messages) {
- Throwable onlyCause = null;
- for (Message message : messages) {
- Throwable messageCause = message.getCause();
- if (messageCause == null) {
- continue;
- }
- if (onlyCause != null
- && !ThrowableEquivalence.INSTANCE.equivalent(onlyCause, messageCause)) {
- return null;
- }
- onlyCause = messageCause;
- }
- return onlyCause;
- }
- public int size() {
- return root.errors == null ? 0 : root.errors.size();
- }
- private static abstract class Converter<T> {
- final Class<T> type;
- Converter(Class<T> type) {
- this.type = type;
- }
- boolean appliesTo(Object o) {
- return o != null && type.isAssignableFrom(o.getClass());
- }
- String convert(Object o) {
- return toString(type.cast(o));
- }
- abstract String toString(T t);
- }
- private static final Collection<Converter<?>> converters = ImmutableList.of(
- new Converter<Class>(Class.class) {
- @Override public String toString(Class c) {
- return c.getName();
- }
- },
- new Converter<Member>(Member.class) {
- @Override public String toString(Member member) {
- return Classes.toString(member);
- }
- },
- new Converter<Key>(Key.class) {
- @Override public String toString(Key key) {
- if (key.getAnnotationType() != null) {
- return key.getTypeLiteral() + " annotated with "
- + (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
- } else {
- return key.getTypeLiteral().toString();
- }
- }
- });
- public static Object convert(Object o) {
- ElementSource source = null;
- if (o instanceof ElementSource) {
- source = (ElementSource)o;
- o = source.getDeclaringSource();
- }
- return convert(o, source);
- }
- public static Object convert(Object o, ElementSource source) {
- for (Converter<?> converter : converters) {
- if (converter.appliesTo(o)) {
- return appendModules(converter.convert(o), source);
- }
- }
- return appendModules(o, source);
- }
- private static Object appendModules(Object source, ElementSource elementSource) {
- String modules = moduleSourceString(elementSource);
- if (modules.length() == 0) {
- return source;
- } else {
- return source + modules;
- }
- }
- private static String moduleSourceString(ElementSource elementSource) {
- // if we only have one module (or don't know what they are), then don't bother
- // reporting it, because the source already is going to report exactly that module.
- if (elementSource == null) {
- return "";
- }
- List<String> modules = Lists.newArrayList(elementSource.getModuleClassNames());
- // Insert any original element sources w/ module info into the path.
- while(elementSource.getOriginalElementSource() != null) {
- elementSource = elementSource.getOriginalElementSource();
- modules.addAll(0, elementSource.getModuleClassNames());
- }
- if (modules.size() <= 1) {
- return "";
- }
- // Ideally we'd do:
- // return Joiner.on(" -> ")
- // .appendTo(new StringBuilder(" (via modules: "), Lists.reverse(modules))
- // .append(")").toString();
- // ... but for some reason we can't find Lists.reverse, so do it the boring way.
- StringBuilder builder = new StringBuilder(" (via modules: ");
- for (int i = modules.size() - 1; i >= 0; i--) {
- builder.append(modules.get(i));
- if (i != 0) {
- builder.append(" -> ");
- }
- }
- builder.append(")");
- return builder.toString();
- }
- public static void formatSource(Formatter formatter, Object source) {
- ElementSource elementSource = null;
- if (source instanceof ElementSource) {
- elementSource = (ElementSource)source;
- source = elementSource.getDeclaringSource();
- }
- formatSource(formatter, source, elementSource);
- }
- public static void formatSource(Formatter formatter, Object source, ElementSource elementSource) {
- String modules = moduleSourceString(elementSource);
- if (source instanceof Dependency) {
- Dependency<?> dependency = (Dependency<?>) source;
- InjectionPoint injectionPoint = dependency.getInjectionPoint();
- if (injectionPoint != null) {
- formatInjectionPoint(formatter, dependency, injectionPoint, elementSource);
- } else {
- formatSource(formatter, dependency.getKey(), elementSource);
- }
- } else if (source instanceof InjectionPoint) {
- formatInjectionPoint(formatter, null, (InjectionPoint) source, elementSource);
- } else if (source instanceof Class) {
- formatter.format(" at %s%s%n", StackTraceElements.forType((Class<?>) source), modules);
- } else if (source instanceof Member) {
- formatter.format(" at %s%s%n", StackTraceElements.forMember((Member) source), modules);
- } else if (source instanceof TypeLiteral) {
- formatter.format(" while locating %s%s%n", source, modules);
- } else if (source instanceof Key) {
- Key<?> key = (Key<?>) source;
- formatter.format(" while locating %s%n", convert(key, elementSource));
- } else if (source instanceof Thread) {
- formatter.format(" in thread %s%n", source);
- } else {
- formatter.format(" at %s%s%n", source, modules);
- }
- }
- public static void formatInjectionPoint(Formatter formatter, Dependency<?> dependency,
- InjectionPoint injectionPoint, ElementSource elementSource) {
- Member member = injectionPoint.getMember();
- Class<? extends Member> memberType = Classes.memberType(member);
- if (memberType == Field.class) {
- dependency = injectionPoint.getDependencies().get(0);
- formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
- formatter.format(" for field at %s%n", StackTraceElements.forMember(member));
- } else if (dependency != null) {
- formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
- formatter.format(" for parameter %s at %s%n",
- dependency.getParameterIndex(), StackTraceElements.forMember(member));
- } else {
- formatSource(formatter, injectionPoint.getMember());
- }
- }
- static class ThrowableEquivalence extends Equivalence<Throwable> {
- static final ThrowableEquivalence INSTANCE = new ThrowableEquivalence();
- @Override protected boolean doEquivalent(Throwable a, Throwable b) {
- return a.getClass().equals(b.getClass())
- && Objects.equal(a.getMessage(), b.getMessage())
- && Arrays.equals(a.getStackTrace(), b.getStackTrace())
- && equivalent(a.getCause(), b.getCause());
- }
- @Override protected int doHash(Throwable t) {
- return Objects.hashCode(t.getClass().hashCode(), t.getMessage(), hash(t.getCause()));
- }
- }
- }