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