• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject.internal;
18 
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.ImmutableSet;
21 import com.google.common.collect.Lists;
22 import com.google.common.collect.Ordering;
23 import com.google.common.primitives.Primitives;
24 import com.google.inject.Binding;
25 import com.google.inject.ConfigurationException;
26 import com.google.inject.CreationException;
27 import com.google.inject.Injector;
28 import com.google.inject.Key;
29 import com.google.inject.ProvisionException;
30 import com.google.inject.Scope;
31 import com.google.inject.TypeLiteral;
32 import com.google.inject.internal.util.SourceProvider;
33 import com.google.inject.spi.ElementSource;
34 import com.google.inject.spi.Message;
35 import com.google.inject.spi.ScopeBinding;
36 import com.google.inject.spi.TypeConverterBinding;
37 import com.google.inject.spi.TypeListenerBinding;
38 import java.io.Serializable;
39 import java.lang.annotation.Annotation;
40 import java.lang.reflect.Constructor;
41 import java.lang.reflect.Field;
42 import java.lang.reflect.Member;
43 import java.lang.reflect.Method;
44 import java.lang.reflect.Type;
45 import java.util.ArrayList;
46 import java.util.Collection;
47 import java.util.Formatter;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Set;
51 
52 /**
53  * A collection of error messages. If this type is passed as a method parameter, the method is
54  * considered to have executed successfully only if new errors were not added to this collection.
55  *
56  * <p>Errors can be chained to provide additional context. To add context, call {@link #withSource}
57  * to create a new Errors instance that contains additional context. All messages added to the
58  * returned instance will contain full context.
59  *
60  * <p>To avoid messages with redundant context, {@link #withSource} should be added sparingly. A
61  * good rule of thumb is to assume a method's caller has already specified enough context to
62  * identify that method. When calling a method that's defined in a different context, call that
63  * method with an errors object that includes its context.
64  *
65  * @author jessewilson@google.com (Jesse Wilson)
66  */
67 public final class Errors implements Serializable {
68 
69   /** When a binding is not found, show at most this many bindings with the same type */
70   private static final int MAX_MATCHING_TYPES_REPORTED = 3;
71 
72   /** When a binding is not found, show at most this many bindings that have some similarities */
73   private static final int MAX_RELATED_TYPES_REPORTED = 3;
74 
75   /**
76    * Throws a ConfigurationException with an NullPointerExceptions as the cause if the given
77    * reference is {@code null}.
78    */
checkNotNull(T reference, String name)79   static <T> T checkNotNull(T reference, String name) {
80     if (reference != null) {
81       return reference;
82     }
83 
84     NullPointerException npe = new NullPointerException(name);
85     throw new ConfigurationException(ImmutableSet.of(new Message(npe.toString(), npe)));
86   }
87 
88   /**
89    * Throws a ConfigurationException with a formatted {@link Message} if this condition is {@code
90    * false}.
91    */
checkConfiguration(boolean condition, String format, Object... args)92   static void checkConfiguration(boolean condition, String format, Object... args) {
93     if (condition) {
94       return;
95     }
96 
97     throw new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
98   }
99 
100   /**
101    * If the key is unknown and it is one of these types, it generally means there is a missing
102    * annotation.
103    */
104   private static final ImmutableSet<Class<?>> COMMON_AMBIGUOUS_TYPES =
105       ImmutableSet.<Class<?>>builder()
106           .add(Object.class)
107           .add(String.class)
108           .addAll(Primitives.allWrapperTypes())
109           .build();
110 
111   /** The root errors object. Used to access the list of error messages. */
112   private final Errors root;
113 
114   /** The parent errors object. Used to obtain the chain of source objects. */
115   private final Errors parent;
116 
117   /** The leaf source for errors added here. */
118   private final Object source;
119 
120   /** null unless (root == this) and error messages exist. Never an empty list. */
121   private List<Message> errors; // lazy, use getErrorsForAdd()
122 
Errors()123   public Errors() {
124     this.root = this;
125     this.parent = null;
126     this.source = SourceProvider.UNKNOWN_SOURCE;
127   }
128 
Errors(Object source)129   public Errors(Object source) {
130     this.root = this;
131     this.parent = null;
132     this.source = source;
133   }
134 
Errors(Errors parent, Object source)135   private Errors(Errors parent, Object source) {
136     this.root = parent.root;
137     this.parent = parent;
138     this.source = source;
139   }
140 
141   /** Returns an instance that uses {@code source} as a reference point for newly added errors. */
withSource(Object source)142   public Errors withSource(Object source) {
143     return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
144         ? this
145         : new Errors(this, source);
146   }
147 
148   /**
149    * We use a fairly generic error message here. The motivation is to share the same message for
150    * both bind time errors:
151    *
152    * <pre><code>Guice.createInjector(new AbstractModule() {
153    *   public void configure() {
154    *     bind(Runnable.class);
155    *   }
156    * }</code></pre>
157    *
158    * ...and at provide-time errors:
159    *
160    * <pre><code>Guice.createInjector().getInstance(Runnable.class);</code></pre>
161    *
162    * Otherwise we need to know who's calling when resolving a just-in-time binding, which makes
163    * things unnecessarily complex.
164    */
missingImplementation(Key key)165   public Errors missingImplementation(Key key) {
166     return addMessage("No implementation for %s was bound.", key);
167   }
168 
169   /** Within guice's core, allow for better missing binding messages */
missingImplementationWithHint(Key<T> key, Injector injector)170   <T> Errors missingImplementationWithHint(Key<T> key, Injector injector) {
171     StringBuilder sb = new StringBuilder();
172 
173     sb.append(format("No implementation for %s was bound.", key));
174 
175     // Keys which have similar strings as the desired key
176     List<String> possibleMatches = new ArrayList<>();
177 
178     // Check for other keys that may have the same type,
179     // but not the same annotation
180     TypeLiteral<T> type = key.getTypeLiteral();
181     List<Binding<T>> sameTypes = injector.findBindingsByType(type);
182     if (!sameTypes.isEmpty()) {
183       sb.append(format("%n  Did you mean?"));
184       int howMany = Math.min(sameTypes.size(), MAX_MATCHING_TYPES_REPORTED);
185       for (int i = 0; i < howMany; ++i) {
186         // TODO: Look into a better way to prioritize suggestions. For example, possbily
187         // use levenshtein distance of the given annotation vs actual annotation.
188         sb.append(format("%n    * %s", sameTypes.get(i).getKey()));
189       }
190       int remaining = sameTypes.size() - MAX_MATCHING_TYPES_REPORTED;
191       if (remaining > 0) {
192         String plural = (remaining == 1) ? "" : "s";
193         sb.append(format("%n    %d more binding%s with other annotations.", remaining, plural));
194       }
195     } else {
196       // For now, do a simple substring search for possibilities. This can help spot
197       // issues when there are generics being used (such as a wrapper class) and the
198       // user has forgotten they need to bind based on the wrapper, not the underlying
199       // class. In the future, consider doing a strict in-depth type search.
200       // TODO: Look into a better way to prioritize suggestions. For example, possbily
201       // use levenshtein distance of the type literal strings.
202       String want = type.toString();
203       Map<Key<?>, Binding<?>> bindingMap = injector.getAllBindings();
204       for (Key<?> bindingKey : bindingMap.keySet()) {
205         String have = bindingKey.getTypeLiteral().toString();
206         if (have.contains(want) || want.contains(have)) {
207           Formatter fmt = new Formatter();
208           Messages.formatSource(fmt, bindingMap.get(bindingKey).getSource());
209           String match = String.format("%s bound%s", convert(bindingKey), fmt.toString());
210           possibleMatches.add(match);
211           // TODO: Consider a check that if there are more than some number of results,
212           // don't suggest any.
213           if (possibleMatches.size() > MAX_RELATED_TYPES_REPORTED) {
214             // Early exit if we have found more than we need.
215             break;
216           }
217         }
218       }
219 
220       if ((possibleMatches.size() > 0) && (possibleMatches.size() <= MAX_RELATED_TYPES_REPORTED)) {
221         sb.append(format("%n  Did you mean?"));
222         for (String possibleMatch : possibleMatches) {
223           sb.append(format("%n    %s", possibleMatch));
224         }
225       }
226     }
227 
228     // If where are no possibilities to suggest, then handle the case of missing
229     // annotations on simple types. This is usually a bad idea.
230     if (sameTypes.isEmpty()
231         && possibleMatches.isEmpty()
232         && key.getAnnotation() == null
233         && COMMON_AMBIGUOUS_TYPES.contains(key.getTypeLiteral().getRawType())) {
234       // We don't recommend using such simple types without annotations.
235       sb.append(format("%nThe key seems very generic, did you forget an annotation?"));
236     }
237 
238     return addMessage(sb.toString());
239   }
240 
jitDisabled(Key<?> key)241   public Errors jitDisabled(Key<?> key) {
242     return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
243   }
244 
jitDisabledInParent(Key<?> key)245   public Errors jitDisabledInParent(Key<?> key) {
246     return addMessage(
247         "Explicit bindings are required and %s would be bound in a parent injector.%n"
248             + "Please add an explicit binding for it, either in the child or the parent.",
249         key);
250   }
251 
atInjectRequired(Class clazz)252   public Errors atInjectRequired(Class clazz) {
253     return addMessage(
254         "Explicit @Inject annotations are required on constructors,"
255             + " but %s has no constructors annotated with @Inject.",
256         clazz);
257   }
258 
converterReturnedNull( String stringValue, Object source, TypeLiteral<?> type, TypeConverterBinding typeConverterBinding)259   public Errors converterReturnedNull(
260       String stringValue,
261       Object source,
262       TypeLiteral<?> type,
263       TypeConverterBinding typeConverterBinding) {
264     return addMessage(
265         "Received null converting '%s' (bound at %s) to %s%n using %s.",
266         stringValue, convert(source), type, typeConverterBinding);
267   }
268 
conversionTypeError( String stringValue, Object source, TypeLiteral<?> type, TypeConverterBinding typeConverterBinding, Object converted)269   public Errors conversionTypeError(
270       String stringValue,
271       Object source,
272       TypeLiteral<?> type,
273       TypeConverterBinding typeConverterBinding,
274       Object converted) {
275     return addMessage(
276         "Type mismatch converting '%s' (bound at %s) to %s%n"
277             + " using %s.%n"
278             + " Converter returned %s.",
279         stringValue, convert(source), type, typeConverterBinding, converted);
280   }
281 
conversionError( String stringValue, Object source, TypeLiteral<?> type, TypeConverterBinding typeConverterBinding, RuntimeException cause)282   public Errors conversionError(
283       String stringValue,
284       Object source,
285       TypeLiteral<?> type,
286       TypeConverterBinding typeConverterBinding,
287       RuntimeException cause) {
288     return errorInUserCode(
289         cause,
290         "Error converting '%s' (bound at %s) to %s%n using %s.%n Reason: %s",
291         stringValue,
292         convert(source),
293         type,
294         typeConverterBinding,
295         cause);
296   }
297 
ambiguousTypeConversion( String stringValue, Object source, TypeLiteral<?> type, TypeConverterBinding a, TypeConverterBinding b)298   public Errors ambiguousTypeConversion(
299       String stringValue,
300       Object source,
301       TypeLiteral<?> type,
302       TypeConverterBinding a,
303       TypeConverterBinding b) {
304     return addMessage(
305         "Multiple converters can convert '%s' (bound at %s) to %s:%n"
306             + " %s and%n"
307             + " %s.%n"
308             + " Please adjust your type converter configuration to avoid overlapping matches.",
309         stringValue, convert(source), type, a, b);
310   }
311 
bindingToProvider()312   public Errors bindingToProvider() {
313     return addMessage("Binding to Provider is not allowed.");
314   }
315 
notASubtype(Class<?> implementationType, Class<?> type)316   public Errors notASubtype(Class<?> implementationType, Class<?> type) {
317     return addMessage("%s doesn't extend %s.", implementationType, type);
318   }
319 
recursiveImplementationType()320   public Errors recursiveImplementationType() {
321     return addMessage("@ImplementedBy points to the same class it annotates.");
322   }
323 
recursiveProviderType()324   public Errors recursiveProviderType() {
325     return addMessage("@ProvidedBy points to the same class it annotates.");
326   }
327 
missingRuntimeRetention(Class<? extends Annotation> annotation)328   public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) {
329     return addMessage(format("Please annotate %s with @Retention(RUNTIME).", annotation));
330   }
331 
missingScopeAnnotation(Class<? extends Annotation> annotation)332   public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) {
333     return addMessage(format("Please annotate %s with @ScopeAnnotation.", annotation));
334   }
335 
optionalConstructor(Constructor constructor)336   public Errors optionalConstructor(Constructor constructor) {
337     return addMessage(
338         "%s is annotated @Inject(optional=true), but constructors cannot be optional.",
339         constructor);
340   }
341 
cannotBindToGuiceType(String simpleName)342   public Errors cannotBindToGuiceType(String simpleName) {
343     return addMessage("Binding to core guice framework type is not allowed: %s.", simpleName);
344   }
345 
scopeNotFound(Class<? extends Annotation> scopeAnnotation)346   public Errors scopeNotFound(Class<? extends Annotation> scopeAnnotation) {
347     return addMessage("No scope is bound to %s.", scopeAnnotation);
348   }
349 
scopeAnnotationOnAbstractType( Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source)350   public Errors scopeAnnotationOnAbstractType(
351       Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
352     return addMessage(
353         "%s is annotated with %s, but scope annotations are not supported "
354             + "for abstract types.%n Bound at %s.",
355         type, scopeAnnotation, convert(source));
356   }
357 
misplacedBindingAnnotation(Member member, Annotation bindingAnnotation)358   public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
359     return addMessage(
360         "%s is annotated with %s, but binding annotations should be applied "
361             + "to its parameters instead.",
362         member, bindingAnnotation);
363   }
364 
365   private static final String CONSTRUCTOR_RULES =
366       "Classes must have either one (and only one) constructor "
367           + "annotated with @Inject or a zero-argument constructor that is not private.";
368 
missingConstructor(Class<?> implementation)369   public Errors missingConstructor(Class<?> implementation) {
370     return addMessage(
371         "Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES, implementation);
372   }
373 
tooManyConstructors(Class<?> implementation)374   public Errors tooManyConstructors(Class<?> implementation) {
375     return addMessage(
376         "%s has more than one constructor annotated with @Inject. " + CONSTRUCTOR_RULES,
377         implementation);
378   }
379 
constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type)380   public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) {
381     return addMessage("%s does not define %s", type, constructor);
382   }
383 
duplicateScopes( ScopeBinding existing, Class<? extends Annotation> annotationType, Scope scope)384   public Errors duplicateScopes(
385       ScopeBinding existing, Class<? extends Annotation> annotationType, Scope scope) {
386     return addMessage(
387         "Scope %s is already bound to %s at %s.%n Cannot bind %s.",
388         existing.getScope(), annotationType, existing.getSource(), scope);
389   }
390 
voidProviderMethod()391   public Errors voidProviderMethod() {
392     return addMessage("Provider methods must return a value. Do not return void.");
393   }
394 
missingConstantValues()395   public Errors missingConstantValues() {
396     return addMessage("Missing constant value. Please call to(...).");
397   }
398 
cannotInjectInnerClass(Class<?> type)399   public Errors cannotInjectInnerClass(Class<?> type) {
400     return addMessage(
401         "Injecting into inner classes is not supported.  "
402             + "Please use a 'static' class (top-level or nested) instead of %s.",
403         type);
404   }
405 
duplicateBindingAnnotations( Member member, Class<? extends Annotation> a, Class<? extends Annotation> b)406   public Errors duplicateBindingAnnotations(
407       Member member, Class<? extends Annotation> a, Class<? extends Annotation> b) {
408     return addMessage(
409         "%s has more than one annotation annotated with @BindingAnnotation: %s and %s",
410         member, a, b);
411   }
412 
staticInjectionOnInterface(Class<?> clazz)413   public Errors staticInjectionOnInterface(Class<?> clazz) {
414     return addMessage("%s is an interface, but interfaces have no static injection points.", clazz);
415   }
416 
cannotInjectFinalField(Field field)417   public Errors cannotInjectFinalField(Field field) {
418     return addMessage("Injected field %s cannot be final.", field);
419   }
420 
cannotInjectAbstractMethod(Method method)421   public Errors cannotInjectAbstractMethod(Method method) {
422     return addMessage("Injected method %s cannot be abstract.", method);
423   }
424 
cannotInjectNonVoidMethod(Method method)425   public Errors cannotInjectNonVoidMethod(Method method) {
426     return addMessage("Injected method %s must return void.", method);
427   }
428 
cannotInjectMethodWithTypeParameters(Method method)429   public Errors cannotInjectMethodWithTypeParameters(Method method) {
430     return addMessage("Injected method %s cannot declare type parameters of its own.", method);
431   }
432 
duplicateScopeAnnotations( Class<? extends Annotation> a, Class<? extends Annotation> b)433   public Errors duplicateScopeAnnotations(
434       Class<? extends Annotation> a, Class<? extends Annotation> b) {
435     return addMessage("More than one scope annotation was found: %s and %s.", a, b);
436   }
437 
recursiveBinding()438   public Errors recursiveBinding() {
439     return addMessage("Binding points to itself.");
440   }
441 
bindingAlreadySet(Key<?> key, Object source)442   public Errors bindingAlreadySet(Key<?> key, Object source) {
443     return addMessage("A binding to %s was already configured at %s.", key, convert(source));
444   }
445 
jitBindingAlreadySet(Key<?> key)446   public Errors jitBindingAlreadySet(Key<?> key) {
447     return addMessage(
448         "A just-in-time binding to %s was already configured on a parent injector.", key);
449   }
450 
childBindingAlreadySet(Key<?> key, Set<Object> sources)451   public Errors childBindingAlreadySet(Key<?> key, Set<Object> sources) {
452     Formatter allSources = new Formatter();
453     for (Object source : sources) {
454       if (source == null) {
455         allSources.format("%n    (bound by a just-in-time binding)");
456       } else {
457         allSources.format("%n    bound at %s", source);
458       }
459     }
460     Errors errors =
461         addMessage(
462             "Unable to create binding for %s."
463                 + " It was already configured on one or more child injectors or private modules"
464                 + "%s%n"
465                 + "  If it was in a PrivateModule, did you forget to expose the binding?",
466             key, allSources.out());
467     return errors;
468   }
469 
errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t)470   public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) {
471     return addMessage(
472         "A binding to %s was already configured at %s and an error was thrown "
473             + "while checking duplicate bindings.  Error: %s",
474         key, convert(source), t);
475   }
476 
errorNotifyingTypeListener( TypeListenerBinding listener, TypeLiteral<?> type, Throwable cause)477   public Errors errorNotifyingTypeListener(
478       TypeListenerBinding listener, TypeLiteral<?> type, Throwable cause) {
479     return errorInUserCode(
480         cause,
481         "Error notifying TypeListener %s (bound at %s) of %s.%n Reason: %s",
482         listener.getListener(),
483         convert(listener.getSource()),
484         type,
485         cause);
486   }
487 
exposedButNotBound(Key<?> key)488   public Errors exposedButNotBound(Key<?> key) {
489     return addMessage("Could not expose() %s, it must be explicitly bound.", key);
490   }
491 
keyNotFullySpecified(TypeLiteral<?> typeLiteral)492   public Errors keyNotFullySpecified(TypeLiteral<?> typeLiteral) {
493     return addMessage("%s cannot be used as a key; It is not fully specified.", typeLiteral);
494   }
495 
errorEnhancingClass(Class<?> clazz, Throwable cause)496   public Errors errorEnhancingClass(Class<?> clazz, Throwable cause) {
497     return errorInUserCode(cause, "Unable to method intercept: %s", clazz);
498   }
499 
getMessagesFromThrowable(Throwable throwable)500   public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
501     if (throwable instanceof ProvisionException) {
502       return ((ProvisionException) throwable).getErrorMessages();
503     } else if (throwable instanceof ConfigurationException) {
504       return ((ConfigurationException) throwable).getErrorMessages();
505     } else if (throwable instanceof CreationException) {
506       return ((CreationException) throwable).getErrorMessages();
507     } else {
508       return ImmutableSet.of();
509     }
510   }
511 
errorInUserCode(Throwable cause, String messageFormat, Object... arguments)512   public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
513     Collection<Message> messages = getMessagesFromThrowable(cause);
514 
515     if (!messages.isEmpty()) {
516       return merge(messages);
517     } else {
518       return addMessage(cause, messageFormat, arguments);
519     }
520   }
521 
cannotInjectRawProvider()522   public Errors cannotInjectRawProvider() {
523     return addMessage("Cannot inject a Provider that has no type parameter");
524   }
525 
cannotInjectRawMembersInjector()526   public Errors cannotInjectRawMembersInjector() {
527     return addMessage("Cannot inject a MembersInjector that has no type parameter");
528   }
529 
cannotInjectTypeLiteralOf(Type unsupportedType)530   public Errors cannotInjectTypeLiteralOf(Type unsupportedType) {
531     return addMessage("Cannot inject a TypeLiteral of %s", unsupportedType);
532   }
533 
cannotInjectRawTypeLiteral()534   public Errors cannotInjectRawTypeLiteral() {
535     return addMessage("Cannot inject a TypeLiteral that has no type parameter");
536   }
537 
throwCreationExceptionIfErrorsExist()538   public void throwCreationExceptionIfErrorsExist() {
539     if (!hasErrors()) {
540       return;
541     }
542 
543     throw new CreationException(getMessages());
544   }
545 
throwConfigurationExceptionIfErrorsExist()546   public void throwConfigurationExceptionIfErrorsExist() {
547     if (!hasErrors()) {
548       return;
549     }
550 
551     throw new ConfigurationException(getMessages());
552   }
553 
554   // Guice no longer calls this, but external callers do
throwProvisionExceptionIfErrorsExist()555   public void throwProvisionExceptionIfErrorsExist() {
556     if (!hasErrors()) {
557       return;
558     }
559 
560     throw new ProvisionException(getMessages());
561   }
562 
merge(Collection<Message> messages)563   public Errors merge(Collection<Message> messages) {
564     List<Object> sources = getSources();
565     for (Message message : messages) {
566       addMessage(Messages.mergeSources(sources, message));
567     }
568     return this;
569   }
570 
merge(Errors moreErrors)571   public Errors merge(Errors moreErrors) {
572     if (moreErrors.root == root || moreErrors.root.errors == null) {
573       return this;
574     }
575 
576     merge(moreErrors.root.errors);
577     return this;
578   }
579 
merge(InternalProvisionException ipe)580   public Errors merge(InternalProvisionException ipe) {
581     merge(ipe.getErrors());
582     return this;
583   }
584 
getSources()585   private List<Object> getSources() {
586     List<Object> sources = Lists.newArrayList();
587     for (Errors e = this; e != null; e = e.parent) {
588       if (e.source != SourceProvider.UNKNOWN_SOURCE) {
589         sources.add(0, e.source);
590       }
591     }
592     return sources;
593   }
594 
throwIfNewErrors(int expectedSize)595   public void throwIfNewErrors(int expectedSize) throws ErrorsException {
596     if (size() == expectedSize) {
597       return;
598     }
599 
600     throw toException();
601   }
602 
toException()603   public ErrorsException toException() {
604     return new ErrorsException(this);
605   }
606 
hasErrors()607   public boolean hasErrors() {
608     return root.errors != null;
609   }
610 
addMessage(String messageFormat, Object... arguments)611   public Errors addMessage(String messageFormat, Object... arguments) {
612     return addMessage(null, messageFormat, arguments);
613   }
614 
addMessage(Throwable cause, String messageFormat, Object... arguments)615   private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
616     addMessage(Messages.create(cause, getSources(), messageFormat, arguments));
617     return this;
618   }
619 
addMessage(Message message)620   public Errors addMessage(Message message) {
621     if (root.errors == null) {
622       root.errors = Lists.newArrayList();
623     }
624     root.errors.add(message);
625     return this;
626   }
627 
628   // TODO(lukes): inline into callers
format(String messageFormat, Object... arguments)629   public static String format(String messageFormat, Object... arguments) {
630     return Messages.format(messageFormat, arguments);
631   }
632 
getMessages()633   public List<Message> getMessages() {
634     if (root.errors == null) {
635       return ImmutableList.of();
636     }
637 
638     return new Ordering<Message>() {
639       @Override
640       public int compare(Message a, Message b) {
641         return a.getSource().compareTo(b.getSource());
642       }
643     }.sortedCopy(root.errors);
644   }
645 
646   public int size() {
647     return root.errors == null ? 0 : root.errors.size();
648   }
649 
650   // TODO(lukes): inline in callers.  There are some callers outside of guice, so this is difficult
651   public static Object convert(Object o) {
652     return Messages.convert(o);
653   }
654 
655   // TODO(lukes): inline in callers.  There are some callers outside of guice, so this is difficult
656   public static Object convert(Object o, ElementSource source) {
657     return Messages.convert(o, source);
658   }
659 
660   // TODO(lukes): inline in callers.  There are some callers outside of guice, so this is difficult
661   public static void formatSource(Formatter formatter, Object source) {
662     Messages.formatSource(formatter, source);
663   }
664 
665 }
666