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