Java tutorial
/* * 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; import static com.squareup.javapoet.MethodSpec.methodBuilder; import static com.squareup.javapoet.TypeSpec.classBuilder; import static dagger.internal.codegen.TypeSpecs.addSupertype; import static java.lang.Character.isUpperCase; import static java.lang.String.format; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; import com.google.common.base.CharMatcher; import com.google.common.base.Joiner; 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.Iterables; import com.google.common.collect.Multimaps; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; /** * Creates the implementation class for a component. */ final class ComponentWriter extends AbstractComponentWriter { ComponentWriter(Types types, Elements elements, Key.Factory keyFactory, CompilerOptions compilerOptions, ClassName name, BindingGraph graph, boolean forTests) { super(types, elements, keyFactory, compilerOptions, name, graph, new UniqueSubcomponentNamesGenerator(graph).generate(), new OptionalFactories(), forTests); } /** * Generates a map of unique simple names for all subcomponents, keyed by their {@link * ComponentDescriptor}. */ public static class UniqueSubcomponentNamesGenerator { private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.'); private static final Joiner QUALIFIED_NAME_JOINER = Joiner.on('_'); private final BindingGraph graph; private final ImmutableListMultimap<String, ComponentDescriptor> componentDescriptorsBySimpleName; private final ImmutableMap<ComponentDescriptor, Namer> componentNamers; public UniqueSubcomponentNamesGenerator(BindingGraph graph) { this.graph = graph; componentDescriptorsBySimpleName = Multimaps.index(graph.componentDescriptors(), componentDescriptor -> componentDescriptor.componentDefinitionType().getSimpleName() .toString()); componentNamers = qualifiedNames(graph.componentDescriptors()); } public ImmutableBiMap<ComponentDescriptor, String> generate() { Map<ComponentDescriptor, String> subcomponentImplSimpleNames = new LinkedHashMap<>(); for (Entry<String, Collection<ComponentDescriptor>> componentEntry : componentDescriptorsBySimpleName .asMap().entrySet()) { Collection<ComponentDescriptor> components = componentEntry.getValue(); subcomponentImplSimpleNames.putAll(disambiguateConflictingSimpleNames(components)); } subcomponentImplSimpleNames.remove(graph.componentDescriptor()); return ImmutableBiMap.copyOf(subcomponentImplSimpleNames); } private ImmutableBiMap<ComponentDescriptor, String> disambiguateConflictingSimpleNames( Collection<ComponentDescriptor> components) { Map<String, ComponentDescriptor> 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<ComponentDescriptor, Namer> qualifiedNames( Iterable<ComponentDescriptor> componentDescriptors) { ImmutableMap.Builder<ComponentDescriptor, Namer> builder = ImmutableMap.builder(); for (ComponentDescriptor component : componentDescriptors) { builder.put(component, new Namer(component.componentDefinitionType())); } 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<String> 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() : "$"; } } } @Override protected void decorateComponent() { component.addModifiers(PUBLIC); addSupertype(component, graph.componentType()); } @Override protected ClassName builderName() { return name.nestedClass("Builder"); } @Override protected TypeSpec.Builder createBuilder(String builderSimpleName) { TypeSpec.Builder builder = classBuilder(builderSimpleName).addModifiers(STATIC); return builder; } public void postAddBuildMethod() { // Only top-level components have the factory builder() method. // Mirror the user's builder API type if they had one. MethodSpec builderFactoryMethod = methodBuilder("builder").addModifiers(PUBLIC, STATIC) .addParameter(ClassName.get(graph.application().get()), "application") .returns(graph.componentDescriptor().builderSpec().isPresent() ? ClassName.get(graph.componentDescriptor().builderSpec().get().builderDefinitionType()) : builderName.get()) .addStatement("return application.$L(new $T())", "decorate" + graph.componentType().getSimpleName().toString(), builderName.get()) .build(); component.addMethod(builderFactoryMethod); } @Override protected void addBuilderClass(TypeSpec builder) { component.addType(builder); } @Override protected void addFactoryMethods() { /*if (canInstantiateAllRequirements()) { CharSequence buildMethodName = graph.componentDescriptor().builderSpec().isPresent() ? graph.componentDescriptor().builderSpec().get().buildMethod().getSimpleName() : "build"; component.addMethod( methodBuilder("create") .returns(componentDefinitionTypeName()) .addModifiers(PUBLIC, STATIC) .addStatement("return new Builder().$L()", buildMethodName) .build()); }*/ } /** {@code true} if all of the graph's required dependencies can be automatically constructed. */ private boolean canInstantiateAllRequirements() { return !Iterables.any(graph.componentRequirements(), dependency -> dependency.requiresAPassedInstance(elements, types)); } }