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; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static dagger.internal.codegen.DaggerStreams.toImmutableMap; 21 import static java.lang.Character.isUpperCase; 22 import static java.lang.String.format; 23 24 import com.google.common.base.CharMatcher; 25 import com.google.common.base.Splitter; 26 import com.google.common.collect.ImmutableBiMap; 27 import com.google.common.collect.ImmutableListMultimap; 28 import com.google.common.collect.ImmutableMap; 29 import com.google.common.collect.ImmutableSet; 30 import com.google.common.collect.Multimaps; 31 import dagger.model.Key; 32 import java.util.Collection; 33 import java.util.Iterator; 34 import java.util.LinkedHashMap; 35 import java.util.Map; 36 import javax.lang.model.element.Name; 37 import javax.lang.model.element.TypeElement; 38 39 /** 40 * Holds the unique simple names for all subcomponents, keyed by their {@link ComponentDescriptor} 41 * and {@link Key} of the subcomponent builder. 42 */ 43 final class SubcomponentNames { 44 private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.'); 45 46 private final ImmutableMap<ComponentDescriptor, String> namesByDescriptor; 47 private final ImmutableMap<Key, ComponentDescriptor> descriptorsByCreatorKey; 48 SubcomponentNames(BindingGraph graph, KeyFactory keyFactory)49 SubcomponentNames(BindingGraph graph, KeyFactory keyFactory) { 50 this.namesByDescriptor = namesByDescriptor(graph); 51 this.descriptorsByCreatorKey = descriptorsByCreatorKey(keyFactory, namesByDescriptor.keySet()); 52 } 53 54 /** Returns the simple component name for the given {@link ComponentDescriptor}. */ get(ComponentDescriptor componentDescriptor)55 String get(ComponentDescriptor componentDescriptor) { 56 return namesByDescriptor.get(componentDescriptor); 57 } 58 59 /** 60 * Returns the simple name for the subcomponent creator implementation with the given {@link Key}. 61 */ getCreatorName(Key key)62 String getCreatorName(Key key) { 63 return getCreatorName(descriptorsByCreatorKey.get(key)); 64 } 65 66 /** 67 * Returns the simple name for the subcomponent creator implementation for the given {@link 68 * ComponentDescriptor}. 69 */ getCreatorName(ComponentDescriptor componentDescriptor)70 String getCreatorName(ComponentDescriptor componentDescriptor) { 71 checkArgument(componentDescriptor.creatorDescriptor().isPresent()); 72 ComponentCreatorDescriptor creatorDescriptor = componentDescriptor.creatorDescriptor().get(); 73 return get(componentDescriptor) + creatorDescriptor.kind().typeName(); 74 } 75 namesByDescriptor(BindingGraph graph)76 private static ImmutableMap<ComponentDescriptor, String> namesByDescriptor(BindingGraph graph) { 77 ImmutableListMultimap<String, ComponentDescriptor> componentDescriptorsBySimpleName = 78 Multimaps.index( 79 graph.componentDescriptors(), 80 componentDescriptor -> componentDescriptor.typeElement().getSimpleName().toString()); 81 ImmutableMap<ComponentDescriptor, Namer> componentNamers = 82 qualifiedNames(graph.componentDescriptors()); 83 Map<ComponentDescriptor, String> subcomponentImplSimpleNames = new LinkedHashMap<>(); 84 componentDescriptorsBySimpleName 85 .asMap() 86 .values() 87 .forEach( 88 components -> 89 subcomponentImplSimpleNames.putAll( 90 disambiguateConflictingSimpleNames(components, componentNamers))); 91 subcomponentImplSimpleNames.remove(graph.componentDescriptor()); 92 return ImmutableMap.copyOf(subcomponentImplSimpleNames); 93 } 94 descriptorsByCreatorKey( KeyFactory keyFactory, ImmutableSet<ComponentDescriptor> subcomponents)95 private static ImmutableMap<Key, ComponentDescriptor> descriptorsByCreatorKey( 96 KeyFactory keyFactory, ImmutableSet<ComponentDescriptor> subcomponents) { 97 return subcomponents.stream() 98 .filter(subcomponent -> subcomponent.creatorDescriptor().isPresent()) 99 .collect( 100 toImmutableMap( 101 subcomponent -> 102 keyFactory.forSubcomponentCreator( 103 subcomponent.creatorDescriptor().get().typeElement().asType()), 104 subcomponent -> subcomponent)); 105 } 106 disambiguateConflictingSimpleNames( Collection<ComponentDescriptor> components, ImmutableMap<ComponentDescriptor, Namer> componentNamers)107 private static ImmutableBiMap<ComponentDescriptor, String> disambiguateConflictingSimpleNames( 108 Collection<ComponentDescriptor> components, 109 ImmutableMap<ComponentDescriptor, Namer> componentNamers) { 110 Map<String, ComponentDescriptor> generatedSimpleNames = new LinkedHashMap<>(); 111 112 // Let's see if we can get away with using simpleName() everywhere. 113 for (ComponentDescriptor component : components) { 114 Namer namer = componentNamers.get(component); 115 if (generatedSimpleNames.containsKey(namer.simpleName())) { 116 break; 117 } 118 generatedSimpleNames.put(namer.simpleName(), component); 119 } 120 121 if (generatedSimpleNames.size() != components.size()) { 122 // Simple approach didn't work out, let's use more complicated names. 123 // We keep them small to fix https://github.com/google/dagger/issues/421. 124 generatedSimpleNames.clear(); 125 UniqueNameSet nameSet = new UniqueNameSet(); 126 for (ComponentDescriptor component : components) { 127 Namer namer = componentNamers.get(component); 128 String simpleName = namer.simpleName(); 129 String basePrefix = namer.uniquingPrefix(); 130 generatedSimpleNames.put( 131 format("%s_%s", nameSet.getUniqueName(basePrefix), simpleName), component); 132 } 133 } 134 return ImmutableBiMap.copyOf(generatedSimpleNames).inverse(); 135 } 136 qualifiedNames( Iterable<ComponentDescriptor> componentDescriptors)137 private static ImmutableMap<ComponentDescriptor, Namer> qualifiedNames( 138 Iterable<ComponentDescriptor> componentDescriptors) { 139 ImmutableMap.Builder<ComponentDescriptor, Namer> builder = ImmutableMap.builder(); 140 for (ComponentDescriptor component : componentDescriptors) { 141 builder.put(component, new Namer(component.typeElement())); 142 } 143 return builder.build(); 144 } 145 146 private static final class Namer { 147 final TypeElement typeElement; 148 Namer(TypeElement typeElement)149 Namer(TypeElement typeElement) { 150 this.typeElement = typeElement; 151 } 152 simpleName()153 String simpleName() { 154 return typeElement.getSimpleName().toString(); 155 } 156 157 /** Returns a prefix that could make {@link #simpleName()} more unique. */ uniquingPrefix()158 String uniquingPrefix() { 159 String containerName = typeElement.getEnclosingElement().getSimpleName().toString(); 160 161 // If parent element looks like a class, use its initials as a prefix. 162 if (!containerName.isEmpty() && isUpperCase(containerName.charAt(0))) { 163 return CharMatcher.javaLowerCase().removeFrom(containerName); 164 } 165 166 // Not in a normally named class. Prefix with the initials of the elements leading here. 167 Name qualifiedName = typeElement.getQualifiedName(); 168 Iterator<String> pieces = QUALIFIED_NAME_SPLITTER.split(qualifiedName).iterator(); 169 StringBuilder b = new StringBuilder(); 170 171 while (pieces.hasNext()) { 172 String next = pieces.next(); 173 if (pieces.hasNext()) { 174 b.append(next.charAt(0)); 175 } 176 } 177 178 // Note that a top level class in the root package will be prefixed "$_". 179 return b.length() > 0 ? b.toString() : "$"; 180 } 181 } 182 } 183