• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Dagger Authors.
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 
17 package dagger.internal.codegen;
18 
19 import static com.google.auto.common.MoreElements.isAnnotationPresent;
20 import static com.google.common.collect.Iterables.getOnlyElement;
21 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
22 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
23 import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
24 import static java.util.stream.Collectors.joining;
25 
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.collect.ImmutableSet;
28 import java.lang.annotation.Annotation;
29 import java.util.HashMap;
30 import java.util.Map;
31 import javax.inject.Inject;
32 import javax.lang.model.element.ExecutableElement;
33 
34 /** Validates any binding method. */
35 final class AnyBindingMethodValidator {
36 
37   private final ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> validators;
38   private final Map<ExecutableElement, ValidationReport<ExecutableElement>> reports =
39       new HashMap<>();
40 
41   @Inject
AnyBindingMethodValidator( ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> validators)42   AnyBindingMethodValidator(
43       ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> validators) {
44     this.validators = validators;
45   }
46 
47   /** Returns the binding method annotations considered by this validator. */
methodAnnotations()48   ImmutableSet<Class<? extends Annotation>> methodAnnotations() {
49     return validators.keySet();
50   }
51 
52   /**
53    * Returns {@code true} if {@code method} is annotated with at least one of {@link
54    * #methodAnnotations()}.
55    */
isBindingMethod(ExecutableElement method)56   boolean isBindingMethod(ExecutableElement method) {
57     return isAnyAnnotationPresent(method, methodAnnotations());
58   }
59 
60   /**
61    * Returns a validation report for a method.
62    *
63    * <ul>
64    *   <li>Reports an error if {@code method} is annotated with more than one {@linkplain
65    *       #methodAnnotations() binding method annotation}.
66    *   <li>Validates {@code method} with the {@link BindingMethodValidator} for the single
67    *       {@linkplain #methodAnnotations() binding method annotation}.
68    * </ul>
69    *
70    * @throws IllegalArgumentException if {@code method} is not annotated by any {@linkplain
71    *     #methodAnnotations() binding method annotation}
72    */
validate(ExecutableElement method)73   ValidationReport<ExecutableElement> validate(ExecutableElement method) {
74     return reentrantComputeIfAbsent(reports, method, this::validateUncached);
75   }
76 
77   /**
78    * Returns {@code true} if {@code method} was already {@linkplain #validate(ExecutableElement)
79    * validated}.
80    */
wasAlreadyValidated(ExecutableElement method)81   boolean wasAlreadyValidated(ExecutableElement method) {
82     return reports.containsKey(method);
83   }
84 
validateUncached(ExecutableElement method)85   private ValidationReport<ExecutableElement> validateUncached(ExecutableElement method) {
86     ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(method);
87     ImmutableSet<? extends Class<? extends Annotation>> bindingMethodAnnotations =
88         methodAnnotations()
89             .stream()
90             .filter(annotation -> isAnnotationPresent(method, annotation))
91             .collect(toImmutableSet());
92     switch (bindingMethodAnnotations.size()) {
93       case 0:
94         throw new IllegalArgumentException(
95             String.format("%s has no binding method annotation", method));
96 
97       case 1:
98         report.addSubreport(
99             validators.get(getOnlyElement(bindingMethodAnnotations)).validate(method));
100         break;
101 
102       default:
103         report.addError(
104             String.format(
105                 "%s is annotated with more than one of (%s)",
106                 method.getSimpleName(),
107                 methodAnnotations().stream().map(Class::getCanonicalName).collect(joining(", "))),
108             method);
109         break;
110     }
111     return report.build();
112   }
113 }
114