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