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