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
17package com.google.inject.spi;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20
21import com.google.common.base.Objects;
22import com.google.common.collect.ImmutableList;
23import com.google.inject.Binder;
24import com.google.inject.internal.Errors;
25import com.google.inject.internal.util.SourceProvider;
26
27import java.io.ObjectStreamException;
28import java.io.Serializable;
29import java.util.List;
30
31/**
32 * An error message and the context in which it occured. Messages are usually created internally by
33 * Guice and its extensions. Messages can be created explicitly in a module using {@link
34 * com.google.inject.Binder#addError(Throwable) addError()} statements:
35 * <pre>
36 *     try {
37 *       bindPropertiesFromFile();
38 *     } catch (IOException e) {
39 *       addError(e);
40 *     }</pre>
41 *
42 * @author crazybob@google.com (Bob Lee)
43 */
44public final class Message implements Serializable, Element {
45  private final String message;
46  private final Throwable cause;
47  private final List<Object> sources;
48
49  /**
50   * @since 2.0
51   */
52  public Message(List<Object> sources, String message, Throwable cause) {
53    this.sources = ImmutableList.copyOf(sources);
54    this.message = checkNotNull(message, "message");
55    this.cause = cause;
56  }
57
58  /**
59   * @since 4.0
60   */
61  public Message(String message, Throwable cause) {
62    this(ImmutableList.of(), message, cause);
63  }
64
65  public Message(Object source, String message) {
66    this(ImmutableList.of(source), message, null);
67  }
68
69  public Message(String message) {
70    this(ImmutableList.of(), message, null);
71  }
72
73  public String getSource() {
74    return sources.isEmpty()
75        ? SourceProvider.UNKNOWN_SOURCE.toString()
76        : Errors.convert(sources.get(sources.size() - 1)).toString();
77  }
78
79  /** @since 2.0 */
80  public List<Object> getSources() {
81    return sources;
82  }
83
84  /**
85   * Gets the error message text.
86   */
87  public String getMessage() {
88    return message;
89  }
90
91  /** @since 2.0 */
92  public <T> T acceptVisitor(ElementVisitor<T> visitor) {
93    return visitor.visit(this);
94  }
95
96  /**
97   * Returns the throwable that caused this message, or {@code null} if this
98   * message was not caused by a throwable.
99   *
100   * @since 2.0
101   */
102  public Throwable getCause() {
103    return cause;
104  }
105
106  @Override public String toString() {
107    return message;
108  }
109
110  @Override public int hashCode() {
111    return message.hashCode();
112  }
113
114  @Override public boolean equals(Object o) {
115    if (!(o instanceof Message)) {
116      return false;
117    }
118    Message e = (Message) o;
119    return message.equals(e.message) && Objects.equal(cause, e.cause) && sources.equals(e.sources);
120  }
121
122  /** @since 2.0 */
123  public void applyTo(Binder binder) {
124    binder.withSource(getSource()).addError(this);
125  }
126
127  /**
128   * When serialized, we eagerly convert sources to strings. This hurts our formatting, but it
129   * guarantees that the receiving end will be able to read the message.
130   */
131  private Object writeReplace() throws ObjectStreamException {
132    Object[] sourcesAsStrings = sources.toArray();
133    for (int i = 0; i < sourcesAsStrings.length; i++) {
134      sourcesAsStrings[i] = Errors.convert(sourcesAsStrings[i]).toString();
135    }
136    return new Message(ImmutableList.copyOf(sourcesAsStrings), message, cause);
137  }
138
139  private static final long serialVersionUID = 0;
140}
141