1 /*
2  * Copyright (C) 2014 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 package dagger.internal.codegen;
17 
18 import com.google.auto.value.AutoValue;
19 import com.google.common.base.Optional;
20 import com.google.common.collect.ImmutableSet;
21 import javax.annotation.processing.Messager;
22 import javax.lang.model.element.AnnotationMirror;
23 import javax.lang.model.element.Element;
24 import javax.lang.model.element.ExecutableElement;
25 import javax.lang.model.util.SimpleElementVisitor6;
26 import javax.tools.Diagnostic;
27 import javax.tools.Diagnostic.Kind;
28 
29 import static javax.tools.Diagnostic.Kind.ERROR;
30 import static javax.tools.Diagnostic.Kind.NOTE;
31 import static javax.tools.Diagnostic.Kind.WARNING;
32 
33 /**
34  * A collection of items describing contractual issues with the code as presented to an annotation
35  * processor.  A "clean" report (i.e. with no issues) is a report with no {@linkplain Item items}
36  * and clean subreports. Callers will typically print the results of the report to a
37  * {@link Messager} instance using {@link #printMessagesTo}.
38  *
39  * <p>A report describes a subject {@link Element}.  Callers may choose to add report items about
40  * other elements that are contained within or related to the subject. Since {@link Diagnostic}
41  * reporting is expected to be associated with elements that are currently being compiled,
42  * {@link #printMessagesTo(Messager)} will only associate messages with non-subject elements if they
43  * are contained within the subject. Otherwise, they will be associated with the subject and contain
44  * a reference to the item's element in the message string. It is the responsibility of the caller
45  * to choose subjects that are part of the compilation.
46  *
47  * @author Gregory Kick
48  * @since 2.0
49  */
50 @AutoValue
51 abstract class ValidationReport<T extends Element> {
subject()52   abstract T subject();
items()53   abstract ImmutableSet<Item> items();
subreports()54   abstract ImmutableSet<ValidationReport<?>> subreports();
55 
isClean()56   boolean isClean() {
57     for (Item item : items()) {
58       switch (item.kind()) {
59         case ERROR:
60           return false;
61         default:
62           break;
63       }
64     }
65     for (ValidationReport<?> subreport : subreports()) {
66       if (!subreport.isClean()) {
67         return false;
68       }
69     }
70     return true;
71   }
72 
printMessagesTo(Messager messager)73   void printMessagesTo(Messager messager) {
74     for (Item item : items()) {
75       if (isEnclosedIn(subject(), item.element())) {
76         if (item.annotation().isPresent()) {
77           messager.printMessage(
78               item.kind(), item.message(), item.element(), item.annotation().get());
79         } else {
80           messager.printMessage(item.kind(), item.message(), item.element());
81         }
82       } else {
83         String message = String.format("[%s] %s", elementString(item.element()), item.message());
84         if (item.annotation().isPresent()) {
85           messager.printMessage(item.kind(), message, subject(), item.annotation().get());
86         } else {
87           messager.printMessage(item.kind(), message, subject());
88         }
89       }
90     }
91     for (ValidationReport<?> subreport : subreports()) {
92       subreport.printMessagesTo(messager);
93     }
94   }
95 
elementString(Element element)96   private static String elementString(Element element) {
97     return element.accept(
98         new SimpleElementVisitor6<String, Void>() {
99           @Override
100           protected String defaultAction(Element e, Void p) {
101             return e.toString();
102           }
103 
104           @Override
105           public String visitExecutable(ExecutableElement e, Void p) {
106             return e.getEnclosingElement().accept(this, null) + '.' + e.toString();
107           }
108         },
109         null);
110   }
111 
112   private static boolean isEnclosedIn(Element parent, Element child) {
113     Element current = child;
114     while (current != null) {
115       if (current.equals(parent)) {
116         return true;
117       }
118       current = current.getEnclosingElement();
119     }
120     return false;
121   }
122 
123   @AutoValue
124   static abstract class Item {
125     abstract String message();
126     abstract Kind kind();
127     abstract Element element();
128     abstract Optional<AnnotationMirror> annotation();
129   }
130 
131   static <T extends Element> Builder<T> about(T subject) {
132     return new Builder<T>(subject);
133   }
134 
135   static final class Builder<T extends Element> {
136     private final T subject;
137     private final ImmutableSet.Builder<Item> items = ImmutableSet.builder();
138     private final ImmutableSet.Builder<ValidationReport<?>> subreports = ImmutableSet.builder();
139 
140     private Builder(T subject) {
141       this.subject = subject;
142     }
143 
144     T getSubject() {
145       return subject;
146     }
147 
148     Builder<T> addItems(Iterable<Item> newItems) {
149       items.addAll(newItems);
150       return this;
151     }
152 
153     Builder<T> addError(String message) {
154       addItem(message, ERROR, subject, Optional.<AnnotationMirror>absent());
155       return this;
156     }
157 
158     Builder<T> addError(String message, Element element) {
159       addItem(message, ERROR, element, Optional.<AnnotationMirror>absent());
160       return this;
161     }
162 
163     Builder<T> addError(String message, Element element, AnnotationMirror annotation) {
164       addItem(message, ERROR, element, Optional.of(annotation));
165       return this;
166     }
167 
168     Builder<T> addWarning(String message) {
169       addItem(message, WARNING, subject, Optional.<AnnotationMirror>absent());
170       return this;
171     }
172 
173     Builder<T> addWarning(String message, Element element) {
174       addItem(message, WARNING, element, Optional.<AnnotationMirror>absent());
175       return this;
176     }
177 
178     Builder<T> addWarning(String message, Element element, AnnotationMirror annotation) {
179       addItem(message, WARNING, element, Optional.of(annotation));
180       return this;
181     }
182 
183     Builder<T> addNote(String message) {
184       addItem(message, NOTE, subject, Optional.<AnnotationMirror>absent());
185       return this;
186     }
187 
188     Builder<T> addNote(String message, Element element) {
189       addItem(message, NOTE, element, Optional.<AnnotationMirror>absent());
190       return this;
191     }
192 
193     Builder<T> addNote(String message, Element element, AnnotationMirror annotation) {
194       addItem(message, NOTE, element, Optional.of(annotation));
195       return this;
196     }
197 
198     Builder<T> addItem(String message, Kind kind, Element element) {
199       addItem(message, kind, element, Optional.<AnnotationMirror>absent());
200       return this;
201     }
202 
203     Builder<T> addItem(String message, Kind kind, Element element, AnnotationMirror annotation) {
204       addItem(message, kind, element, Optional.of(annotation));
205       return this;
206     }
207 
208     private Builder<T> addItem(String message, Kind kind, Element element,
209         Optional<AnnotationMirror> annotation) {
210       items.add(new AutoValue_ValidationReport_Item(message, kind, element, annotation));
211       return this;
212     }
213 
214     Builder<T> addSubreport(ValidationReport<?> subreport) {
215       subreports.add(subreport);
216       return this;
217     }
218 
219     ValidationReport<T> build() {
220       return new AutoValue_ValidationReport<T>(subject, items.build(), subreports.build());
221     }
222   }
223 }
224