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