dagger2.internal.codegen.SourceFiles.java Source code

Java tutorial

Introduction

Here is the source code for dagger2.internal.codegen.SourceFiles.java

Source

/*
 * Copyright (C) 2014 Google, Inc.
 *
 * 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 dagger2.internal.codegen;

import com.google.common.base.CaseFormat;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import dagger2.internal.DoubleCheckLazy;
import dagger2.internal.codegen.ContributionBinding.BindingType;
import dagger2.internal.codegen.writer.ClassName;
import dagger2.internal.codegen.writer.ParameterizedTypeName;
import dagger2.internal.codegen.writer.Snippet;
import dagger2.internal.codegen.writer.TypeName;
import dagger2.internal.codegen.writer.TypeNames;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;

import static com.google.common.base.CaseFormat.UPPER_CAMEL;

/**
 * Utilities for generating files.
 *
 * @author Gregory Kick
 * @since 2.0
 */
class SourceFiles {
    /**
     * Sorts {@link DependencyRequest} instances in an order likely to reflect their logical
     * importance.
     */
    static final Ordering<DependencyRequest> DEPENDENCY_ORDERING = new Ordering<DependencyRequest>() {
        @Override
        public int compare(DependencyRequest left, DependencyRequest right) {
            return ComparisonChain.start()
                    // put fields before parameters
                    .compare(left.requestElement().getKind(), right.requestElement().getKind())
                    // order by dependency kind
                    .compare(left.kind(), right.kind())
                    // then sort by name
                    .compare(left.requestElement().getSimpleName().toString(),
                            right.requestElement().getSimpleName().toString())
                    .result();
        }
    };

    /**
     * A variant of {@link #indexDependenciesByKey} that maps from unresolved keys
     * to requests.  This is used when generating component's initialize()
     * methods (and in members injectors) in order to instantiate dependent
     * providers.  Consider a generic type of {@code Foo<T>} with a constructor
     * of {@code Foo(T t, T t1, A a, A a1)}.  That will be collapsed to a factory
     * taking a {@code Provider<T> tProvider, Provider<A> aProvider}. However,
     * if it was referenced as {@code Foo<A>}, we need to make sure we still
     * pass two providers.  Naively (if we just referenced by resolved BindingKey),
     * we would have passed a single {@code aProvider}.
     */
    // TODO(user): Refactor these indexing methods so that the binding itself knows what sort of
    // binding keys and framework classes that it needs.
    static ImmutableSetMultimap<BindingKey, DependencyRequest> indexDependenciesByUnresolvedKey(Types types,
            Iterable<? extends DependencyRequest> dependencies) {
        ImmutableSetMultimap.Builder<BindingKey, DependencyRequest> dependenciesByKeyBuilder = new ImmutableSetMultimap.Builder<BindingKey, DependencyRequest>()
                .orderValuesBy(DEPENDENCY_ORDERING);
        for (DependencyRequest dependency : dependencies) {
            BindingKey resolved = dependency.bindingKey();
            // To get the proper unresolved type, we have to extract the proper type from the
            // request type again (because we're looking at the actual element's type).
            TypeMirror unresolvedType = DependencyRequest.Factory
                    .extractKindAndType(dependency.requestElement().asType()).type();
            BindingKey unresolved = BindingKey.create(resolved.kind(),
                    resolved.key().withType(types, unresolvedType));
            dependenciesByKeyBuilder.put(unresolved, dependency);
        }
        return dependenciesByKeyBuilder.build();
    }

    /**
     * Allows dependency requests to be grouped by the key they're requesting.
     * This is used by factory generation in order to minimize the number of parameters
     * required in the case where a given key is requested more than once.  This expects
     * unresolved dependency requests, otherwise we may generate factories based on
     * a particular usage of a class as opposed to the generic types of the class.
     */
    static ImmutableSetMultimap<BindingKey, DependencyRequest> indexDependenciesByKey(
            Iterable<? extends DependencyRequest> dependencies) {
        ImmutableSetMultimap.Builder<BindingKey, DependencyRequest> dependenciesByKeyBuilder = new ImmutableSetMultimap.Builder<BindingKey, DependencyRequest>()
                .orderValuesBy(DEPENDENCY_ORDERING);
        for (DependencyRequest dependency : dependencies) {
            dependenciesByKeyBuilder.put(dependency.bindingKey(), dependency);
        }
        return dependenciesByKeyBuilder.build();
    }

    /**
     * This method generates names and keys for the framework classes necessary for all of the
     * bindings. It is responsible for the following:
     * <ul>
     * <li>Choosing a name that associates the binding with all of the dependency requests for this
     * type.
     * <li>Choosing a name that is <i>probably</i> associated with the type being bound.
     * <li>Ensuring that no two bindings end up with the same name.
     * </ul>
     *
     * @return Returns the mapping from {@link BindingKey} to field, sorted by the name of the field.
     */
    static ImmutableMap<BindingKey, FrameworkField> generateBindingFieldsForDependencies(
            DependencyRequestMapper dependencyRequestMapper, Iterable<? extends DependencyRequest> dependencies) {
        ImmutableSetMultimap<BindingKey, DependencyRequest> dependenciesByKey = indexDependenciesByKey(
                dependencies);
        Map<BindingKey, Collection<DependencyRequest>> dependenciesByKeyMap = dependenciesByKey.asMap();
        ImmutableMap.Builder<BindingKey, FrameworkField> bindingFields = ImmutableMap.builder();
        for (Entry<BindingKey, Collection<DependencyRequest>> entry : dependenciesByKeyMap.entrySet()) {
            BindingKey bindingKey = entry.getKey();
            Collection<DependencyRequest> requests = entry.getValue();
            Class<?> frameworkClass = dependencyRequestMapper.getFrameworkClass(requests.iterator().next());
            // collect together all of the names that we would want to call the provider
            ImmutableSet<String> dependencyNames = FluentIterable.from(requests)
                    .transform(new DependencyVariableNamer()).toSet();

            if (dependencyNames.size() == 1) {
                // if there's only one name, great! use it!
                String name = Iterables.getOnlyElement(dependencyNames);
                bindingFields.put(bindingKey,
                        FrameworkField.createWithTypeFromKey(frameworkClass, bindingKey, name));
            } else {
                // in the event that a field is being used for a bunch of deps with different names,
                // add all the names together with "And"s in the middle. E.g.: stringAndS
                Iterator<String> namesIterator = dependencyNames.iterator();
                String first = namesIterator.next();
                StringBuilder compositeNameBuilder = new StringBuilder(first);
                while (namesIterator.hasNext()) {
                    compositeNameBuilder.append("And")
                            .append(CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL, namesIterator.next()));
                }
                bindingFields.put(bindingKey, FrameworkField.createWithTypeFromKey(frameworkClass, bindingKey,
                        compositeNameBuilder.toString()));
            }
        }
        return bindingFields.build();
    }

    static Snippet frameworkTypeUsageStatement(Snippet frameworkTypeMemberSelect,
            DependencyRequest.Kind dependencyKind) {
        switch (dependencyKind) {
        case LAZY:
            return Snippet.format("%s.create(%s)", ClassName.fromClass(DoubleCheckLazy.class),
                    frameworkTypeMemberSelect);
        case INSTANCE:
        case FUTURE:
            return Snippet.format("%s.get()", frameworkTypeMemberSelect);
        case PROVIDER:
        case PRODUCER:
        case MEMBERS_INJECTOR:
            return Snippet.format("%s", frameworkTypeMemberSelect);
        default:
            throw new AssertionError();
        }
    }

    static ClassName factoryNameForProvisionBinding(ProvisionBinding binding) {
        TypeElement enclosingTypeElement = binding.bindingTypeElement();
        ClassName enclosingClassName = ClassName.fromTypeElement(enclosingTypeElement);
        switch (binding.bindingKind()) {
        case INJECTION:
        case PROVISION:
            return enclosingClassName.topLevelClassName()
                    .peerNamed(enclosingClassName.classFileName() + "_" + factoryPrefix(binding) + "Factory");
        case SYNTHETIC_PROVISON:
            throw new IllegalArgumentException();
        default:
            throw new AssertionError();
        }
    }

    /**
     * Returns the factory name parameterized with the ProvisionBinding's parameters (if necessary).
     */
    static TypeName parameterizedFactoryNameForProvisionBinding(ProvisionBinding binding) {
        ClassName factoryName = factoryNameForProvisionBinding(binding);
        List<TypeName> parameters = ImmutableList.of();
        if (binding.bindingType().equals(BindingType.UNIQUE)) {
            switch (binding.bindingKind()) {
            case INJECTION:
                TypeName bindingName = TypeNames.forTypeMirror(binding.key().type());
                // If the binding is parameterized, parameterize the factory.
                if (bindingName instanceof ParameterizedTypeName) {
                    parameters = ((ParameterizedTypeName) bindingName).parameters();
                }
                break;
            case PROVISION:
                // For provision bindings, we parameterize creation on the types of
                // the module, not the types of the binding.
                // Consider: Module<A, B, C> { @Provides List<B> provideB(B b) { .. }}
                // The binding is just parameterized on <B>, but we need all of <A, B, C>.
                if (!binding.bindingTypeElement().getTypeParameters().isEmpty()) {
                    parameters = ((ParameterizedTypeName) TypeNames
                            .forTypeMirror(binding.bindingTypeElement().asType())).parameters();
                }
                break;
            default: // fall through.
            }
        }
        return parameters.isEmpty() ? factoryName : ParameterizedTypeName.create(factoryName, parameters);
    }

    static ClassName factoryNameForProductionBinding(ProductionBinding binding) {
        TypeElement enclosingTypeElement = binding.bindingTypeElement();
        ClassName enclosingClassName = ClassName.fromTypeElement(enclosingTypeElement);
        switch (binding.bindingKind()) {
        case IMMEDIATE:
        case FUTURE_PRODUCTION:
            return enclosingClassName.topLevelClassName()
                    .peerNamed(enclosingClassName.classFileName() + "_" + factoryPrefix(binding) + "Factory");
        default:
            throw new AssertionError();
        }
    }

    /**
     * Returns the members injector's name parameterized with the binding's parameters (if necessary).
     */
    static TypeName parameterizedMembersInjectorNameForMembersInjectionBinding(MembersInjectionBinding binding) {
        ClassName factoryName = membersInjectorNameForMembersInjectionBinding(binding);
        TypeName bindingName = TypeNames.forTypeMirror(binding.key().type());
        // If the binding is parameterized, parameterize the MembersInjector.
        if (bindingName instanceof ParameterizedTypeName) {
            return ParameterizedTypeName.create(factoryName, ((ParameterizedTypeName) bindingName).parameters());
        }
        return factoryName;
    }

    static ClassName membersInjectorNameForMembersInjectionBinding(MembersInjectionBinding binding) {
        ClassName injectedClassName = ClassName.fromTypeElement(binding.bindingElement());
        return injectedClassName.topLevelClassName()
                .peerNamed(injectedClassName.classFileName() + "_MembersInjector");
    }

    private static String factoryPrefix(ProvisionBinding binding) {
        switch (binding.bindingKind()) {
        case INJECTION:
            return "";
        case PROVISION:
            return CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL,
                    ((ExecutableElement) binding.bindingElement()).getSimpleName().toString());
        default:
            throw new IllegalArgumentException();
        }
    }

    private static String factoryPrefix(ProductionBinding binding) {
        switch (binding.bindingKind()) {
        case IMMEDIATE:
        case FUTURE_PRODUCTION:
            return CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL,
                    ((ExecutableElement) binding.bindingElement()).getSimpleName().toString());
        default:
            throw new IllegalArgumentException();
        }
    }

    private SourceFiles() {
    }
}