• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 Google LLC
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 package com.google.auto.factory.processor;
17 
18 import com.google.auto.value.AutoValue;
19 import com.google.common.base.CharMatcher;
20 import com.google.common.collect.ImmutableBiMap;
21 import com.google.common.collect.ImmutableMap;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.ImmutableSetMultimap;
24 import com.google.common.collect.Iterables;
25 import com.google.common.collect.Sets;
26 import com.google.common.collect.Streams;
27 import java.util.HashSet;
28 import java.util.Optional;
29 import java.util.Set;
30 import javax.lang.model.element.AnnotationMirror;
31 import javax.lang.model.type.TypeMirror;
32 
33 /**
34  * A value object representing a factory to be generated.
35  *
36  * @author Gregory Kick
37  */
38 @AutoValue
39 abstract class FactoryDescriptor {
40   private static final CharMatcher invalidIdentifierCharacters =
41       new CharMatcher() {
42         @Override
43         public boolean matches(char c) {
44           return !Character.isJavaIdentifierPart(c);
45         }
46       };
47 
name()48   abstract PackageAndClass name();
49 
extendingType()50   abstract TypeMirror extendingType();
51 
implementingTypes()52   abstract ImmutableSet<TypeMirror> implementingTypes();
53 
publicType()54   abstract boolean publicType();
55 
methodDescriptors()56   abstract ImmutableSet<FactoryMethodDescriptor> methodDescriptors();
57 
implementationMethodDescriptors()58   abstract ImmutableSet<ImplementationMethodDescriptor> implementationMethodDescriptors();
59 
allowSubclasses()60   abstract boolean allowSubclasses();
61 
providers()62   abstract ImmutableMap<Key, ProviderField> providers();
63 
declaration()64   final AutoFactoryDeclaration declaration() {
65     // There is always at least one method descriptor.
66     return methodDescriptors().iterator().next().declaration();
67   }
68 
69   private static class UniqueNameSet {
70     private final Set<String> uniqueNames = new HashSet<>();
71 
72     /**
73      * Generates a unique name using {@code base}. If {@code base} has not yet been added, it will
74      * be returned as-is. If your {@code base} is healthy, this will always return {@code base}.
75      */
getUniqueName(CharSequence base)76     String getUniqueName(CharSequence base) {
77       String name = base.toString();
78       for (int differentiator = 2; !uniqueNames.add(name); differentiator++) {
79         name = base.toString() + differentiator;
80       }
81       return name;
82     }
83   }
84 
create( PackageAndClass name, TypeMirror extendingType, ImmutableSet<TypeMirror> implementingTypes, boolean publicType, ImmutableSet<FactoryMethodDescriptor> methodDescriptors, ImmutableSet<ImplementationMethodDescriptor> implementationMethodDescriptors, boolean allowSubclasses)85   static FactoryDescriptor create(
86       PackageAndClass name,
87       TypeMirror extendingType,
88       ImmutableSet<TypeMirror> implementingTypes,
89       boolean publicType,
90       ImmutableSet<FactoryMethodDescriptor> methodDescriptors,
91       ImmutableSet<ImplementationMethodDescriptor> implementationMethodDescriptors,
92       boolean allowSubclasses) {
93     ImmutableSetMultimap.Builder<Key, Parameter> parametersForProviders =
94         ImmutableSetMultimap.builder();
95     for (FactoryMethodDescriptor descriptor : methodDescriptors) {
96       for (Parameter parameter : descriptor.providedParameters()) {
97         parametersForProviders.put(parameter.key(), parameter);
98       }
99     }
100     ImmutableMap.Builder<Key, ProviderField> providersBuilder = ImmutableMap.builder();
101     UniqueNameSet uniqueNames = new UniqueNameSet();
102     parametersForProviders
103         .build()
104         .asMap()
105         .forEach(
106             (key, parameters) -> {
107               switch (parameters.size()) {
108                 case 0:
109                   throw new AssertionError();
110                 case 1:
111                   Parameter parameter = Iterables.getOnlyElement(parameters);
112                   providersBuilder.put(
113                       key,
114                       ProviderField.create(
115                           uniqueNames.getUniqueName(parameter.name() + "Provider"),
116                           key,
117                           parameter.nullable()));
118                   break;
119                 default:
120                   String providerName =
121                       uniqueNames.getUniqueName(
122                           invalidIdentifierCharacters.replaceFrom(key.toString(), '_')
123                               + "Provider");
124                   Optional<AnnotationMirror> nullable =
125                       parameters.stream()
126                           .map(Parameter::nullable)
127                           .flatMap(Streams::stream)
128                           .findFirst();
129                   providersBuilder.put(key, ProviderField.create(providerName, key, nullable));
130                   break;
131               }
132             });
133 
134     ImmutableBiMap<FactoryMethodDescriptor, ImplementationMethodDescriptor>
135         duplicateMethodDescriptors =
136             createDuplicateMethodDescriptorsBiMap(
137                 methodDescriptors, implementationMethodDescriptors);
138 
139     ImmutableSet<FactoryMethodDescriptor> deduplicatedMethodDescriptors =
140         getDeduplicatedMethodDescriptors(methodDescriptors, duplicateMethodDescriptors);
141 
142     ImmutableSet<ImplementationMethodDescriptor> deduplicatedImplementationMethodDescriptors =
143         Sets.difference(implementationMethodDescriptors, duplicateMethodDescriptors.values())
144             .immutableCopy();
145 
146     return new AutoValue_FactoryDescriptor(
147         name,
148         extendingType,
149         implementingTypes,
150         publicType,
151         deduplicatedMethodDescriptors,
152         deduplicatedImplementationMethodDescriptors,
153         allowSubclasses,
154         providersBuilder.build());
155   }
156 
157   /**
158    * Creates a bi-map of duplicate {@link ImplementationMethodDescriptor}s by their respective
159    * {@link FactoryMethodDescriptor}.
160    */
161   private static ImmutableBiMap<FactoryMethodDescriptor, ImplementationMethodDescriptor>
createDuplicateMethodDescriptorsBiMap( ImmutableSet<FactoryMethodDescriptor> factoryMethodDescriptors, ImmutableSet<ImplementationMethodDescriptor> implementationMethodDescriptors)162       createDuplicateMethodDescriptorsBiMap(
163           ImmutableSet<FactoryMethodDescriptor> factoryMethodDescriptors,
164           ImmutableSet<ImplementationMethodDescriptor> implementationMethodDescriptors) {
165 
166     ImmutableBiMap.Builder<FactoryMethodDescriptor, ImplementationMethodDescriptor> builder =
167         ImmutableBiMap.builder();
168 
169     for (FactoryMethodDescriptor factoryMethodDescriptor : factoryMethodDescriptors) {
170       for (ImplementationMethodDescriptor implementationMethodDescriptor :
171           implementationMethodDescriptors) {
172 
173         boolean areDuplicateMethodDescriptors =
174             areDuplicateMethodDescriptors(factoryMethodDescriptor, implementationMethodDescriptor);
175         if (areDuplicateMethodDescriptors) {
176           builder.put(factoryMethodDescriptor, implementationMethodDescriptor);
177           break;
178         }
179       }
180     }
181 
182     return builder.build();
183   }
184 
185   /**
186    * Returns a set of deduplicated {@link FactoryMethodDescriptor}s from the set of original
187    * descriptors and the bi-map of duplicate descriptors.
188    *
189    * <p>Modifies the duplicate {@link FactoryMethodDescriptor}s such that they are overriding and
190    * reflect properties from the {@link ImplementationMethodDescriptor} they are implementing.
191    */
getDeduplicatedMethodDescriptors( ImmutableSet<FactoryMethodDescriptor> methodDescriptors, ImmutableBiMap<FactoryMethodDescriptor, ImplementationMethodDescriptor> duplicateMethodDescriptors)192   private static ImmutableSet<FactoryMethodDescriptor> getDeduplicatedMethodDescriptors(
193       ImmutableSet<FactoryMethodDescriptor> methodDescriptors,
194       ImmutableBiMap<FactoryMethodDescriptor, ImplementationMethodDescriptor>
195           duplicateMethodDescriptors) {
196 
197     ImmutableSet.Builder<FactoryMethodDescriptor> deduplicatedMethodDescriptors =
198         ImmutableSet.builder();
199 
200     for (FactoryMethodDescriptor methodDescriptor : methodDescriptors) {
201       ImplementationMethodDescriptor duplicateMethodDescriptor =
202           duplicateMethodDescriptors.get(methodDescriptor);
203 
204       FactoryMethodDescriptor newMethodDescriptor =
205           (duplicateMethodDescriptor != null)
206               ? methodDescriptor.toBuilder()
207                   .overridingMethod(true)
208                   .publicMethod(duplicateMethodDescriptor.publicMethod())
209                   .returnType(duplicateMethodDescriptor.returnType())
210                   .exceptions(duplicateMethodDescriptor.exceptions())
211                   .build()
212               : methodDescriptor;
213       deduplicatedMethodDescriptors.add(newMethodDescriptor);
214     }
215 
216     return deduplicatedMethodDescriptors.build();
217   }
218 
219   /**
220    * Returns true if the given {@link FactoryMethodDescriptor} and
221    * {@link ImplementationMethodDescriptor} are duplicates.
222    *
223    * <p>Descriptors are duplicates if they have the same name and if they have the same passed types
224    * in the same order.
225    */
areDuplicateMethodDescriptors( FactoryMethodDescriptor factory, ImplementationMethodDescriptor implementation)226   private static boolean areDuplicateMethodDescriptors(
227       FactoryMethodDescriptor factory, ImplementationMethodDescriptor implementation) {
228 
229     if (!factory.name().equals(implementation.name())) {
230       return false;
231     }
232 
233     // Descriptors are identical if they have the same passed types in the same order.
234     return Iterables.elementsEqual(
235         Iterables.transform(factory.passedParameters(), Parameter::type),
236         Iterables.transform(implementation.passedParameters(), Parameter::type));
237   }
238 }
239