Skip to content
This repository has been archived by the owner on Oct 16, 2024. It is now read-only.

Commit

Permalink
mergeFrom() added for super types and super type builders
Browse files Browse the repository at this point in the history
- merge from super types builders is allowed only if the type and super type are in the same package due to Enum Property visibility and non-default values.
  • Loading branch information
ph4r05 committed Dec 8, 2016
1 parent 5313ce1 commit aa6f182
Show file tree
Hide file tree
Showing 17 changed files with 557 additions and 16 deletions.
83 changes: 83 additions & 0 deletions src/main/java/org/inferred/freebuilder/processor/Analyser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -166,10 +170,89 @@ 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 {
// For mergeFrom - iterate all super types, add properties from all supertypes.
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()));
}

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);

// For now we support mergeFrom(superBuilder) only for builders from the same package
// Due to package local visibility of the Enum Property
if (!pkg.getQualifiedName().contentEquals(elements.getPackageOf(superType).getQualifiedName())){
continue;
}

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())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ public void addMergeFromValue(Block code, String value) {
code.addLine("%s.mergeFrom(%s.%s());", propertyName, value, property.getGetterName());
}

@Override
public void addMergeFromSuperValue(Block code, String value) {
addMergeFromValue(code, value);
}

@Override
public void addMergeFromBuilder(Block code, String builder) {
String propertyName = property.getName();
Expand All @@ -285,6 +290,11 @@ public void addMergeFromBuilder(Block code, String builder) {
code.add(");\n");
}

@Override
public void addMergeFromSuperBuilder(Block code, String builder) {
addMergeFromBuilder(code, builder);
}

@Override
public void addSetFromResult(SourceBuilder code, String builder, String variable) {
code.addLine("%s.%s(%s);", builder, setter(property), variable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,14 @@
import org.inferred.freebuilder.processor.util.Excerpts;
import org.inferred.freebuilder.processor.util.PreconditionExcerpts;
import org.inferred.freebuilder.processor.util.SourceBuilder;
import org.inferred.freebuilder.processor.util.ParameterizedType;
import org.inferred.freebuilder.processor.util.QualifiedName;

import java.io.Serializable;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

Expand Down Expand Up @@ -76,7 +79,9 @@ void writeBuilderSource(SourceBuilder code, Metadata metadata) {
addAccessors(metadata, code);
addMergeFromValueMethod(code, metadata);
addMergeFromBuilderMethod(code, metadata);
addMergeFromSuperTypes(code, metadata);
addClearMethod(code, metadata);
addIsPropertyUnsetMethod(code, metadata);
addBuildMethod(code, metadata);
addBuildPartialMethod(code, metadata);

Expand Down Expand Up @@ -203,6 +208,48 @@ private static void addMergeFromBuilderMethod(SourceBuilder code, Metadata metad
.addLine("}");
}

private static void addMergeFromSuperTypes(SourceBuilder code, Metadata metadata) {
for(Map.Entry<ParameterizedType, ImmutableList<Property>> e : metadata.getSuperTypeProperties().entrySet()){
final ParameterizedType type = e.getKey();
final ImmutableList<Property> properties = e.getValue();

// mergeFrom - value
code.addLine("")
.addLine("/**")
.addLine(" * Sets all property values using the given {@code %s} as a template.", type.getQualifiedName())
.addLine(" */")
.addLine("public %s mergeFrom(%s value) {", metadata.getBuilder(), type.getQualifiedName());
Block body = new Block(code);
for (Property property : properties) {
property.getCodeGenerator().addMergeFromSuperValue(body, "value");
}
code.add(body)
.addLine(" return (%s) this;", metadata.getBuilder())
.addLine("}");

// has builder ?
if (!metadata.getSuperBuilderTypes().contains(type)){
continue;
}

// mergeFrom - builder
final QualifiedName builder = type.getQualifiedName().nestedType("Builder");
code.addLine("")
.addLine("/**")
.addLine(" * Copies values from the given {@code %s}.", builder.getSimpleName())
.addLine(" * Does not affect any properties not set on the input.")
.addLine(" */")
.addLine("public %s mergeFrom(%s template) {", metadata.getBuilder(), builder);
Block fromBuilderBody = new Block(code);
for (Property property : properties) {
property.getCodeGenerator().addMergeFromSuperBuilder(fromBuilderBody, "template");
}
code.add(fromBuilderBody)
.addLine(" return (%s) this;", metadata.getBuilder())
.addLine("}");
}
}

private static void addClearMethod(SourceBuilder code, Metadata metadata) {
code.addLine("")
.addLine("/**")
Expand All @@ -227,6 +274,20 @@ private static void addClearMethod(SourceBuilder code, Metadata metadata) {
.addLine("}");
}

private static void addIsPropertyUnsetMethod(SourceBuilder code, Metadata metadata) {
if (!any(metadata.getProperties(), IS_REQUIRED)) {
return;

}
code.addLine("")
.addLine("/**")
.addLine(" * Returns true if the required property is not set.")
.addLine(" */")
.addLine("public boolean isPropertyUnset(%s property) {", metadata.getPropertyEnum())
.addLine(" return _unsetProperties.contains(property);")
.addLine("}");
}

private static void addBuildPartialMethod(SourceBuilder code, Metadata metadata) {
code.addLine("")
.addLine("/**")
Expand All @@ -252,7 +313,7 @@ private static void addBuildPartialMethod(SourceBuilder code, Metadata metadata)

private static void addPropertyEnum(Metadata metadata, SourceBuilder code) {
code.addLine("")
.addLine("private enum %s {", metadata.getPropertyEnum().getSimpleName());
.addLine("enum %s {", metadata.getPropertyEnum().getSimpleName());
for (Property property : metadata.getProperties()) {
if (property.getCodeGenerator().getType() == Type.REQUIRED) {
code.addLine(" %s(\"%s\"),", property.getAllCapsName(), property.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,18 +191,33 @@ public void addMergeFromValue(Block code, String value) {
}
}

@Override
public void addMergeFromSuperValue(Block code, String value) {
addMergeFromValue(code, value);
}

@Override
public void addMergeFromBuilder(Block code, String builder) {
addMergeFromBuilder(code, builder, false);
}

@Override
public void addMergeFromSuperBuilder(Block code, String builder) {
addMergeFromBuilder(code, builder, true);
}

public void addMergeFromBuilder(Block code, String builder, boolean fromSuper) {
Excerpt base =
hasDefault ? null : Declarations.upcastToGeneratedBuilder(code, metadata, builder);
Excerpt defaults = Declarations.freshBuilder(code, metadata).orNull();
String unsetContains = fromSuper ? "isPropertyUnset" : "_unsetProperties.contains";
if (defaults != null) {
code.add("if (");
if (!hasDefault) {
code.add("!%s._unsetProperties.contains(%s.%s) && ",
base, metadata.getPropertyEnum(), property.getAllCapsName())
.add("(%s._unsetProperties.contains(%s.%s) ||",
defaults, metadata.getPropertyEnum(), property.getAllCapsName());
code.add("!%s.%s(%s.%s) && ",
base, unsetContains, metadata.getPropertyEnum(), property.getAllCapsName())
.add("(%s.%s(%s.%s) ||",
defaults, unsetContains, metadata.getPropertyEnum(), property.getAllCapsName());
}
if (isPrimitive) {
code.add("%1$s.%2$s() != %3$s.%2$s()", builder, getter(property), defaults);
Expand All @@ -214,8 +229,8 @@ public void addMergeFromBuilder(Block code, String builder) {
}
code.add(") {%n");
} else if (!hasDefault) {
code.addLine("if (!%s._unsetProperties.contains(%s.%s)) {",
base, metadata.getPropertyEnum(), property.getAllCapsName());
code.addLine("if (!%s.%s(%s.%s)) {",
base, unsetContains, metadata.getPropertyEnum(), property.getAllCapsName());
}
code.addLine(" %s(%s.%s());", setter(property), builder, getter(property));
if (defaults != null || !hasDefault) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@ public void addMergeFromValue(Block code, String value) {
code.addLine("%s(%s.%s());", putAllMethod(property), value, property.getGetterName());
}

@Override
public void addMergeFromSuperValue(Block code, String value) {
addMergeFromValue(code, value);
}

@Override
public void addMergeFromBuilder(Block code, String builder) {
code.addLine("%s(((%s) %s).%s);",
Expand All @@ -382,6 +387,15 @@ public void addMergeFromBuilder(Block code, String builder) {
property.getName());
}

@Override
public void addMergeFromSuperBuilder(Block code, String builder) {
code.addLine("%s(((%s) %s).%s());",
putAllMethod(property),
metadata.getGeneratedBuilder(),
builder,
getter(property));
}

@Override
public void addSetFromResult(SourceBuilder code, String builder, String variable) {
code.addLine("%s.%s(%s);", builder, putAllMethod(property), variable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,8 @@ public void addFinalFieldAssignment(SourceBuilder code, String finalField, Strin
}
}

@Override
public void addMergeFromValue(Block code, String value) {
if (code.feature(GUAVA).isAvailable()) {
private void addMergeFromValue(Block code, String value, boolean guavaMerge) {
if (guavaMerge) {
code.addLine("if (%s instanceof %s && %s == %s.<%s>of()) {",
value,
metadata.getValueType(),
Expand All @@ -347,17 +346,33 @@ public void addMergeFromValue(Block code, String value) {
.addLine("} else {");
}
code.addLine("%s(%s.%s());", addAllMethod(property), value, property.getGetterName());
if (code.feature(GUAVA).isAvailable()) {
if (guavaMerge) {
code.addLine("}");
}
}

@Override
public void addMergeFromValue(Block code, String value) {
addMergeFromValue(code, value, code.feature(GUAVA).isAvailable());
}

@Override
public void addMergeFromSuperValue(Block code, String value) {
addMergeFromValue(code, value, false);
}

@Override
public void addMergeFromBuilder(Block code, String builder) {
Excerpt base = Declarations.upcastToGeneratedBuilder(code, metadata, builder);
code.addLine("%s(%s.%s);", addAllMethod(property), base, property.getName());
}

@Override
public void addMergeFromSuperBuilder(Block code, String builder) {
Excerpt base = Declarations.upcastToGeneratedBuilder(code, metadata, builder);
code.addLine("%s(%s.%s());", addAllMethod(property), base, getter(property));
}

@Override
public void addSetFromResult(SourceBuilder code, String builder, String variable) {
code.addLine("%s.%s(%s);", builder, addAllMethod(property), variable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,11 @@ public void addMergeFromValue(Block code, String value) {
code.addLine("%s(%s.%s());", putAllMethod(property), value, property.getGetterName());
}

@Override
public void addMergeFromSuperValue(Block code, String value) {
addMergeFromValue(code, value);
}

@Override
public void addMergeFromBuilder(Block code, String builder) {
code.addLine("%s(((%s) %s).%s);",
Expand All @@ -316,6 +321,15 @@ public void addMergeFromBuilder(Block code, String builder) {
property.getName());
}

@Override
public void addMergeFromSuperBuilder(Block code, String builder) {
code.addLine("%s(((%s) %s).%s());",
putAllMethod(property),
metadata.getGeneratedBuilder(),
builder,
getter(property));
}

@Override
public void addSetFromResult(SourceBuilder code, String builder, String variable) {
code.addLine("%s.%s(%s);", builder, putAllMethod(property), variable);
Expand Down
Loading

0 comments on commit aa6f182

Please sign in to comment.