/* * Copyright (C) 2014 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen; import com.google.auto.value.AutoValue; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; import javax.annotation.processing.Messager; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.util.SimpleElementVisitor6; import javax.tools.Diagnostic; import javax.tools.Diagnostic.Kind; import static javax.tools.Diagnostic.Kind.ERROR; import static javax.tools.Diagnostic.Kind.NOTE; import static javax.tools.Diagnostic.Kind.WARNING; /** * A collection of items describing contractual issues with the code as presented to an annotation * processor. A "clean" report (i.e. with no issues) is a report with no {@linkplain Item items} * and clean subreports. Callers will typically print the results of the report to a * {@link Messager} instance using {@link #printMessagesTo}. * *

A report describes a subject {@link Element}. Callers may choose to add report items about * other elements that are contained within or related to the subject. Since {@link Diagnostic} * reporting is expected to be associated with elements that are currently being compiled, * {@link #printMessagesTo(Messager)} will only associate messages with non-subject elements if they * are contained within the subject. Otherwise, they will be associated with the subject and contain * a reference to the item's element in the message string. It is the responsibility of the caller * to choose subjects that are part of the compilation. * * @author Gregory Kick * @since 2.0 */ @AutoValue abstract class ValidationReport { abstract T subject(); abstract ImmutableSet items(); abstract ImmutableSet> subreports(); boolean isClean() { for (Item item : items()) { switch (item.kind()) { case ERROR: return false; default: break; } } for (ValidationReport subreport : subreports()) { if (!subreport.isClean()) { return false; } } return true; } void printMessagesTo(Messager messager) { for (Item item : items()) { if (isEnclosedIn(subject(), item.element())) { if (item.annotation().isPresent()) { messager.printMessage( item.kind(), item.message(), item.element(), item.annotation().get()); } else { messager.printMessage(item.kind(), item.message(), item.element()); } } else { String message = String.format("[%s] %s", elementString(item.element()), item.message()); if (item.annotation().isPresent()) { messager.printMessage(item.kind(), message, subject(), item.annotation().get()); } else { messager.printMessage(item.kind(), message, subject()); } } } for (ValidationReport subreport : subreports()) { subreport.printMessagesTo(messager); } } private static String elementString(Element element) { return element.accept( new SimpleElementVisitor6() { @Override protected String defaultAction(Element e, Void p) { return e.toString(); } @Override public String visitExecutable(ExecutableElement e, Void p) { return e.getEnclosingElement().accept(this, null) + '.' + e.toString(); } }, null); } private static boolean isEnclosedIn(Element parent, Element child) { Element current = child; while (current != null) { if (current.equals(parent)) { return true; } current = current.getEnclosingElement(); } return false; } @AutoValue static abstract class Item { abstract String message(); abstract Kind kind(); abstract Element element(); abstract Optional annotation(); } static Builder about(T subject) { return new Builder(subject); } static final class Builder { private final T subject; private final ImmutableSet.Builder items = ImmutableSet.builder(); private final ImmutableSet.Builder> subreports = ImmutableSet.builder(); private Builder(T subject) { this.subject = subject; } T getSubject() { return subject; } Builder addItems(Iterable newItems) { items.addAll(newItems); return this; } Builder addError(String message) { addItem(message, ERROR, subject, Optional.absent()); return this; } Builder addError(String message, Element element) { addItem(message, ERROR, element, Optional.absent()); return this; } Builder addError(String message, Element element, AnnotationMirror annotation) { addItem(message, ERROR, element, Optional.of(annotation)); return this; } Builder addWarning(String message) { addItem(message, WARNING, subject, Optional.absent()); return this; } Builder addWarning(String message, Element element) { addItem(message, WARNING, element, Optional.absent()); return this; } Builder addWarning(String message, Element element, AnnotationMirror annotation) { addItem(message, WARNING, element, Optional.of(annotation)); return this; } Builder addNote(String message) { addItem(message, NOTE, subject, Optional.absent()); return this; } Builder addNote(String message, Element element) { addItem(message, NOTE, element, Optional.absent()); return this; } Builder addNote(String message, Element element, AnnotationMirror annotation) { addItem(message, NOTE, element, Optional.of(annotation)); return this; } Builder addItem(String message, Kind kind, Element element) { addItem(message, kind, element, Optional.absent()); return this; } Builder addItem(String message, Kind kind, Element element, AnnotationMirror annotation) { addItem(message, kind, element, Optional.of(annotation)); return this; } private Builder addItem(String message, Kind kind, Element element, Optional annotation) { items.add(new AutoValue_ValidationReport_Item(message, kind, element, annotation)); return this; } Builder addSubreport(ValidationReport subreport) { subreports.add(subreport); return this; } ValidationReport build() { return new AutoValue_ValidationReport(subject, items.build(), subreports.build()); } } }