-
Notifications
You must be signed in to change notification settings - Fork 101
mergeFrom() on supertypes and their builders #220
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,7 @@ | |
import static org.inferred.freebuilder.processor.util.ModelUtils.getReturnType; | ||
import static org.inferred.freebuilder.processor.util.ModelUtils.maybeAsTypeElement; | ||
import static org.inferred.freebuilder.processor.util.ModelUtils.maybeType; | ||
import static org.inferred.freebuilder.processor.util.ModelUtils.findAnnotationMirror; | ||
|
||
import com.google.common.base.Optional; | ||
import com.google.common.base.Predicate; | ||
|
@@ -43,6 +44,7 @@ | |
import com.google.common.collect.ImmutableSet; | ||
import com.google.common.collect.Sets; | ||
|
||
import org.inferred.freebuilder.FreeBuilder; | ||
import org.inferred.freebuilder.processor.Metadata.Property; | ||
import org.inferred.freebuilder.processor.Metadata.StandardMethod; | ||
import org.inferred.freebuilder.processor.Metadata.UnderrideLevel; | ||
|
@@ -53,6 +55,8 @@ | |
|
||
import java.io.Serializable; | ||
import java.util.LinkedHashMap; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
@@ -166,10 +170,88 @@ Metadata analyse(TypeElement type) throws CannotGenerateCodeException { | |
metadataBuilder | ||
.clearProperties() | ||
.addAllProperties(codeGenerators(properties, baseMetadata, builder.get())); | ||
|
||
metadataBuilder.addAllSuperBuilderTypes(superBuilders(type)); | ||
metadataBuilder.putAllSuperTypeProperties( | ||
processSuperTypeProperties(type, baseMetadata, builder)); | ||
} | ||
return metadataBuilder.build(); | ||
} | ||
|
||
private ImmutableMap<ParameterizedType, ImmutableList<Property>> processSuperTypeProperties( | ||
TypeElement type, | ||
Metadata baseMetadata, | ||
Optional<TypeElement> builder) throws CannotGenerateCodeException { | ||
Map<ParameterizedType, ImmutableList<Property>> toRet = | ||
new HashMap<ParameterizedType, ImmutableList<Property>>(); | ||
|
||
final ImmutableSet<TypeElement> superTypes = MethodFinder.getSupertypes(type); | ||
for (TypeElement superType : superTypes) { | ||
if (superType.equals(type)) { | ||
continue; | ||
} | ||
|
||
final ImmutableSet<ExecutableElement> superMethods = methodsOn(superType, elements); | ||
final Map<ExecutableElement, Property> superPropertiesRet = | ||
findProperties(superType, superMethods); | ||
if (superPropertiesRet.isEmpty()) { | ||
continue; | ||
} | ||
|
||
ParameterizedType pType = QualifiedName.of(superType).withParameters( | ||
superType.getTypeParameters()); | ||
|
||
// Code builder dance | ||
if (builder.isPresent()) { | ||
final Metadata metadataSuperType = analyse(superType); | ||
final Metadata.Builder metadataBld = Metadata.Builder.from(metadataSuperType); | ||
metadataBld.setBuilderFactory(Optional.<BuilderFactory>absent()); | ||
|
||
for (Map.Entry<ExecutableElement, Property> entry : superPropertiesRet.entrySet()) { | ||
Config config = new ConfigImpl( | ||
builder.get(), | ||
metadataBld.build(), | ||
entry.getValue(), | ||
entry.getKey(), | ||
ImmutableSet.<String>of()); | ||
|
||
entry.setValue(new Property.Builder() | ||
.mergeFrom(entry.getValue()) | ||
.setCodeGenerator(createCodeGenerator(config)) | ||
.build()); | ||
} | ||
} | ||
|
||
toRet.put(pType, ImmutableList.copyOf(superPropertiesRet.values())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't want to generate a merge method for every single supertype, only those which declare a getter method. |
||
} | ||
|
||
return ImmutableMap.copyOf(toRet); | ||
} | ||
|
||
private ImmutableSet<ParameterizedType> superBuilders(TypeElement type) | ||
throws CannotGenerateCodeException { | ||
Set<ParameterizedType> toRet = new HashSet<ParameterizedType>(); | ||
PackageElement pkg = elements.getPackageOf(type); | ||
|
||
final ImmutableSet<TypeElement> superTypes = MethodFinder.getSupertypes(type); | ||
for (TypeElement superType : superTypes) { | ||
if (superType.equals(type)) { | ||
continue; | ||
} | ||
|
||
final Optional<AnnotationMirror> freeBuilderMirror = | ||
findAnnotationMirror(superType, FreeBuilder.class); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This won't work: the FreeBuilder annotation is source-only. If your two types are not compiled together, or if your IDE decides to compile the two classes separately (e.g. you change your subtype in Eclipse), you'll be given the class definition from the compiled JAR. Check out the logic in BuildablePropertyFactory, for example. In this case, however, you'll need to check for the presence of all the getter methods too, since a user might have generated a broken builder by hand. |
||
|
||
if (freeBuilderMirror.isPresent()) { | ||
ParameterizedType pType = QualifiedName.of(superType).withParameters( | ||
superType.getTypeParameters()); | ||
toRet.add(pType); | ||
} | ||
} | ||
|
||
return ImmutableSet.copyOf(toRet); | ||
} | ||
|
||
private static Set<QualifiedName> visibleTypesIn(TypeElement type) { | ||
ImmutableSet.Builder<QualifiedName> visibleTypes = ImmutableSet.builder(); | ||
for (TypeElement nestedType : typesIn(type.getEnclosedElements())) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've already figured out what properties are implied by what getter methods; we should just check to see which of those methods are declared abstract on this supertype, rather than regenerating everything.
Also, you can't call analyse on non-FreeBuilder-annotated types, as they shouldn't be expected to follow FreeBuilder's rules about abstract methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and covariant return types are going to cause issues. You can either identify those occasions where runtime type checking is going to be safe (not forgetting optionals, collections, etc. can typecheck members), or bail out entirely if the return type doesn't exactly match what you'd expect. I'd recommend the latter, especially as a first PR.