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