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 androidx.room.compiler.processing.compat.XConverters.toJavac; 20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 21 import static javax.tools.Diagnostic.Kind.ERROR; 22 23 import androidx.room.compiler.processing.XFiler; 24 import androidx.room.compiler.processing.XProcessingEnv; 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.ProcessingOptions; 29 import dagger.internal.codegen.validation.DiagnosticReporterFactory.DiagnosticReporterImpl; 30 import dagger.spi.DiagnosticReporter; 31 import dagger.spi.model.BindingGraph; 32 import dagger.spi.model.BindingGraphPlugin; 33 import java.util.Map; 34 import java.util.Set; 35 import java.util.stream.Stream; 36 import javax.inject.Inject; 37 38 /** Initializes {@link BindingGraphPlugin}s. */ 39 public final class ExternalBindingGraphPlugins { 40 private final ImmutableSet<dagger.spi.BindingGraphPlugin> legacyPlugins; 41 private final ImmutableSet<BindingGraphPlugin> plugins; 42 private final DiagnosticReporterFactory diagnosticReporterFactory; 43 private final XFiler filer; 44 private final XProcessingEnv processingEnv; 45 private final Map<String, String> processingOptions; 46 47 @Inject ExternalBindingGraphPlugins( @xternal ImmutableSet<dagger.spi.BindingGraphPlugin> legacyPlugins, @External ImmutableSet<BindingGraphPlugin> plugins, DiagnosticReporterFactory diagnosticReporterFactory, XFiler filer, XProcessingEnv processingEnv, @ProcessingOptions Map<String, String> processingOptions)48 ExternalBindingGraphPlugins( 49 @External ImmutableSet<dagger.spi.BindingGraphPlugin> legacyPlugins, 50 @External ImmutableSet<BindingGraphPlugin> plugins, 51 DiagnosticReporterFactory diagnosticReporterFactory, 52 XFiler filer, 53 XProcessingEnv processingEnv, 54 @ProcessingOptions Map<String, String> processingOptions) { 55 this.legacyPlugins = legacyPlugins; 56 this.plugins = plugins; 57 this.diagnosticReporterFactory = diagnosticReporterFactory; 58 this.filer = filer; 59 this.processingEnv = processingEnv; 60 this.processingOptions = processingOptions; 61 } 62 63 /** Returns {@link BindingGraphPlugin#supportedOptions()} from all the plugins. */ allSupportedOptions()64 public ImmutableSet<String> allSupportedOptions() { 65 return Stream.concat( 66 legacyPlugins.stream().flatMap(plugin -> plugin.supportedOptions().stream()), 67 plugins.stream().flatMap(plugin -> plugin.supportedOptions().stream())) 68 .collect(toImmutableSet()); 69 } 70 71 /** Initializes the plugins. */ 72 // TODO(ronshapiro): Should we validate the uniqueness of plugin names? initializePlugins()73 public void initializePlugins() { 74 plugins.forEach(this::initializePlugin); 75 legacyPlugins.forEach(this::initializeLegacyPlugin); 76 } 77 initializePlugin(BindingGraphPlugin plugin)78 private void initializePlugin(BindingGraphPlugin plugin) { 79 Set<String> supportedOptions = plugin.supportedOptions(); 80 Map<String, String> filteredOptions = 81 supportedOptions.isEmpty() 82 ? ImmutableMap.of() 83 : Maps.filterKeys(processingOptions, supportedOptions::contains); 84 plugin.init(SpiModelBindingGraphConverter.toSpiModel(processingEnv), filteredOptions); 85 } 86 initializeLegacyPlugin(dagger.spi.BindingGraphPlugin plugin)87 private void initializeLegacyPlugin(dagger.spi.BindingGraphPlugin plugin) { 88 plugin.initFiler(toJavac(filer)); 89 plugin.initTypes(toJavac(processingEnv).getTypeUtils()); // ALLOW_TYPES_ELEMENTS 90 plugin.initElements(toJavac(processingEnv).getElementUtils()); // ALLOW_TYPES_ELEMENTS 91 Set<String> supportedOptions = plugin.supportedOptions(); 92 if (!supportedOptions.isEmpty()) { 93 plugin.initOptions(Maps.filterKeys(processingOptions, supportedOptions::contains)); 94 } 95 } 96 97 /** Returns {@code false} if any of the plugins reported an error. */ visit(dagger.internal.codegen.model.BindingGraph graph)98 boolean visit(dagger.internal.codegen.model.BindingGraph graph) { 99 return visitLegacyPlugins(graph) && visitPlugins(graph); 100 } 101 visitLegacyPlugins(dagger.internal.codegen.model.BindingGraph graph)102 private boolean visitLegacyPlugins(dagger.internal.codegen.model.BindingGraph graph) { 103 // Return early to avoid converting the binding graph when there are no external plugins. 104 if (legacyPlugins.isEmpty()) { 105 return true; 106 } 107 dagger.model.BindingGraph legacyGraph = ModelBindingGraphConverter.toModel(graph); 108 boolean isClean = true; 109 for (dagger.spi.BindingGraphPlugin legacyPlugin : legacyPlugins) { 110 DiagnosticReporterImpl reporter = 111 diagnosticReporterFactory.reporter(graph, legacyPlugin.pluginName()); 112 DiagnosticReporter legacyReporter = ModelBindingGraphConverter.toModel(reporter); 113 legacyPlugin.visitGraph(legacyGraph, legacyReporter); 114 if (reporter.reportedDiagnosticKinds().contains(ERROR)) { 115 isClean = false; 116 } 117 } 118 return isClean; 119 } 120 visitPlugins(dagger.internal.codegen.model.BindingGraph graph)121 private boolean visitPlugins(dagger.internal.codegen.model.BindingGraph graph) { 122 BindingGraph spiGraph = SpiModelBindingGraphConverter.toSpiModel(graph, processingEnv); 123 boolean isClean = true; 124 for (BindingGraphPlugin plugin : plugins) { 125 DiagnosticReporterImpl reporter = 126 diagnosticReporterFactory.reporter(graph, plugin.pluginName()); 127 plugin.visitGraph(spiGraph, SpiModelBindingGraphConverter.toSpiModel(reporter)); 128 if (reporter.reportedDiagnosticKinds().contains(ERROR)) { 129 isClean = false; 130 } 131 } 132 return isClean; 133 } 134 endPlugins()135 public void endPlugins() { 136 legacyPlugins.forEach(dagger.spi.BindingGraphPlugin::onPluginEnd); 137 plugins.forEach(BindingGraphPlugin::onPluginEnd); 138 } 139 } 140