• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.writing;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkState;
21 import static dagger.internal.codegen.binding.SourceFiles.classFileName;
22 import static dagger.internal.codegen.extension.DaggerCollectors.onlyElement;
23 import static java.lang.String.format;
24 
25 import com.google.common.base.CharMatcher;
26 import com.google.common.base.Splitter;
27 import com.google.common.collect.ImmutableMap;
28 import com.google.common.collect.ImmutableMultimap;
29 import com.google.common.collect.Iterables;
30 import com.google.common.collect.Multimaps;
31 import com.squareup.javapoet.ClassName;
32 import dagger.internal.codegen.base.ComponentCreatorKind;
33 import dagger.internal.codegen.base.UniqueNameSet;
34 import dagger.internal.codegen.binding.BindingGraph;
35 import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
36 import dagger.internal.codegen.binding.ComponentDescriptor;
37 import dagger.internal.codegen.binding.KeyFactory;
38 import dagger.internal.codegen.compileroption.CompilerOptions;
39 import dagger.internal.codegen.model.ComponentPath;
40 import dagger.internal.codegen.model.Key;
41 import java.util.Collection;
42 import java.util.Iterator;
43 import java.util.LinkedHashMap;
44 import java.util.Map;
45 import javax.inject.Inject;
46 
47 /**
48  * Holds the unique simple names for all components, keyed by their {@link ComponentPath} and {@link
49  * Key} of the subcomponent builder.
50  */
51 public final class ComponentNames {
52   /** Returns the class name for the top-level generated class. */
getTopLevelClassName(ComponentDescriptor componentDescriptor)53   public static ClassName getTopLevelClassName(ComponentDescriptor componentDescriptor) {
54     checkState(!componentDescriptor.isSubcomponent());
55     ClassName componentName = componentDescriptor.typeElement().getClassName();
56     return ClassName.get(componentName.packageName(), "Dagger" + classFileName(componentName));
57   }
58 
59   private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.');
60 
61   private final CompilerOptions compilerOptions;
62   private final ClassName topLevelClassName;
63   private final ImmutableMap<ComponentPath, String> namesByPath;
64   private final ImmutableMap<ComponentPath, String> creatorNamesByPath;
65   private final ImmutableMultimap<Key, ComponentPath> pathsByCreatorKey;
66 
67   @Inject
ComponentNames( CompilerOptions compilerOptions, @TopLevel BindingGraph graph, KeyFactory keyFactory)68   ComponentNames(
69       CompilerOptions compilerOptions, @TopLevel BindingGraph graph, KeyFactory keyFactory) {
70     this.compilerOptions = compilerOptions;
71     this.topLevelClassName = getTopLevelClassName(graph.componentDescriptor());
72     this.namesByPath = namesByPath(graph);
73     this.creatorNamesByPath = creatorNamesByPath(namesByPath, graph);
74     this.pathsByCreatorKey = pathsByCreatorKey(keyFactory, graph);
75   }
76 
77   /** Returns the simple component name for the given {@link ComponentDescriptor}. */
get(ComponentPath componentPath)78   ClassName get(ComponentPath componentPath) {
79     return compilerOptions.generatedClassExtendsComponent() && componentPath.atRoot()
80         ? topLevelClassName
81         : topLevelClassName.nestedClass(namesByPath.get(componentPath) + "Impl");
82   }
83 
84   /**
85    * Returns the component descriptor for the component with the given subcomponent creator {@link
86    * Key}.
87    */
getSubcomponentCreatorName(ComponentPath componentPath, Key creatorKey)88   ClassName getSubcomponentCreatorName(ComponentPath componentPath, Key creatorKey) {
89     checkArgument(pathsByCreatorKey.containsKey(creatorKey));
90     // First, find the subcomponent path corresponding to the subcomponent creator key.
91     // The key may correspond to multiple paths, so we need to find the one under this component.
92     ComponentPath subcomponentPath =
93         pathsByCreatorKey.get(creatorKey).stream()
94             .filter(path -> path.parent().equals(componentPath))
95             .collect(onlyElement());
96     return getCreatorName(subcomponentPath);
97   }
98 
99   /**
100    * Returns the simple name for the subcomponent creator implementation for the given {@link
101    * ComponentDescriptor}.
102    */
getCreatorName(ComponentPath componentPath)103   ClassName getCreatorName(ComponentPath componentPath) {
104     checkArgument(creatorNamesByPath.containsKey(componentPath));
105     return topLevelClassName.nestedClass(creatorNamesByPath.get(componentPath));
106   }
107 
creatorNamesByPath( ImmutableMap<ComponentPath, String> namesByPath, BindingGraph graph)108   private static ImmutableMap<ComponentPath, String> creatorNamesByPath(
109       ImmutableMap<ComponentPath, String> namesByPath, BindingGraph graph) {
110     ImmutableMap.Builder<ComponentPath, String> builder = ImmutableMap.builder();
111     graph
112         .componentDescriptorsByPath()
113         .forEach(
114             (componentPath, componentDescriptor) -> {
115               if (componentPath.atRoot()) {
116                 ComponentCreatorKind creatorKind =
117                     componentDescriptor
118                         .creatorDescriptor()
119                         .map(ComponentCreatorDescriptor::kind)
120                         .orElse(ComponentCreatorKind.BUILDER);
121                 builder.put(componentPath, creatorKind.typeName());
122               } else if (componentDescriptor.creatorDescriptor().isPresent()) {
123                 ComponentCreatorDescriptor creatorDescriptor =
124                     componentDescriptor.creatorDescriptor().get();
125                 String componentName = namesByPath.get(componentPath);
126                 builder.put(componentPath, componentName + creatorDescriptor.kind().typeName());
127               }
128             });
129     return builder.build();
130   }
131 
namesByPath(BindingGraph graph)132   private static ImmutableMap<ComponentPath, String> namesByPath(BindingGraph graph) {
133     Map<ComponentPath, String> componentPathsBySimpleName = new LinkedHashMap<>();
134     Multimaps.index(graph.componentDescriptorsByPath().keySet(), ComponentNames::simpleName)
135         .asMap()
136         .values()
137         .stream()
138         .map(ComponentNames::disambiguateConflictingSimpleNames)
139         .forEach(componentPathsBySimpleName::putAll);
140     return ImmutableMap.copyOf(componentPathsBySimpleName);
141   }
142 
pathsByCreatorKey( KeyFactory keyFactory, BindingGraph graph)143   private static ImmutableMultimap<Key, ComponentPath> pathsByCreatorKey(
144       KeyFactory keyFactory, BindingGraph graph) {
145     ImmutableMultimap.Builder<Key, ComponentPath> builder = ImmutableMultimap.builder();
146     graph
147         .componentDescriptorsByPath()
148         .forEach(
149             (componentPath, componentDescriptor) -> {
150               if (componentDescriptor.creatorDescriptor().isPresent()) {
151                 Key creatorKey =
152                     keyFactory.forSubcomponentCreator(
153                         componentDescriptor.creatorDescriptor().get().typeElement().getType());
154                 builder.put(creatorKey, componentPath);
155               }
156             });
157     return builder.build();
158   }
159 
disambiguateConflictingSimpleNames( Collection<ComponentPath> componentsWithConflictingNames)160   private static ImmutableMap<ComponentPath, String> disambiguateConflictingSimpleNames(
161       Collection<ComponentPath> componentsWithConflictingNames) {
162     // If there's only 1 component there's nothing to disambiguate so return the simple name.
163     if (componentsWithConflictingNames.size() == 1) {
164       ComponentPath componentPath = Iterables.getOnlyElement(componentsWithConflictingNames);
165       return ImmutableMap.of(componentPath, simpleName(componentPath));
166     }
167 
168     // There are conflicting simple names, so disambiguate them with a unique prefix.
169     // We keep them small to fix https://github.com/google/dagger/issues/421.
170     UniqueNameSet nameSet = new UniqueNameSet();
171     ImmutableMap.Builder<ComponentPath, String> uniqueNames = ImmutableMap.builder();
172     for (ComponentPath componentPath : componentsWithConflictingNames) {
173       String simpleName = simpleName(componentPath);
174       String basePrefix = uniquingPrefix(componentPath);
175       uniqueNames.put(
176           componentPath, format("%s_%s", nameSet.getUniqueName(basePrefix), simpleName));
177     }
178     return uniqueNames.build();
179   }
180 
simpleName(ComponentPath componentPath)181   private static String simpleName(ComponentPath componentPath) {
182     return componentPath.currentComponent().className().simpleName();
183   }
184 
185   /** Returns a prefix that could make the component's simple name more unique. */
uniquingPrefix(ComponentPath componentPath)186   private static String uniquingPrefix(ComponentPath componentPath) {
187     ClassName component = componentPath.currentComponent().className();
188 
189     if (component.enclosingClassName() != null) {
190       return CharMatcher.javaLowerCase().removeFrom(component.enclosingClassName().simpleName());
191     }
192 
193     // Not in a normally named class. Prefix with the initials of the elements leading here.
194     Iterator<String> pieces = QUALIFIED_NAME_SPLITTER.split(component.canonicalName()).iterator();
195     StringBuilder b = new StringBuilder();
196 
197     while (pieces.hasNext()) {
198       String next = pieces.next();
199       if (pieces.hasNext()) {
200         b.append(next.charAt(0));
201       }
202     }
203 
204     // Note that a top level class in the root package will be prefixed "$_".
205     return b.length() > 0 ? b.toString() : "$";
206   }
207 }
208