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