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

Commit

Permalink
mergeFrom() super type builder works across the packages
Browse files Browse the repository at this point in the history
  • Loading branch information
ph4r05 committed Dec 8, 2016
1 parent aa6f182 commit b1c924e
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 44 deletions.
37 changes: 18 additions & 19 deletions src/main/java/org/inferred/freebuilder/processor/Analyser.java
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ Metadata analyse(TypeElement type) throws CannotGenerateCodeException {
.addAllProperties(codeGenerators(properties, baseMetadata, builder.get()));

metadataBuilder.addAllSuperBuilderTypes(superBuilders(type));
metadataBuilder.putAllSuperTypeProperties(processSuperTypeProperties(type, baseMetadata, builder));
metadataBuilder.putAllSuperTypeProperties(
processSuperTypeProperties(type, baseMetadata, builder));
}
return metadataBuilder.build();
}
Expand All @@ -181,22 +182,24 @@ private ImmutableMap<ParameterizedType, ImmutableList<Property>> processSuperTyp
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>>();
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)){
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()){
final Map<ExecutableElement, Property> superPropertiesRet =
findProperties(superType, superMethods);
if (superPropertiesRet.isEmpty()) {
continue;
}

ParameterizedType pType = QualifiedName.of(superType).withParameters(superType.getTypeParameters());
ParameterizedType pType = QualifiedName.of(superType).withParameters(
superType.getTypeParameters());

// Code builder dance
if (builder.isPresent()) {
Expand Down Expand Up @@ -225,27 +228,23 @@ private ImmutableMap<ParameterizedType, ImmutableList<Property>> processSuperTyp
return ImmutableMap.copyOf(toRet);
}

private ImmutableSet<ParameterizedType> superBuilders(TypeElement type) throws CannotGenerateCodeException {
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)){
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());
if (freeBuilderMirror.isPresent()) {
ParameterizedType pType = QualifiedName.of(superType).withParameters(
superType.getTypeParameters());
toRet.add(pType);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,9 @@ public static String clearMethod(Property property) {
return "clear" + property.getCapitalizedName();
}

public static String isPropertySetMethod(Property property) {
return "isProperty" + property.getCapitalizedName() + "Set";
}

private BuilderMethods() {}
}
42 changes: 28 additions & 14 deletions src/main/java/org/inferred/freebuilder/processor/CodeGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.getLast;
import static com.google.common.collect.Iterables.getOnlyElement;
import static org.inferred.freebuilder.processor.BuilderMethods.isPropertySetMethod;
import static org.inferred.freebuilder.processor.BuilderFactory.TypeInference.EXPLICIT_TYPES;
import static org.inferred.freebuilder.processor.Metadata.GET_CODE_GENERATOR;
import static org.inferred.freebuilder.processor.Metadata.UnderrideLevel.ABSENT;
Expand Down Expand Up @@ -81,7 +82,7 @@ void writeBuilderSource(SourceBuilder code, Metadata metadata) {
addMergeFromBuilderMethod(code, metadata);
addMergeFromSuperTypes(code, metadata);
addClearMethod(code, metadata);
addIsPropertyUnsetMethod(code, metadata);
addPropertiesSetMethods(code, metadata);
addBuildMethod(code, metadata);
addBuildPartialMethod(code, metadata);

Expand Down Expand Up @@ -209,16 +210,19 @@ private static void addMergeFromBuilderMethod(SourceBuilder code, Metadata metad
}

private static void addMergeFromSuperTypes(SourceBuilder code, Metadata metadata) {
for(Map.Entry<ParameterizedType, ImmutableList<Property>> e : metadata.getSuperTypeProperties().entrySet()){
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(" * 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());
.addLine("public %s mergeFrom(%s value) {",
metadata.getBuilder(), type.getQualifiedName());
Block body = new Block(code);
for (Property property : properties) {
property.getCodeGenerator().addMergeFromSuperValue(body, "value");
Expand All @@ -228,7 +232,7 @@ private static void addMergeFromSuperTypes(SourceBuilder code, Metadata metadata
.addLine("}");

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

Expand Down Expand Up @@ -274,18 +278,28 @@ private static void addClearMethod(SourceBuilder code, Metadata metadata) {
.addLine("}");
}

private static void addIsPropertyUnsetMethod(SourceBuilder code, Metadata metadata) {
private static void addPropertiesSetMethods(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("}");

for (Property property : metadata.getProperties()) {
if (!IS_REQUIRED.apply(property)) {
continue;
}

code.addLine("")
.addLine("/**")
.addLine(" * Returns true if the required property corresponding to")
.addLine(" * %s is set. ", metadata.getType().javadocNoArgMethodLink(
property.getGetterName()))
.addLine(" */")
.addLine("public boolean %s() {", isPropertySetMethod(property))
.addLine(" return _unsetProperties.contains(%s.%s);",
metadata.getPropertyEnum(), property.getAllCapsName())
.addLine("}");
}
}

private static void addBuildPartialMethod(SourceBuilder code, Metadata metadata) {
Expand Down Expand Up @@ -313,7 +327,7 @@ private static void addBuildPartialMethod(SourceBuilder code, Metadata metadata)

private static void addPropertyEnum(Metadata metadata, SourceBuilder code) {
code.addLine("")
.addLine("enum %s {", metadata.getPropertyEnum().getSimpleName());
.addLine("private 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 @@ -19,6 +19,7 @@
import static org.inferred.freebuilder.processor.BuilderMethods.getter;
import static org.inferred.freebuilder.processor.BuilderMethods.mapper;
import static org.inferred.freebuilder.processor.BuilderMethods.setter;
import static org.inferred.freebuilder.processor.BuilderMethods.isPropertySetMethod;
import static org.inferred.freebuilder.processor.util.PreconditionExcerpts.checkNotNullInline;
import static org.inferred.freebuilder.processor.util.PreconditionExcerpts.checkNotNullPreamble;
import static org.inferred.freebuilder.processor.util.feature.FunctionPackage.FUNCTION_PACKAGE;
Expand Down Expand Up @@ -207,17 +208,26 @@ public void addMergeFromSuperBuilder(Block code, String builder) {
}

public void addMergeFromBuilder(Block code, String builder, boolean fromSuper) {
Excerpt base =
hasDefault ? null : Declarations.upcastToGeneratedBuilder(code, metadata, builder);
Excerpt base = hasDefault || fromSuper ? null : Declarations.upcastToGeneratedBuilder(
code, metadata, builder);
Excerpt defaults = Declarations.freshBuilder(code, metadata).orNull();
String unsetContains = fromSuper ? "isPropertyUnset" : "_unsetProperties.contains";
Block unsetContains = new Block(code);
String isPropertySetPositive = fromSuper ? "" : "!";
String isPropertySetNegative = fromSuper ? "!" : "";
if (fromSuper) {
unsetContains.add("%s()", isPropertySetMethod(property));
base = new Block(code).add(builder);
} else {
unsetContains.add("_unsetProperties.contains(%s.%s)",
metadata.getPropertyEnum(), property.getAllCapsName());
}
if (defaults != null) {
code.add("if (");
if (!hasDefault) {
code.add("!%s.%s(%s.%s) && ",
base, unsetContains, metadata.getPropertyEnum(), property.getAllCapsName())
.add("(%s.%s(%s.%s) ||",
defaults, unsetContains, metadata.getPropertyEnum(), property.getAllCapsName());
code.add("%s%s.%s && ",
isPropertySetPositive, base, unsetContains)
.add("(%s%s.%s ||",
isPropertySetNegative, defaults, unsetContains);
}
if (isPrimitive) {
code.add("%1$s.%2$s() != %3$s.%2$s()", builder, getter(property), defaults);
Expand All @@ -229,8 +239,8 @@ public void addMergeFromBuilder(Block code, String builder, boolean fromSuper) {
}
code.add(") {%n");
} else if (!hasDefault) {
code.addLine("if (!%s.%s(%s.%s)) {",
base, unsetContains, metadata.getPropertyEnum(), property.getAllCapsName());
code.addLine("if (%s%s.%s) {",
isPropertySetPositive, base, unsetContains);
}
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 @@ -369,7 +369,7 @@ public void addMergeFromBuilder(Block code, String builder) {

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1432,7 +1432,8 @@ public void mergeFromSuperType()
assertThat(metadata.getSuperTypeProperties()).hasSize(1);
assertThat(metadata.getSuperTypeProperties()).containsKey(superType);
assertThat(metadata.getSuperTypeProperties().get(superType)).hasSize(1);
assertThat(metadata.getSuperTypeProperties().get(superType).get(0).getName()).isEqualTo("alpha");
assertThat(metadata.getSuperTypeProperties().get(superType).get(0).getName()).
isEqualTo("alpha");
}

private static String asSource(Excerpt annotation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,22 @@ public void testJ6_noGuava_oneDefaultProperty() {
" }",
"",
" /**",
" * Returns true if the required property corresponding to",
" * {@link Person#getName()} is set.",
" */",
" public boolean isPropertyNameSet() {",
" return _unsetProperties.contains(Person_Builder.Property.NAME);",
" }",
"",
" /**",
" * Returns true if the required property corresponding to",
" * {@link Person#getShoeSize()} is set.",
" */",
" public boolean isPropertyShoeSizeSet() {",
" return _unsetProperties.contains(Person_Builder.Property.SHOE_SIZE);",
" }",
"",
" /**",
" * Returns a newly-created {@link Person} based on the contents of the {@code Builder}.",
" *",
" * @throws IllegalStateException if any field has not been set",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,22 @@ public void testJ6() {
" }",
"",
" /**",
" * Returns true if the required property corresponding to",
" * {@link Person#getName()} is set.",
" */",
" public boolean isPropertyNameSet() {",
" return _unsetProperties.contains(Person_Builder.Property.NAME);",
" }",
"",
" /**",
" * Returns true if the required property corresponding to",
" * {@link Person#getAge()} is set.",
" */",
" public boolean isPropertyAgeSet() {",
" return _unsetProperties.contains(Person_Builder.Property.AGE);",
" }",
"",
" /**",
" * Returns a newly-created {@link Person} based on the contents of the {@code Builder}.",
" *",
" * @throws IllegalStateException if any field has not been set",
Expand Down Expand Up @@ -448,6 +464,22 @@ public void testJ7() {
" }",
"",
" /**",
" * Returns true if the required property corresponding to",
" * {@link Person#getName()} is set.",
" */",
" public boolean isPropertyNameSet() {",
" return _unsetProperties.contains(Person_Builder.Property.NAME);",
" }",
"",
" /**",
" * Returns true if the required property corresponding to",
" * {@link Person#getAge()} is set.",
" */",
" public boolean isPropertyAgeSet() {",
" return _unsetProperties.contains(Person_Builder.Property.AGE);",
" }",
"",
" /**",
" * Returns a newly-created {@link Person} based on the contents of the {@code Builder}.",
" *",
" * @throws IllegalStateException if any field has not been set",
Expand Down Expand Up @@ -733,6 +765,22 @@ public void testJ8() {
" }",
"",
" /**",
" * Returns true if the required property corresponding to",
" * {@link Person#getName()} is set.",
" */",
" public boolean isPropertyNameSet() {",
" return _unsetProperties.contains(Person_Builder.Property.NAME);",
" }",
"",
" /**",
" * Returns true if the required property corresponding to",
" * {@link Person#getAge()} is set.",
" */",
" public boolean isPropertyAgeSet() {",
" return _unsetProperties.contains(Person_Builder.Property.AGE);",
" }",
"",
" /**",
" * Returns a newly-created {@link Person} based on the contents of the {@code Builder}.",
" *",
" * @throws IllegalStateException if any field has not been set",
Expand Down
Loading

0 comments on commit b1c924e

Please sign in to comment.