1 /* 2 * Copyright (C) 2018 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.Verify.verify; 20 21 import com.google.auto.common.MoreTypes; 22 import com.google.auto.value.AutoValue; 23 import com.google.common.base.Equivalence; 24 import com.google.common.collect.ImmutableList; 25 import com.google.common.collect.ImmutableMap; 26 import com.google.common.collect.Maps; 27 import com.squareup.javapoet.MethodSpec; 28 import java.util.Map; 29 import java.util.Optional; 30 import javax.lang.model.type.TypeMirror; 31 32 /** 33 * A registry for those methods which each wrap a binding whose definition may be modified across 34 * each class in the class hierarchy implementing a subcomponent. Subcomponent implementations are 35 * spread across a class hierarchy when generating ahead-of-time subcomponents. There is one 36 * subcomponent implementation class for each of the subcomponent's ancestor components. An instance 37 * of {@link ModifiableBindingMethod} is associated with a single class in this hierarchy. For a 38 * given subcomponent implementation class we can use the {@link ModifiableBindingMethod}s of its 39 * superclasses to know what binding methods to attempt to modify. 40 */ 41 final class ModifiableBindingMethods { 42 private final Map<BindingRequest, ModifiableBindingMethod> methods = Maps.newLinkedHashMap(); 43 44 /** Registers a new method encapsulating a modifiable binding. */ addModifiableMethod( ModifiableBindingType type, BindingRequest request, TypeMirror returnType, MethodSpec method, boolean finalized)45 void addModifiableMethod( 46 ModifiableBindingType type, 47 BindingRequest request, 48 TypeMirror returnType, 49 MethodSpec method, 50 boolean finalized) { 51 // It's ok for the type to not be modifiable, since it could be overriding a previously 52 // modifiable method (such as with addReimplementedMethod). 53 addMethod(ModifiableBindingMethod.create(type, request, returnType, method, finalized)); 54 } 55 56 /** Registers a reimplemented modifiable method. */ addReimplementedMethod(ModifiableBindingMethod method)57 void addReimplementedMethod(ModifiableBindingMethod method) { 58 addMethod(method); 59 } 60 addMethod(ModifiableBindingMethod method)61 private void addMethod(ModifiableBindingMethod method) { 62 ModifiableBindingMethod previousMethod = methods.put(method.request(), method); 63 verify( 64 previousMethod == null, 65 "registering %s but %s is already registered for the same binding request", 66 method, 67 previousMethod); 68 } 69 70 /** Returns all {@link ModifiableBindingMethod}s that have not been marked as finalized. */ getNonFinalizedMethods()71 ImmutableMap<BindingRequest, ModifiableBindingMethod> getNonFinalizedMethods() { 72 return ImmutableMap.copyOf(Maps.filterValues(methods, m -> !m.finalized())); 73 } 74 75 /** Returns the {@link ModifiableBindingMethod} for the given binding if present. */ getMethod(BindingRequest request)76 Optional<ModifiableBindingMethod> getMethod(BindingRequest request) { 77 return Optional.ofNullable(methods.get(request)); 78 } 79 80 /** Returns all of the {@link ModifiableBindingMethod}s. */ allMethods()81 ImmutableList<ModifiableBindingMethod> allMethods() { 82 return ImmutableList.copyOf(methods.values()); 83 } 84 85 /** Whether a given binding has been marked as finalized. */ 86 // TODO(ronshapiro): possibly rename this to something that indicates that the BindingRequest for 87 // `method` has been finalized in *this* component implementation? finalized(ModifiableBindingMethod method)88 boolean finalized(ModifiableBindingMethod method) { 89 ModifiableBindingMethod storedMethod = methods.get(method.request()); 90 return storedMethod != null && storedMethod.finalized(); 91 } 92 93 @AutoValue 94 abstract static class ModifiableBindingMethod { create( ModifiableBindingType type, BindingRequest request, TypeMirror returnType, MethodSpec methodSpec, boolean finalized)95 private static ModifiableBindingMethod create( 96 ModifiableBindingType type, 97 BindingRequest request, 98 TypeMirror returnType, 99 MethodSpec methodSpec, 100 boolean finalized) { 101 return new AutoValue_ModifiableBindingMethods_ModifiableBindingMethod( 102 type, request, MoreTypes.equivalence().wrap(returnType), methodSpec, finalized); 103 } 104 105 /** Creates a {@ModifiableBindingMethod} that reimplements the current method. */ reimplement( ModifiableBindingType newModifiableBindingType, MethodSpec newImplementation, boolean finalized)106 ModifiableBindingMethod reimplement( 107 ModifiableBindingType newModifiableBindingType, 108 MethodSpec newImplementation, 109 boolean finalized) { 110 return new AutoValue_ModifiableBindingMethods_ModifiableBindingMethod( 111 newModifiableBindingType, request(), returnTypeWrapper(), newImplementation, finalized); 112 } 113 type()114 abstract ModifiableBindingType type(); 115 request()116 abstract BindingRequest request(); 117 returnType()118 final TypeMirror returnType() { 119 return returnTypeWrapper().get(); 120 } 121 returnTypeWrapper()122 abstract Equivalence.Wrapper<TypeMirror> returnTypeWrapper(); 123 methodSpec()124 abstract MethodSpec methodSpec(); 125 finalized()126 abstract boolean finalized(); 127 128 /** Whether a {@link ModifiableBindingMethod} is for the same binding request. */ fulfillsSameRequestAs(ModifiableBindingMethod other)129 boolean fulfillsSameRequestAs(ModifiableBindingMethod other) { 130 return request().equals(other.request()); 131 } 132 } 133 } 134