1 /* 2 * Copyright (C) 2018 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.android.processor; 18 19 import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations; 20 import static com.google.common.collect.Iterables.getOnlyElement; 21 import static dagger.android.processor.AndroidMapKeys.injectedTypeFromMapKey; 22 import static java.util.stream.Collectors.collectingAndThen; 23 import static java.util.stream.Collectors.toList; 24 import static javax.tools.Diagnostic.Kind.ERROR; 25 26 import com.google.auto.common.MoreTypes; 27 import com.google.auto.service.AutoService; 28 import com.google.common.collect.ImmutableListMultimap; 29 import com.google.common.collect.ImmutableSet; 30 import com.google.common.collect.Maps; 31 import com.google.common.collect.Multimaps; 32 import dagger.MapKey; 33 import dagger.android.AndroidInjector; 34 import dagger.android.DispatchingAndroidInjector; 35 import dagger.model.Binding; 36 import dagger.model.BindingGraph; 37 import dagger.model.BindingKind; 38 import dagger.model.Key; 39 import dagger.spi.BindingGraphPlugin; 40 import dagger.spi.DiagnosticReporter; 41 import java.util.Formatter; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Optional; 45 import java.util.stream.Stream; 46 import javax.inject.Provider; 47 import javax.lang.model.element.AnnotationMirror; 48 import javax.lang.model.type.TypeKind; 49 import javax.lang.model.type.TypeMirror; 50 51 /** 52 * Validates that the two maps that {@link DispatchingAndroidInjector} injects have logically 53 * different keys. If a contribution exists for the same {@code FooActivity} with 54 * {@code @ActivityKey(FooActivity.class)} and 55 * {@code @AndroidInjectionKey("com.example.FooActivity")}, report an error. 56 */ 57 @AutoService(BindingGraphPlugin.class) 58 public final class DuplicateAndroidInjectorsChecker implements BindingGraphPlugin { 59 @Override visitGraph(BindingGraph graph, DiagnosticReporter diagnosticReporter)60 public void visitGraph(BindingGraph graph, DiagnosticReporter diagnosticReporter) { 61 for (Binding binding : graph.bindings()) { 62 if (isDispatchingAndroidInjector(binding)) { 63 validateMapKeyUniqueness(binding, graph, diagnosticReporter); 64 } 65 } 66 } 67 isDispatchingAndroidInjector(Binding binding)68 private boolean isDispatchingAndroidInjector(Binding binding) { 69 Key key = binding.key(); 70 return MoreTypes.isTypeOf(DispatchingAndroidInjector.class, key.type()) 71 && !key.qualifier().isPresent(); 72 } 73 validateMapKeyUniqueness( Binding dispatchingAndroidInjector, BindingGraph graph, DiagnosticReporter diagnosticReporter)74 private void validateMapKeyUniqueness( 75 Binding dispatchingAndroidInjector, 76 BindingGraph graph, 77 DiagnosticReporter diagnosticReporter) { 78 ImmutableSet<Binding> injectorFactories = 79 injectorMapDependencies(dispatchingAndroidInjector, graph) 80 .flatMap(injectorFactoryMap -> graph.requestedBindings(injectorFactoryMap).stream()) 81 .collect(collectingAndThen(toList(), ImmutableSet::copyOf)); 82 83 ImmutableListMultimap.Builder<String, Binding> mapKeyIndex = ImmutableListMultimap.builder(); 84 for (Binding injectorFactory : injectorFactories) { 85 AnnotationMirror mapKey = mapKey(injectorFactory).get(); 86 Optional<String> injectedType = injectedTypeFromMapKey(mapKey); 87 if (injectedType.isPresent()) { 88 mapKeyIndex.put(injectedType.get(), injectorFactory); 89 } else { 90 diagnosticReporter.reportBinding( 91 ERROR, injectorFactory, "Unrecognized class: %s", mapKey); 92 } 93 } 94 95 Map<String, List<Binding>> duplicates = 96 Maps.filterValues(Multimaps.asMap(mapKeyIndex.build()), bindings -> bindings.size() > 1); 97 if (!duplicates.isEmpty()) { 98 StringBuilder errorMessage = 99 new StringBuilder("Multiple injector factories bound for the same type:\n"); 100 Formatter formatter = new Formatter(errorMessage); 101 duplicates.forEach( 102 (injectedType, duplicateFactories) -> { 103 formatter.format(" %s:\n", injectedType); 104 duplicateFactories.forEach(duplicate -> formatter.format(" %s\n", duplicate)); 105 }); 106 diagnosticReporter.reportBinding(ERROR, dispatchingAndroidInjector, errorMessage.toString()); 107 } 108 } 109 110 /** 111 * Returns a stream of the dependencies of {@code binding} that have a key type of {@code Map<K, 112 * Provider<AndroidInjector.Factory<?>>}. 113 */ injectorMapDependencies(Binding binding, BindingGraph graph)114 private Stream<Binding> injectorMapDependencies(Binding binding, BindingGraph graph) { 115 return graph.requestedBindings(binding).stream() 116 .filter(requestedBinding -> requestedBinding.kind().equals(BindingKind.MULTIBOUND_MAP)) 117 .filter( 118 requestedBinding -> { 119 TypeMirror valueType = 120 MoreTypes.asDeclared(requestedBinding.key().type()).getTypeArguments().get(1); 121 if (!MoreTypes.isTypeOf(Provider.class, valueType) 122 || !valueType.getKind().equals(TypeKind.DECLARED)) { 123 return false; 124 } 125 TypeMirror providedType = MoreTypes.asDeclared(valueType).getTypeArguments().get(0); 126 return MoreTypes.isTypeOf(AndroidInjector.Factory.class, providedType); 127 }); 128 } 129 mapKey(Binding binding)130 private Optional<AnnotationMirror> mapKey(Binding binding) { 131 return binding 132 .bindingElement() 133 .map(bindingElement -> getAnnotatedAnnotations(bindingElement, MapKey.class)) 134 .flatMap( 135 annotations -> 136 annotations.isEmpty() 137 ? Optional.empty() 138 : Optional.of(getOnlyElement(annotations))); 139 } 140 141 @Override pluginName()142 public String pluginName() { 143 return "Dagger/Android/DuplicateAndroidInjectors"; 144 } 145 } 146