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