/* * Copyright (C) 2015 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen.writing; import static com.google.common.base.Preconditions.checkArgument; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap; import static java.lang.Character.isUpperCase; import static java.lang.String.format; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Multimaps; import dagger.internal.codegen.base.UniqueNameSet; import dagger.internal.codegen.binding.BindingGraph; import dagger.internal.codegen.binding.ComponentCreatorDescriptor; import dagger.internal.codegen.binding.ComponentDescriptor; import dagger.internal.codegen.binding.KeyFactory; import dagger.model.Key; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; /** * Holds the unique simple names for all subcomponents, keyed by their {@link ComponentDescriptor} * and {@link Key} of the subcomponent builder. */ public final class SubcomponentNames { private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.'); private final ImmutableMap namesByDescriptor; private final ImmutableMap descriptorsByCreatorKey; public SubcomponentNames(BindingGraph graph, KeyFactory keyFactory) { this.namesByDescriptor = namesByDescriptor(graph); this.descriptorsByCreatorKey = descriptorsByCreatorKey(keyFactory, namesByDescriptor.keySet()); } /** Returns the simple component name for the given {@link ComponentDescriptor}. */ String get(ComponentDescriptor componentDescriptor) { return namesByDescriptor.get(componentDescriptor); } /** * Returns the simple name for the subcomponent creator implementation with the given {@link Key}. */ String getCreatorName(Key key) { return getCreatorName(descriptorsByCreatorKey.get(key)); } /** * Returns the simple name for the subcomponent creator implementation for the given {@link * ComponentDescriptor}. */ String getCreatorName(ComponentDescriptor componentDescriptor) { checkArgument(componentDescriptor.creatorDescriptor().isPresent()); ComponentCreatorDescriptor creatorDescriptor = componentDescriptor.creatorDescriptor().get(); return get(componentDescriptor) + creatorDescriptor.kind().typeName(); } private static ImmutableMap namesByDescriptor(BindingGraph graph) { ImmutableListMultimap componentDescriptorsBySimpleName = Multimaps.index(graph.componentDescriptors(), SubcomponentNames::simpleName); Map subcomponentImplSimpleNames = new LinkedHashMap<>(); componentDescriptorsBySimpleName .asMap() .values() .stream() .map(SubcomponentNames::disambiguateConflictingSimpleNames) .forEach(subcomponentImplSimpleNames::putAll); subcomponentImplSimpleNames.remove(graph.componentDescriptor()); return ImmutableMap.copyOf(subcomponentImplSimpleNames); } private static ImmutableMap descriptorsByCreatorKey( KeyFactory keyFactory, ImmutableSet subcomponents) { return subcomponents.stream() .filter(subcomponent -> subcomponent.creatorDescriptor().isPresent()) .collect( toImmutableMap( subcomponent -> keyFactory.forSubcomponentCreator( subcomponent.creatorDescriptor().get().typeElement().asType()), subcomponent -> subcomponent)); } private static ImmutableMap disambiguateConflictingSimpleNames( Collection componentsWithConflictingNames) { // If there's only 1 component there's nothing to disambiguate so return the simple name. if (componentsWithConflictingNames.size() == 1) { ComponentDescriptor component = Iterables.getOnlyElement(componentsWithConflictingNames); return ImmutableMap.of(component, simpleName(component)); } // There are conflicting simple names, so disambiguate them with a unique prefix. // We keep them small to fix https://github.com/google/dagger/issues/421. UniqueNameSet nameSet = new UniqueNameSet(); ImmutableMap.Builder uniqueNames = ImmutableMap.builder(); for (ComponentDescriptor component : componentsWithConflictingNames) { String simpleName = simpleName(component); String basePrefix = uniquingPrefix(component); uniqueNames.put(component, format("%s_%s", nameSet.getUniqueName(basePrefix), simpleName)); } return uniqueNames.build(); } private static String simpleName(ComponentDescriptor component) { return component.typeElement().getSimpleName().toString(); } /** Returns a prefix that could make the component's simple name more unique. */ private static String uniquingPrefix(ComponentDescriptor component) { TypeElement typeElement = component.typeElement(); String containerName = typeElement.getEnclosingElement().getSimpleName().toString(); // If parent element looks like a class, use its initials as a prefix. if (!containerName.isEmpty() && isUpperCase(containerName.charAt(0))) { return CharMatcher.javaLowerCase().removeFrom(containerName); } // Not in a normally named class. Prefix with the initials of the elements leading here. Name qualifiedName = typeElement.getQualifiedName(); Iterator pieces = QUALIFIED_NAME_SPLITTER.split(qualifiedName).iterator(); StringBuilder b = new StringBuilder(); while (pieces.hasNext()) { String next = pieces.next(); if (pieces.hasNext()) { b.append(next.charAt(0)); } } // Note that a top level class in the root package will be prefixed "$_". return b.length() > 0 ? b.toString() : "$"; } }