All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dagger.internal.codegen.writing.SubcomponentNames Maven / Gradle / Ivy

There is a newer version: 2.52
Show newest version
/*
 * 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.ImmutableBiMap;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
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(),
            componentDescriptor -> componentDescriptor.typeElement().getSimpleName().toString());
    ImmutableMap componentNamers =
        qualifiedNames(graph.componentDescriptors());
    Map subcomponentImplSimpleNames = new LinkedHashMap<>();
    componentDescriptorsBySimpleName
        .asMap()
        .values()
        .forEach(
            components ->
                subcomponentImplSimpleNames.putAll(
                    disambiguateConflictingSimpleNames(components, componentNamers)));
    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 ImmutableBiMap disambiguateConflictingSimpleNames(
      Collection components,
      ImmutableMap componentNamers) {
    Map generatedSimpleNames = new LinkedHashMap<>();

    // Let's see if we can get away with using simpleName() everywhere.
    for (ComponentDescriptor component : components) {
      Namer namer = componentNamers.get(component);
      if (generatedSimpleNames.containsKey(namer.simpleName())) {
        break;
      }
      generatedSimpleNames.put(namer.simpleName(), component);
    }

    if (generatedSimpleNames.size() != components.size()) {
      // Simple approach didn't work out, let's use more complicated names.
      // We keep them small to fix https://github.com/google/dagger/issues/421.
      generatedSimpleNames.clear();
      UniqueNameSet nameSet = new UniqueNameSet();
      for (ComponentDescriptor component : components) {
        Namer namer = componentNamers.get(component);
        String simpleName = namer.simpleName();
        String basePrefix = namer.uniquingPrefix();
        generatedSimpleNames.put(
            format("%s_%s", nameSet.getUniqueName(basePrefix), simpleName), component);
      }
    }
    return ImmutableBiMap.copyOf(generatedSimpleNames).inverse();
  }

  private static ImmutableMap qualifiedNames(
      Iterable componentDescriptors) {
    ImmutableMap.Builder builder = ImmutableMap.builder();
    for (ComponentDescriptor component : componentDescriptors) {
      builder.put(component, new Namer(component.typeElement()));
    }
    return builder.build();
  }

  private static final class Namer {
    final TypeElement typeElement;

    Namer(TypeElement typeElement) {
      this.typeElement = typeElement;
    }

    String simpleName() {
      return typeElement.getSimpleName().toString();
    }

    /** Returns a prefix that could make {@link #simpleName()} more unique. */
    String uniquingPrefix() {
      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() : "$";
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy