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 java.util.stream.Collectors.joining;
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.Maps;
import com.google.inject.Key;
import com.google.inject.internal.util.Classes;
import com.google.inject.spi.ElementSource;
import com.google.inject.spi.ErrorDetail;
import com.google.inject.spi.Message;
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;
import java.util.stream.Collectors;
/** 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 message.withSource(
ImmutableList.builder().addAll(sources).addAll(messageSources).build());
}
/**
* 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;
List> remainingErrors =
errorMessages.stream().map(Message::getErrorDetail).collect(Collectors.toList());
Map, Integer> causes = Maps.newHashMap();
while (!remainingErrors.isEmpty()) {
ErrorDetail> currentError = remainingErrors.get(0);
// Split the remaining errors into 2 groups, one that contains mergeable errors with
// currentError and the other that need to be formatted separately in the next iteration.
Map>> partitionedByMergeable =
remainingErrors.subList(1, remainingErrors.size()).stream()
.collect(Collectors.partitioningBy(currentError::isMergeable));
remainingErrors = partitionedByMergeable.get(false);
currentError.format(index, partitionedByMergeable.get(true), fmt);
Throwable cause = currentError.getCause();
if (displayCauses && cause != null) {
Equivalence.Wrapper causeEquivalence = ThrowableEquivalence.INSTANCE.wrap(cause);
if (!causes.containsKey(causeEquivalence)) {
causes.put(causeEquivalence, index);
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");
index++;
}
if (index == 2) {
fmt.format("1 error");
} else {
fmt.format("%s errors", index - 1);
}
return PackageNameCompressor.compressPackagesInMessage(fmt.toString());
}
/**
* Creates a new Message without a cause.
*
* @param errorId The enum id for the error
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(ErrorId errorId, String messageFormat, Object... arguments) {
return create(errorId, null, messageFormat, arguments);
}
/**
* Creates a new Message with the given cause.
*
* @param errorId The enum id for the error
* @param cause The exception that caused the error
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(
ErrorId errorId, Throwable cause, String messageFormat, Object... arguments) {
return create(errorId, cause, ImmutableList.of(), messageFormat, arguments);
}
/**
* Creates a new Message with the given cause and a binding source stack.
*
* @param errorId The enum id for the error
* @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(
ErrorId errorId,
Throwable cause,
List sources,
String messageFormat,
Object... arguments) {
String message = format(messageFormat, arguments);
return new Message(errorId, 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 = SourceFormatter.getModuleStack(elementSource);
if (modules.length() == 0) {
return source;
} else {
return source + " (installed by: " + modules + ")";
}
}
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()));
}
}
private enum FormatOptions {
RED("\u001B[31m"),
BOLD("\u001B[1m"),
FAINT("\u001B[2m"),
ITALIC("\u001B[3m"),
UNDERLINE("\u001B[4m"),
RESET("\u001B[0m");
private final String ansiCode;
FormatOptions(String ansiCode) {
this.ansiCode = ansiCode;
}
}
private static final String formatText(String text, FormatOptions... options) {
if (!InternalFlags.enableColorizeErrorMessages()) {
return text;
}
return String.format(
"%s%s%s",
Arrays.stream(options).map(option -> option.ansiCode).collect(joining()),
text,
FormatOptions.RESET.ansiCode);
}
public static final String bold(String text) {
return formatText(text, FormatOptions.BOLD);
}
public static final String redBold(String text) {
return formatText(text, FormatOptions.RED, FormatOptions.BOLD);
}
public static final String underline(String text) {
return formatText(text, FormatOptions.UNDERLINE);
}
public static final String faint(String text) {
return formatText(text, FormatOptions.FAINT);
}
}