1 /* 2 * Copyright (C) 2021 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 dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 20 import static javax.tools.Diagnostic.Kind.ERROR; 21 22 import androidx.room.compiler.processing.XProcessingEnv; 23 import com.google.common.base.Optional; 24 import com.google.common.base.Supplier; 25 import com.google.common.collect.ImmutableMap; 26 import com.google.common.collect.ImmutableSet; 27 import com.google.common.collect.Maps; 28 import dagger.internal.codegen.compileroption.CompilerOptions; 29 import dagger.internal.codegen.compileroption.ProcessingOptions; 30 import dagger.internal.codegen.compileroption.ValidationType; 31 import dagger.internal.codegen.model.BindingGraph; 32 import dagger.internal.codegen.model.BindingGraphPlugin; 33 import dagger.internal.codegen.model.DaggerProcessingEnv; 34 import dagger.internal.codegen.validation.DiagnosticReporterFactory.DiagnosticReporterImpl; 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Set; 39 import javax.inject.Inject; 40 41 /** Initializes {@link BindingGraphPlugin}s. */ 42 public final class ValidationBindingGraphPlugins { 43 private final ImmutableSet<ValidationBindingGraphPlugin> plugins; 44 private final DiagnosticReporterFactory diagnosticReporterFactory; 45 private final XProcessingEnv processingEnv; 46 private final CompilerOptions compilerOptions; 47 private final Map<String, String> processingOptions; 48 49 @Inject ValidationBindingGraphPlugins( @alidation ImmutableSet<ValidationBindingGraphPlugin> plugins, DiagnosticReporterFactory diagnosticReporterFactory, XProcessingEnv processingEnv, CompilerOptions compilerOptions, @ProcessingOptions Map<String, String> processingOptions)50 ValidationBindingGraphPlugins( 51 @Validation ImmutableSet<ValidationBindingGraphPlugin> plugins, 52 DiagnosticReporterFactory diagnosticReporterFactory, 53 XProcessingEnv processingEnv, 54 CompilerOptions compilerOptions, 55 @ProcessingOptions Map<String, String> processingOptions) { 56 this.plugins = plugins; 57 this.diagnosticReporterFactory = diagnosticReporterFactory; 58 this.processingEnv = processingEnv; 59 this.compilerOptions = compilerOptions; 60 this.processingOptions = processingOptions; 61 } 62 63 /** Returns {@link BindingGraphPlugin#supportedOptions()} from all the plugins. */ allSupportedOptions()64 public ImmutableSet<String> allSupportedOptions() { 65 return plugins.stream() 66 .flatMap(plugin -> plugin.supportedOptions().stream()) 67 .collect(toImmutableSet()); 68 } 69 70 /** Initializes the plugins. */ 71 // TODO(ronshapiro): Should we validate the uniqueness of plugin names? initializePlugins()72 public void initializePlugins() { 73 DaggerProcessingEnv daggerProcessingEnv = DaggerProcessingEnv.from(processingEnv); 74 plugins.forEach(plugin -> plugin.init(daggerProcessingEnv, pluginOptions(plugin))); 75 } 76 77 /** Returns the filtered map of processing options supported by the given plugin. */ pluginOptions(BindingGraphPlugin plugin)78 private ImmutableMap<String, String> pluginOptions(BindingGraphPlugin plugin) { 79 Set<String> supportedOptions = plugin.supportedOptions(); 80 return supportedOptions.isEmpty() 81 ? ImmutableMap.of() 82 : ImmutableMap.copyOf(Maps.filterKeys(processingOptions, supportedOptions::contains)); 83 } 84 85 /** Returns {@code false} if any of the plugins reported an error. */ visit(Optional<BindingGraph> prunedGraph, Supplier<BindingGraph> fullGraphSupplier)86 boolean visit(Optional<BindingGraph> prunedGraph, Supplier<BindingGraph> fullGraphSupplier) { 87 BindingGraph graph = prunedGraph.isPresent() ? prunedGraph.get() : fullGraphSupplier.get(); 88 89 boolean isClean = true; 90 List<ValidationBindingGraphPlugin> rerunPlugins = new ArrayList<>(); 91 for (ValidationBindingGraphPlugin plugin : plugins) { 92 DiagnosticReporterImpl reporter = createReporter(plugin.pluginName(), graph); 93 plugin.visitGraph(graph, reporter); 94 if (plugin.visitFullGraphRequested(graph)) { 95 rerunPlugins.add(plugin); 96 } 97 if (reporter.reportedDiagnosticKinds().contains(ERROR)) { 98 isClean = false; 99 } 100 } 101 if (!rerunPlugins.isEmpty()) { 102 BindingGraph fullGraph = fullGraphSupplier.get(); 103 for (ValidationBindingGraphPlugin plugin : rerunPlugins) { 104 DiagnosticReporterImpl reporter = createReporter(plugin.pluginName(), fullGraph); 105 plugin.revisitFullGraph(prunedGraph.get(), fullGraph, reporter); 106 if (reporter.reportedDiagnosticKinds().contains(ERROR)) { 107 isClean = false; 108 } 109 } 110 } 111 return isClean; 112 } 113 createReporter(String pluginName, BindingGraph graph)114 private DiagnosticReporterImpl createReporter(String pluginName, BindingGraph graph) { 115 boolean errorsAsWarnings = 116 graph.isFullBindingGraph() 117 && compilerOptions.fullBindingGraphValidationType().equals(ValidationType.WARNING); 118 return errorsAsWarnings 119 ? diagnosticReporterFactory.reporterWithErrorAsWarnings(graph, pluginName) 120 : diagnosticReporterFactory.reporter(graph, pluginName); 121 } 122 endPlugins()123 public void endPlugins() { 124 plugins.forEach(BindingGraphPlugin::onPluginEnd); 125 } 126 } 127