Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.inject.internal.Messages Maven / Gradle / Ivy
/*
* Copyright (C) 2017 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 static com.google.common.base.Preconditions.checkArgument;
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.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ElementSource;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.util.Arrays;
import java.util.Collection;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
/** Utility methods for {@link Message} objects */
public final class Messages {
private Messages() {}
/** Prepends the list of sources to the given {@link Message} */
static Message mergeSources(List sources, Message message) {
List messageSources = message.getSources();
// It is possible that the end of getSources() and the beginning of message.getSources() are
// equivalent, in this case we should drop the repeated source when joining the lists. The
// most likely scenario where this would happen is when a scoped binding throws an exception,
// due to the fact that InternalFactoryToProviderAdapter applies the binding source when
// merging errors.
if (!sources.isEmpty()
&& !messageSources.isEmpty()
&& Objects.equal(messageSources.get(0), sources.get(sources.size() - 1))) {
messageSources = messageSources.subList(1, messageSources.size());
}
return new Message(
ImmutableList.builder().addAll(sources).addAll(messageSources).build(),
message.getMessage(),
message.getCause());
}
/**
* Calls {@link String#format} after converting the arguments using some standard guice formatting
* for {@link Key}, {@link Class} and {@link Member} objects.
*/
public static String format(String messageFormat, Object... arguments) {
for (int i = 0; i < arguments.length; i++) {
arguments[i] = convert(arguments[i]);
}
return String.format(messageFormat, arguments);
}
/** Returns the formatted message for an exception with the specified messages. */
public static String formatMessages(String heading, Collection errorMessages) {
Formatter fmt = new Formatter().format(heading).format(":%n%n");
int index = 1;
boolean displayCauses = getOnlyCause(errorMessages) == null;
Map, Integer> causes = Maps.newHashMap();
for (Message errorMessage : errorMessages) {
int thisIdx = index++;
fmt.format("%s) %s%n", thisIdx, errorMessage.getMessage());
List 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 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();
}
/**
* Creates a new Message without a cause.
*
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(String messageFormat, Object... arguments) {
return create(null, messageFormat, arguments);
}
/**
* Creates a new Message with the given cause.
*
* @param cause The exception that caused the error
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(Throwable cause, String messageFormat, Object... arguments) {
return create(cause, ImmutableList.of(), messageFormat, arguments);
}
/**
* Creates a new Message with the given cause and a binding source stack.
*
* @param cause The exception that caused the error
* @param sources The binding sources for the source stack
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(
Throwable cause, List sources, String messageFormat, Object... arguments) {
String message = format(messageFormat, arguments);
return new Message(sources, message, cause);
}
/** Formats an object in a user friendly way. */
static Object convert(Object o) {
ElementSource source = null;
if (o instanceof ElementSource) {
source = (ElementSource) o;
o = source.getDeclaringSource();
}
return convert(o, source);
}
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 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();
}
static void formatSource(Formatter formatter, Object source) {
ElementSource elementSource = null;
if (source instanceof ElementSource) {
elementSource = (ElementSource) source;
source = elementSource.getDeclaringSource();
}
formatSource(formatter, source, elementSource);
}
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);
}
}
private 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 %s%n", formatParameter(dependency));
} else {
formatSource(formatter, injectionPoint.getMember());
}
}
static String formatParameter(Dependency> dependency) {
int ordinal = dependency.getParameterIndex() + 1;
return String.format(
"the %s%s parameter of %s",
ordinal,
getOrdinalSuffix(ordinal),
StackTraceElements.forMember(dependency.getInjectionPoint().getMember()));
}
/**
* Maps {@code 1} to the string {@code "1st"} ditto for all non-negative numbers
*
* @see
* https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers
*/
private static String getOrdinalSuffix(int ordinal) {
// negative ordinals don't make sense, we allow zero though because we are programmers
checkArgument(ordinal >= 0);
if ((ordinal / 10) % 10 == 1) {
// all the 'teens' are weird
return "th";
} else {
// could use a lookup table? any better?
switch (ordinal % 10) {
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
}
}
private abstract static class Converter {
final Class type;
Converter(Class 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);
}
@SuppressWarnings({"unchecked", "rawtypes"}) // rawtypes aren't avoidable
private static final Collection> converters =
ImmutableList.of(
new Converter(Class.class) {
@Override
public String toString(Class c) {
return c.getName();
}
},
new Converter(Member.class) {
@Override
public String toString(Member member) {
return Classes.toString(member);
}
},
new Converter(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();
}
}
});
/**
* 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 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;
}
private static final class ThrowableEquivalence extends Equivalence {
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()));
}
}
}