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 */
16package dagger.internal.codegen;
17
18import com.google.auto.common.MoreElements;
19import com.google.common.collect.ImmutableList;
20import dagger.Module;
21import dagger.producers.ProducerModule;
22import dagger.producers.ProductionComponent;
23import javax.lang.model.element.AnnotationMirror;
24import javax.lang.model.element.TypeElement;
25import javax.lang.model.type.DeclaredType;
26import javax.lang.model.type.TypeMirror;
27import javax.lang.model.util.SimpleTypeVisitor6;
28
29import static com.google.auto.common.MoreElements.getAnnotationMirror;
30import static com.google.common.base.Preconditions.checkState;
31import static dagger.internal.codegen.ConfigurationAnnotations.getComponentModules;
32import static javax.lang.model.element.ElementKind.CLASS;
33import static javax.lang.model.element.ElementKind.INTERFACE;
34import static javax.lang.model.element.Modifier.ABSTRACT;
35
36/**
37 * Performs superficial validation of the contract of the {@link ProductionComponent} annotation.
38 *
39 * @author Jesse Beder
40 */
41final class ProductionComponentValidator {
42  ValidationReport<TypeElement> validate(final TypeElement subject) {
43    final ValidationReport.Builder<TypeElement> builder = ValidationReport.about(subject);
44
45    if (!subject.getKind().equals(INTERFACE)
46        && !(subject.getKind().equals(CLASS) && subject.getModifiers().contains(ABSTRACT))) {
47      builder.addError(
48          "@ProductionComponent may only be applied to an interface or abstract class", subject);
49    }
50
51    AnnotationMirror componentMirror =
52        getAnnotationMirror(subject, ProductionComponent.class).get();
53    ImmutableList<TypeMirror> moduleTypes = getComponentModules(componentMirror);
54
55    // TODO(gak): make unused modules an error
56    for (TypeMirror moduleType : moduleTypes) {
57      moduleType.accept(
58          new SimpleTypeVisitor6<Void, Void>() {
59            @Override
60            protected Void defaultAction(TypeMirror mirror, Void p) {
61              builder.addError(mirror + " is not a valid module type.", subject);
62              return null;
63            }
64
65            @Override
66            public Void visitDeclared(DeclaredType t, Void p) {
67              checkState(t.getTypeArguments().isEmpty());
68              TypeElement moduleElement = MoreElements.asType(t.asElement());
69              if (!getAnnotationMirror(moduleElement, Module.class).isPresent()
70                  && !getAnnotationMirror(moduleElement, ProducerModule.class).isPresent()) {
71                builder.addError(
72                    moduleElement.getQualifiedName()
73                        + " is listed as a module, but is not annotated with @Module or"
74                        + " @ProducerModule",
75                    subject);
76              }
77              return null;
78            }
79          },
80          null);
81    }
82
83    return builder.build();
84  }
85}
86