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