From aa6f182c9761aa1caed5d90e1fc557768459140c Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Thu, 8 Dec 2016 16:36:25 +0100 Subject: [PATCH 1/3] mergeFrom() added for super types and super type builders - 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. --- .../freebuilder/processor/Analyser.java | 83 +++++++ .../processor/BuildablePropertyFactory.java | 10 + .../freebuilder/processor/CodeGenerator.java | 63 ++++- .../processor/DefaultPropertyFactory.java | 27 ++- .../ListMultimapPropertyFactory.java | 14 ++ .../processor/ListPropertyFactory.java | 23 +- .../processor/MapPropertyFactory.java | 14 ++ .../freebuilder/processor/Metadata.java | 6 + .../processor/Metadata_Builder.java | 219 ++++++++++++++++++ .../freebuilder/processor/MethodFinder.java | 2 +- .../processor/MultisetPropertyFactory.java | 14 ++ .../processor/NullablePropertyFactory.java | 10 + .../processor/OptionalPropertyFactory.java | 10 + .../processor/PropertyCodeGenerator.java | 6 + .../processor/SetMultimapPropertyFactory.java | 14 ++ .../processor/SetPropertyFactory.java | 26 ++- .../freebuilder/processor/AnalyserTest.java | 32 +++ 17 files changed, 557 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/inferred/freebuilder/processor/Analyser.java b/src/main/java/org/inferred/freebuilder/processor/Analyser.java index ce94c6337..1167ca6ac 100644 --- a/src/main/java/org/inferred/freebuilder/processor/Analyser.java +++ b/src/main/java/org/inferred/freebuilder/processor/Analyser.java @@ -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,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> processSuperTypeProperties( + TypeElement type, + Metadata baseMetadata, + Optional builder) throws CannotGenerateCodeException { + // For mergeFrom - iterate all super types, add properties from all supertypes. + Map> toRet = new HashMap>(); + + final ImmutableSet superTypes = MethodFinder.getSupertypes(type); + for(TypeElement superType : superTypes){ + if (superType.equals(type)){ + continue; + } + + final ImmutableSet superMethods = methodsOn(superType, elements); + final Map 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.absent()); + + for (Map.Entry entry : superPropertiesRet.entrySet()) { + Config config = new ConfigImpl( + builder.get(), + metadataBld.build(), + entry.getValue(), + entry.getKey(), + ImmutableSet.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 superBuilders(TypeElement type) throws CannotGenerateCodeException { + Set toRet = new HashSet(); + PackageElement pkg = elements.getPackageOf(type); + + final ImmutableSet superTypes = MethodFinder.getSupertypes(type); + for(TypeElement superType : superTypes){ + if (superType.equals(type)){ + continue; + } + + final Optional 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 visibleTypesIn(TypeElement type) { ImmutableSet.Builder visibleTypes = ImmutableSet.builder(); for (TypeElement nestedType : typesIn(type.getEnclosedElements())) { diff --git a/src/main/java/org/inferred/freebuilder/processor/BuildablePropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/BuildablePropertyFactory.java index 0f01f3e44..c300b0ee6 100644 --- a/src/main/java/org/inferred/freebuilder/processor/BuildablePropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/BuildablePropertyFactory.java @@ -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(); @@ -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); diff --git a/src/main/java/org/inferred/freebuilder/processor/CodeGenerator.java b/src/main/java/org/inferred/freebuilder/processor/CodeGenerator.java index cc29f0336..12ea0efa5 100644 --- a/src/main/java/org/inferred/freebuilder/processor/CodeGenerator.java +++ b/src/main/java/org/inferred/freebuilder/processor/CodeGenerator.java @@ -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; @@ -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); @@ -203,6 +208,48 @@ private static void addMergeFromBuilderMethod(SourceBuilder code, Metadata metad .addLine("}"); } + private static void addMergeFromSuperTypes(SourceBuilder code, Metadata metadata) { + for(Map.Entry> e : metadata.getSuperTypeProperties().entrySet()){ + final ParameterizedType type = e.getKey(); + final ImmutableList 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("/**") @@ -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("/**") @@ -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()); diff --git a/src/main/java/org/inferred/freebuilder/processor/DefaultPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/DefaultPropertyFactory.java index 882f726f2..c72385bae 100644 --- a/src/main/java/org/inferred/freebuilder/processor/DefaultPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/DefaultPropertyFactory.java @@ -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); @@ -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) { diff --git a/src/main/java/org/inferred/freebuilder/processor/ListMultimapPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/ListMultimapPropertyFactory.java index 9b8179b66..9703f3a12 100644 --- a/src/main/java/org/inferred/freebuilder/processor/ListMultimapPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/ListMultimapPropertyFactory.java @@ -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);", @@ -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); diff --git a/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java index f2f75803e..ece5b40c3 100644 --- a/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java @@ -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(), @@ -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); diff --git a/src/main/java/org/inferred/freebuilder/processor/MapPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/MapPropertyFactory.java index 24c452536..fe341f2f5 100644 --- a/src/main/java/org/inferred/freebuilder/processor/MapPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/MapPropertyFactory.java @@ -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);", @@ -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); diff --git a/src/main/java/org/inferred/freebuilder/processor/Metadata.java b/src/main/java/org/inferred/freebuilder/processor/Metadata.java index 933a6f04c..de9216ebb 100644 --- a/src/main/java/org/inferred/freebuilder/processor/Metadata.java +++ b/src/main/java/org/inferred/freebuilder/processor/Metadata.java @@ -135,6 +135,12 @@ public ParameterizedType getBuilder() { /** Returns metadata about the properties of the type. */ public abstract ImmutableList getProperties(); + /** Returns super types implementing builder */ + public abstract ImmutableSet getSuperBuilderTypes(); + + /** Returns super type properties */ + public abstract ImmutableMap> getSuperTypeProperties(); + public UnderrideLevel standardMethodUnderride(StandardMethod standardMethod) { UnderrideLevel underrideLevel = getStandardMethodUnderrides().get(standardMethod); return (underrideLevel == null) ? UnderrideLevel.ABSENT : underrideLevel; diff --git a/src/main/java/org/inferred/freebuilder/processor/Metadata_Builder.java b/src/main/java/org/inferred/freebuilder/processor/Metadata_Builder.java index 0dc4ba8d7..0508aac5a 100644 --- a/src/main/java/org/inferred/freebuilder/processor/Metadata_Builder.java +++ b/src/main/java/org/inferred/freebuilder/processor/Metadata_Builder.java @@ -83,6 +83,10 @@ public String toString() { new LinkedHashSet(); private ParameterizedType propertyEnum; private final ArrayList properties = new ArrayList(); + private Set superBuilderTypes = ImmutableSet.of(); + private final LinkedHashMap> + superTypeProperties = + new LinkedHashMap>(); private final LinkedHashMap standardMethodUnderrides = new LinkedHashMap(); @@ -486,6 +490,159 @@ public List getProperties() { return Collections.unmodifiableList(properties); } + /** + * Adds {@code element} to the set to be returned from {@link Metadata#getSuperBuilderTypes()}. + * If the set already contains {@code element}, then {@code addSuperBuilderTypes} + * has no effect (only the previously added element is retained). + * + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + */ + public Metadata.Builder addSuperBuilderTypes(ParameterizedType element) { + if (this.superBuilderTypes instanceof ImmutableSet) { + this.superBuilderTypes = new LinkedHashSet(this.superBuilderTypes); + } + this.superBuilderTypes.add(Preconditions.checkNotNull(element)); + return (Metadata.Builder) this; + } + + /** + * Adds each element of {@code elements} to the set to be returned from + * {@link Metadata#getSuperBuilderTypes()}, ignoring duplicate elements + * (only the first duplicate element is added). + * + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + public Metadata.Builder addSuperBuilderTypes(ParameterizedType... elements) { + return addAllSuperBuilderTypes(Arrays.asList(elements)); + } + + /** + * Adds each element of {@code elements} to the set to be returned from + * {@link Metadata#getSuperBuilderTypes()}, ignoring duplicate elements + * (only the first duplicate element is added). + * + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + public Metadata.Builder addAllSuperBuilderTypes(Iterable elements) { + for (ParameterizedType element : elements) { + addSuperBuilderTypes(element); + } + return (Metadata.Builder) this; + } + + /** + * Removes {@code element} from the set to be returned from {@link Metadata#getSuperBuilderTypes()}. + * Does nothing if {@code element} is not a member of the set. + * + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + */ + public Metadata.Builder removeSuperBuilderTypes(ParameterizedType element) { + if (this.superBuilderTypes instanceof ImmutableSet) { + this.superBuilderTypes = new LinkedHashSet(this.superBuilderTypes); + } + this.superBuilderTypes.remove(Preconditions.checkNotNull(element)); + return (Metadata.Builder) this; + } + + /** + * Clears the set to be returned from {@link Metadata#getSuperBuilderTypes()}. + * + * @return this {@code Builder} object + */ + public Metadata.Builder clearSuperBuilderTypes() { + if (superBuilderTypes instanceof ImmutableSet) { + superBuilderTypes = ImmutableSet.of(); + } else { + superBuilderTypes.clear(); + } + return (Metadata.Builder) this; + } + + /** + * Returns an unmodifiable view of the set that will be returned by + * {@link Metadata#getSuperBuilderTypes()}. + * Changes to this builder will be reflected in the view. + */ + public Set getSuperBuilderTypes() { + if (superBuilderTypes instanceof ImmutableSet) { + superBuilderTypes = new LinkedHashSet(superBuilderTypes); + } + return Collections.unmodifiableSet(superBuilderTypes); + } + + /** + * Associates {@code key} with {@code value} in the map to be returned from + * {@link Metadata#getSuperTypeProperties()}. + * If the map previously contained a mapping for the key, + * the old value is replaced by the specified value. + * + * @return this {@code Builder} object + * @throws NullPointerException if either {@code key} or {@code value} are null + */ + public Metadata.Builder putSuperTypeProperties( + ParameterizedType key, ImmutableList value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + superTypeProperties.put(key, value); + return (Metadata.Builder) this; + } + + /** + * Copies all of the mappings from {@code map} to the map to be returned from + * {@link Metadata#getSuperTypeProperties()}. + * + * @return this {@code Builder} object + * @throws NullPointerException if {@code map} is null or contains a + * null key or value + */ + public Metadata.Builder putAllSuperTypeProperties( + Map> map) { + for (Map.Entry> entry : + map.entrySet()) { + putSuperTypeProperties(entry.getKey(), entry.getValue()); + } + return (Metadata.Builder) this; + } + + /** + * Removes the mapping for {@code key} from the map to be returned from + * {@link Metadata#getSuperTypeProperties()}, if one is present. + * + * @return this {@code Builder} object + * @throws NullPointerException if {@code key} is null + */ + public Metadata.Builder removeSuperTypeProperties(ParameterizedType key) { + Preconditions.checkNotNull(key); + superTypeProperties.remove(key); + return (Metadata.Builder) this; + } + + /** + * Removes all of the mappings from the map to be returned from + * {@link Metadata#getSuperTypeProperties()}. + * + * @return this {@code Builder} object + */ + public Metadata.Builder clearSuperTypeProperties() { + superTypeProperties.clear(); + return (Metadata.Builder) this; + } + + /** + * Returns an unmodifiable view of the map that will be returned by + * {@link Metadata#getSuperTypeProperties()}. + * Changes to this builder will be reflected in the view. + */ + public Map> getSuperTypeProperties() { + return Collections.unmodifiableMap(superTypeProperties); + } + /** * Associates {@code key} with {@code value} in the map to be returned from * {@link Metadata#getStandardMethodUnderrides()}. @@ -833,6 +990,12 @@ public Metadata.Builder mergeFrom(Metadata value) { setPropertyEnum(value.getPropertyEnum()); } addAllProperties(value.getProperties()); + if (value instanceof Metadata_Builder.Value + && superBuilderTypes == ImmutableSet.of()) { + superBuilderTypes = value.getSuperBuilderTypes(); + } else { + addAllSuperBuilderTypes(value.getSuperBuilderTypes()); + } putAllStandardMethodUnderrides(value.getStandardMethodUnderrides()); if (_defaults._unsetProperties.contains(Metadata_Builder.Property.BUILDER_SERIALIZABLE) || value.isBuilderSerializable() != _defaults.isBuilderSerializable()) { @@ -894,6 +1057,8 @@ public Metadata.Builder mergeFrom(Metadata.Builder template) { setPropertyEnum(template.getPropertyEnum()); } addAllProperties(((Metadata_Builder) template).properties); + addAllSuperBuilderTypes(((Metadata_Builder) template).superBuilderTypes); + putAllSuperTypeProperties(((Metadata_Builder) template).superTypeProperties); putAllStandardMethodUnderrides(((Metadata_Builder) template).standardMethodUnderrides); if (!base._unsetProperties.contains(Metadata_Builder.Property.BUILDER_SERIALIZABLE) && (_defaults._unsetProperties.contains(Metadata_Builder.Property.BUILDER_SERIALIZABLE) @@ -926,6 +1091,8 @@ public Metadata.Builder clear() { visibleNestedTypes.clear(); propertyEnum = _defaults.propertyEnum; properties.clear(); + clearSuperBuilderTypes(); + superTypeProperties.clear(); standardMethodUnderrides.clear(); builderSerializable = _defaults.builderSerializable; generatedBuilderAnnotations.clear(); @@ -978,6 +1145,9 @@ private static final class Value extends Metadata { private final ImmutableSet visibleNestedTypes; private final ParameterizedType propertyEnum; private final ImmutableList properties; + private final ImmutableSet superBuilderTypes; + private final ImmutableMap> + superTypeProperties; private final ImmutableMap standardMethodUnderrides; private final boolean builderSerializable; @@ -997,6 +1167,8 @@ private Value(Metadata_Builder builder) { this.visibleNestedTypes = ImmutableSet.copyOf(builder.visibleNestedTypes); this.propertyEnum = builder.propertyEnum; this.properties = ImmutableList.copyOf(builder.properties); + this.superBuilderTypes = ImmutableSet.copyOf(builder.superBuilderTypes); + this.superTypeProperties = ImmutableMap.copyOf(builder.superTypeProperties); this.standardMethodUnderrides = ImmutableMap.copyOf(builder.standardMethodUnderrides); this.builderSerializable = builder.builderSerializable; this.generatedBuilderAnnotations = ImmutableList.copyOf(builder.generatedBuilderAnnotations); @@ -1055,6 +1227,17 @@ public ImmutableList getProperties() { return properties; } + @Override + public ImmutableSet getSuperBuilderTypes() { + return superBuilderTypes; + } + + @Override + public ImmutableMap> + getSuperTypeProperties() { + return superTypeProperties; + } + @Override public ImmutableMap getStandardMethodUnderrides() { @@ -1124,6 +1307,12 @@ public boolean equals(Object obj) { if (!properties.equals(other.properties)) { return false; } + if (!superBuilderTypes.equals(other.superBuilderTypes)) { + return false; + } + if (!superTypeProperties.equals(other.superTypeProperties)) { + return false; + } if (!standardMethodUnderrides.equals(other.standardMethodUnderrides)) { return false; } @@ -1159,6 +1348,8 @@ public int hashCode() { visibleNestedTypes, propertyEnum, properties, + superBuilderTypes, + superTypeProperties, standardMethodUnderrides, builderSerializable, generatedBuilderAnnotations, @@ -1182,6 +1373,8 @@ public String toString() { "visibleNestedTypes=" + visibleNestedTypes, "propertyEnum=" + propertyEnum, "properties=" + properties, + "superBuilderTypes=" + superBuilderTypes, + "superTypeProperties=" + superTypeProperties, "standardMethodUnderrides=" + standardMethodUnderrides, "builderSerializable=" + builderSerializable, "generatedBuilderAnnotations=" + generatedBuilderAnnotations, @@ -1209,6 +1402,9 @@ private static final class Partial extends Metadata { private final ImmutableSet visibleNestedTypes; private final ParameterizedType propertyEnum; private final ImmutableList properties; + private final ImmutableSet superBuilderTypes; + private final ImmutableMap> + superTypeProperties; private final ImmutableMap standardMethodUnderrides; private final boolean builderSerializable; @@ -1229,6 +1425,8 @@ private static final class Partial extends Metadata { this.visibleNestedTypes = ImmutableSet.copyOf(builder.visibleNestedTypes); this.propertyEnum = builder.propertyEnum; this.properties = ImmutableList.copyOf(builder.properties); + this.superBuilderTypes = ImmutableSet.copyOf(builder.superBuilderTypes); + this.superTypeProperties = ImmutableMap.copyOf(builder.superTypeProperties); this.standardMethodUnderrides = ImmutableMap.copyOf(builder.standardMethodUnderrides); this.builderSerializable = builder.builderSerializable; this.generatedBuilderAnnotations = ImmutableList.copyOf(builder.generatedBuilderAnnotations); @@ -1306,6 +1504,17 @@ public ImmutableList getProperties() { return properties; } + @Override + public ImmutableSet getSuperBuilderTypes() { + return superBuilderTypes; + } + + @Override + public ImmutableMap> + getSuperTypeProperties() { + return superTypeProperties; + } + @Override public ImmutableMap getStandardMethodUnderrides() { @@ -1385,6 +1594,12 @@ public boolean equals(Object obj) { if (!properties.equals(other.properties)) { return false; } + if (!superBuilderTypes.equals(other.superBuilderTypes)) { + return false; + } + if (!superTypeProperties.equals(other.superTypeProperties)) { + return false; + } if (!standardMethodUnderrides.equals(other.standardMethodUnderrides)) { return false; } @@ -1422,6 +1637,8 @@ public int hashCode() { visibleNestedTypes, propertyEnum, properties, + superBuilderTypes, + superTypeProperties, standardMethodUnderrides, builderSerializable, generatedBuilderAnnotations, @@ -1456,6 +1673,8 @@ public String toString() { ? "propertyEnum=" + propertyEnum : null), "properties=" + properties, + "superBuilderTypes=" + superBuilderTypes, + "superTypeProperties=" + superTypeProperties, "standardMethodUnderrides=" + standardMethodUnderrides, (!_unsetProperties.contains(Metadata_Builder.Property.BUILDER_SERIALIZABLE) ? "builderSerializable=" + builderSerializable diff --git a/src/main/java/org/inferred/freebuilder/processor/MethodFinder.java b/src/main/java/org/inferred/freebuilder/processor/MethodFinder.java index d4857410d..e8a2513fe 100644 --- a/src/main/java/org/inferred/freebuilder/processor/MethodFinder.java +++ b/src/main/java/org/inferred/freebuilder/processor/MethodFinder.java @@ -73,7 +73,7 @@ public static ImmutableSet methodsOn(TypeElement type, Elemen return ImmutableSet.copyOf(methods.values()); } - private static ImmutableSet getSupertypes(TypeElement type) + public static ImmutableSet getSupertypes(TypeElement type) throws CannotGenerateCodeException { Set supertypes = new LinkedHashSet(); addSupertypesToSet(type, supertypes); diff --git a/src/main/java/org/inferred/freebuilder/processor/MultisetPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/MultisetPropertyFactory.java index b042f8afb..e6dc39402 100644 --- a/src/main/java/org/inferred/freebuilder/processor/MultisetPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/MultisetPropertyFactory.java @@ -315,6 +315,11 @@ public void addMergeFromValue(Block code, String value) { code.addLine("%s(%s.%s());", addAllMethod(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);", @@ -324,6 +329,15 @@ public void addMergeFromBuilder(Block code, String builder) { property.getName()); } + @Override + public void addMergeFromSuperBuilder(Block code, String builder) { + code.addLine("%s(((%s) %s).%s());", + addAllMethod(property), + metadata.getGeneratedBuilder(), + builder, + getter(property)); + } + @Override public void addSetFromResult(SourceBuilder code, String builder, String variable) { code.addLine("%s.%s%s(%s);", builder, addAllMethod(property), variable); diff --git a/src/main/java/org/inferred/freebuilder/processor/NullablePropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/NullablePropertyFactory.java index 721d75d28..0be2f32da 100644 --- a/src/main/java/org/inferred/freebuilder/processor/NullablePropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/NullablePropertyFactory.java @@ -167,11 +167,21 @@ public void addMergeFromValue(Block code, String value) { code.addLine("%s(%s.%s());", setter(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());", setter(property), builder, getter(property)); } + @Override + public void addMergeFromSuperBuilder(Block code, String builder) { + addMergeFromBuilder(code, builder); + } + @Override public void addGetterAnnotations(SourceBuilder code) { for (TypeElement nullableAnnotation : nullables) { diff --git a/src/main/java/org/inferred/freebuilder/processor/OptionalPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/OptionalPropertyFactory.java index 1a6a3e063..8bdb37d2e 100644 --- a/src/main/java/org/inferred/freebuilder/processor/OptionalPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/OptionalPropertyFactory.java @@ -328,12 +328,22 @@ public void addMergeFromValue(Block code, String value) { optional.invokeIfPresent(code, propertyValue, setter(property)); } + @Override + public void addMergeFromSuperValue(Block code, String value) { + addMergeFromValue(code, value); + } + @Override public void addMergeFromBuilder(Block code, String builder) { String propertyValue = builder + "." + getter(property) + "()"; optional.invokeIfPresent(code, propertyValue, setter(property)); } + @Override + public void addMergeFromSuperBuilder(Block code, String builder) { + addMergeFromBuilder(code, builder); + } + @Override public void addReadValueFragment(SourceBuilder code, String finalField) { code.add("%s.", optional.cls); diff --git a/src/main/java/org/inferred/freebuilder/processor/PropertyCodeGenerator.java b/src/main/java/org/inferred/freebuilder/processor/PropertyCodeGenerator.java index 164818229..782aea773 100644 --- a/src/main/java/org/inferred/freebuilder/processor/PropertyCodeGenerator.java +++ b/src/main/java/org/inferred/freebuilder/processor/PropertyCodeGenerator.java @@ -119,9 +119,15 @@ public void addPartialFieldAssignment(SourceBuilder code, String finalField, Str /** Add a merge from value for the property to the builder's source code. */ public abstract void addMergeFromValue(Block code, String value); + /** Add a merge from value for the property to the builder's source code. */ + public abstract void addMergeFromSuperValue(Block code, String value); + /** Add a merge from builder for the property to the builder's source code. */ public abstract void addMergeFromBuilder(Block code, String builder); + /** Add a merge from super type builder for the property to the builder's source code. */ + public abstract void addMergeFromSuperBuilder(Block code, String builder); + /** Adds method annotations for the value type getter method. */ public void addGetterAnnotations(@SuppressWarnings("unused") SourceBuilder code) {} diff --git a/src/main/java/org/inferred/freebuilder/processor/SetMultimapPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/SetMultimapPropertyFactory.java index 0d14694bf..3b871d189 100644 --- a/src/main/java/org/inferred/freebuilder/processor/SetMultimapPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/SetMultimapPropertyFactory.java @@ -369,6 +369,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);", @@ -378,6 +383,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); diff --git a/src/main/java/org/inferred/freebuilder/processor/SetPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/SetPropertyFactory.java index d2f7ed06e..861577e09 100644 --- a/src/main/java/org/inferred/freebuilder/processor/SetPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/SetPropertyFactory.java @@ -338,20 +338,29 @@ public void addFinalFieldAssignment(SourceBuilder code, String finalField, Strin code.add("(%s.%s);\n", builder, property.getName()); } - @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(), property.getName(), ImmutableSet.class, elementType) .addLine(" %s = %s.%s();", property.getName(), value, property.getGetterName()) .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) { code.addLine("%s(((%s) %s).%s);", @@ -361,6 +370,15 @@ public void addMergeFromBuilder(Block code, String builder) { property.getName()); } + @Override + public void addMergeFromSuperBuilder(Block code, String builder) { + code.addLine("%s(((%s) %s).%s());", + addAllMethod(property), + metadata.getGeneratedBuilder(), + builder, + getter(property)); + } + @Override public void addSetFromResult(SourceBuilder code, String builder, String variable) { code.addLine("%s.%s(%s);", builder, addAllMethod(property), variable); diff --git a/src/test/java/org/inferred/freebuilder/processor/AnalyserTest.java b/src/test/java/org/inferred/freebuilder/processor/AnalyserTest.java index c1513c46f..6fe1c69bb 100644 --- a/src/test/java/org/inferred/freebuilder/processor/AnalyserTest.java +++ b/src/test/java/org/inferred/freebuilder/processor/AnalyserTest.java @@ -35,12 +35,14 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.inferred.freebuilder.FreeBuilder; import org.inferred.freebuilder.processor.Analyser.CannotGenerateCodeException; import org.inferred.freebuilder.processor.Metadata.Property; import org.inferred.freebuilder.processor.Metadata.StandardMethod; import org.inferred.freebuilder.processor.Metadata.UnderrideLevel; import org.inferred.freebuilder.processor.PropertyCodeGenerator.Type; import org.inferred.freebuilder.processor.util.Excerpt; +import org.inferred.freebuilder.processor.util.ParameterizedType; import org.inferred.freebuilder.processor.util.QualifiedName; import org.inferred.freebuilder.processor.util.SourceStringBuilder; import org.inferred.freebuilder.processor.util.testing.FakeMessager; @@ -1403,6 +1405,36 @@ public void valueTypeSuperclassesNestedClassesAddedToVisibleList() QualifiedName.of("com.example", "DataType_Builder", "Value")); } + @Test + public void mergeFromSuperType() + throws CannotGenerateCodeException { + model.newType( + "package com.example;", + "@" + FreeBuilder.class.getCanonicalName(), + "public abstract class SuperType {", + " public abstract String getAlpha();", + " public static class Builder extends SuperType_Builder {}", + "}"); + TypeElement dataType = model.newType( + "package com.example;", + "@" + FreeBuilder.class.getCanonicalName(), + "public abstract class DataType extends SuperType {", + " public abstract String getBeta();", + " public static class Builder extends DataType_Builder {}", + "}"); + + Metadata metadata = analyser.analyse(dataType); + + final ParameterizedType superType = QualifiedName.of("com.example", "SuperType") + .withParameters(ImmutableList.of()); + + assertThat(metadata.getSuperBuilderTypes()).containsExactly(superType); + 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"); + } + private static String asSource(Excerpt annotation) { return SourceStringBuilder.simple().add(annotation).toString().trim(); } From b1c924e15b143fa7b70eddf4c54b680b6c34047f Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Thu, 8 Dec 2016 17:36:10 +0100 Subject: [PATCH 2/3] mergeFrom() super type builder works across the packages --- .../freebuilder/processor/Analyser.java | 37 ++++--- .../freebuilder/processor/BuilderMethods.java | 4 + .../freebuilder/processor/CodeGenerator.java | 42 +++++--- .../processor/DefaultPropertyFactory.java | 28 ++++-- .../processor/ListPropertyFactory.java | 2 +- .../freebuilder/processor/AnalyserTest.java | 3 +- .../DefaultedPropertiesSourceTest.java | 16 ++++ .../processor/GenericTypeSourceTest.java | 48 ++++++++++ .../RequiredPropertiesSourceTest.java | 96 +++++++++++++++++++ 9 files changed, 232 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/inferred/freebuilder/processor/Analyser.java b/src/main/java/org/inferred/freebuilder/processor/Analyser.java index 1167ca6ac..c7659744d 100644 --- a/src/main/java/org/inferred/freebuilder/processor/Analyser.java +++ b/src/main/java/org/inferred/freebuilder/processor/Analyser.java @@ -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(); } @@ -181,22 +182,24 @@ private ImmutableMap> processSuperTyp TypeElement type, Metadata baseMetadata, Optional builder) throws CannotGenerateCodeException { - // For mergeFrom - iterate all super types, add properties from all supertypes. - Map> toRet = new HashMap>(); + Map> toRet = + new HashMap>(); final ImmutableSet superTypes = MethodFinder.getSupertypes(type); - for(TypeElement superType : superTypes){ - if (superType.equals(type)){ + for (TypeElement superType : superTypes) { + if (superType.equals(type)) { continue; } final ImmutableSet superMethods = methodsOn(superType, elements); - final Map superPropertiesRet = findProperties(superType, superMethods); - if (superPropertiesRet.isEmpty()){ + final Map 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()) { @@ -225,27 +228,23 @@ private ImmutableMap> processSuperTyp return ImmutableMap.copyOf(toRet); } - private ImmutableSet superBuilders(TypeElement type) throws CannotGenerateCodeException { + private ImmutableSet superBuilders(TypeElement type) + throws CannotGenerateCodeException { Set toRet = new HashSet(); PackageElement pkg = elements.getPackageOf(type); final ImmutableSet superTypes = MethodFinder.getSupertypes(type); - for(TypeElement superType : superTypes){ - if (superType.equals(type)){ + for (TypeElement superType : superTypes) { + if (superType.equals(type)) { continue; } final Optional 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); } } diff --git a/src/main/java/org/inferred/freebuilder/processor/BuilderMethods.java b/src/main/java/org/inferred/freebuilder/processor/BuilderMethods.java index 08768c659..d1664e8c9 100644 --- a/src/main/java/org/inferred/freebuilder/processor/BuilderMethods.java +++ b/src/main/java/org/inferred/freebuilder/processor/BuilderMethods.java @@ -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() {} } diff --git a/src/main/java/org/inferred/freebuilder/processor/CodeGenerator.java b/src/main/java/org/inferred/freebuilder/processor/CodeGenerator.java index 12ea0efa5..8bc505b97 100644 --- a/src/main/java/org/inferred/freebuilder/processor/CodeGenerator.java +++ b/src/main/java/org/inferred/freebuilder/processor/CodeGenerator.java @@ -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; @@ -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); @@ -209,16 +210,19 @@ private static void addMergeFromBuilderMethod(SourceBuilder code, Metadata metad } private static void addMergeFromSuperTypes(SourceBuilder code, Metadata metadata) { - for(Map.Entry> e : metadata.getSuperTypeProperties().entrySet()){ + for (Map.Entry> e + : metadata.getSuperTypeProperties().entrySet()) { final ParameterizedType type = e.getKey(); final ImmutableList 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"); @@ -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; } @@ -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) { @@ -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()); diff --git a/src/main/java/org/inferred/freebuilder/processor/DefaultPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/DefaultPropertyFactory.java index c72385bae..fb19e85b1 100644 --- a/src/main/java/org/inferred/freebuilder/processor/DefaultPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/DefaultPropertyFactory.java @@ -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; @@ -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); @@ -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) { diff --git a/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java index ece5b40c3..4f2e4c9e3 100644 --- a/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java @@ -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)); } diff --git a/src/test/java/org/inferred/freebuilder/processor/AnalyserTest.java b/src/test/java/org/inferred/freebuilder/processor/AnalyserTest.java index 6fe1c69bb..760f9500a 100644 --- a/src/test/java/org/inferred/freebuilder/processor/AnalyserTest.java +++ b/src/test/java/org/inferred/freebuilder/processor/AnalyserTest.java @@ -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) { diff --git a/src/test/java/org/inferred/freebuilder/processor/DefaultedPropertiesSourceTest.java b/src/test/java/org/inferred/freebuilder/processor/DefaultedPropertiesSourceTest.java index 3b490ab21..7679844a4 100644 --- a/src/test/java/org/inferred/freebuilder/processor/DefaultedPropertiesSourceTest.java +++ b/src/test/java/org/inferred/freebuilder/processor/DefaultedPropertiesSourceTest.java @@ -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", diff --git a/src/test/java/org/inferred/freebuilder/processor/GenericTypeSourceTest.java b/src/test/java/org/inferred/freebuilder/processor/GenericTypeSourceTest.java index 904fd9bee..21405e926 100644 --- a/src/test/java/org/inferred/freebuilder/processor/GenericTypeSourceTest.java +++ b/src/test/java/org/inferred/freebuilder/processor/GenericTypeSourceTest.java @@ -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", @@ -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", @@ -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", diff --git a/src/test/java/org/inferred/freebuilder/processor/RequiredPropertiesSourceTest.java b/src/test/java/org/inferred/freebuilder/processor/RequiredPropertiesSourceTest.java index 03ad27465..b90658f77 100644 --- a/src/test/java/org/inferred/freebuilder/processor/RequiredPropertiesSourceTest.java +++ b/src/test/java/org/inferred/freebuilder/processor/RequiredPropertiesSourceTest.java @@ -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", @@ -447,6 +463,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", @@ -708,6 +740,22 @@ public void testJ6_noGuava() { " }", "", " /**", + " * 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", @@ -981,6 +1029,22 @@ public void testJ7_noGuava() { " }", "", " /**", + " * 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", @@ -1270,6 +1334,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", @@ -1528,6 +1608,22 @@ public void testPrefixless() { " }", "", " /**", + " * Returns true if the required property corresponding to", + " * {@link Person#name()} is set.", + " */", + " public boolean isPropertyNameSet() {", + " return _unsetProperties.contains(Person_Builder.Property.NAME);", + " }", + "", + " /**", + " * Returns true if the required property corresponding to", + " * {@link Person#age()} 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", From f592eb822fb9799f2a423e69a8673c6c2d9401a2 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Thu, 8 Dec 2016 20:35:09 +0100 Subject: [PATCH 3/3] property factory fixes (mergeFromSuperBuilder) --- .../processor/ListMultimapPropertyFactory.java | 9 ++++----- .../freebuilder/processor/ListPropertyFactory.java | 6 ++++-- .../freebuilder/processor/MapPropertyFactory.java | 3 +-- .../freebuilder/processor/MultisetPropertyFactory.java | 3 +-- .../processor/SetMultimapPropertyFactory.java | 3 +-- .../freebuilder/processor/SetPropertyFactory.java | 3 +-- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/inferred/freebuilder/processor/ListMultimapPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/ListMultimapPropertyFactory.java index 9703f3a12..88fa67f46 100644 --- a/src/main/java/org/inferred/freebuilder/processor/ListMultimapPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/ListMultimapPropertyFactory.java @@ -389,11 +389,10 @@ public void addMergeFromBuilder(Block code, String builder) { @Override public void addMergeFromSuperBuilder(Block code, String builder) { - code.addLine("%s(((%s) %s).%s());", - putAllMethod(property), - metadata.getGeneratedBuilder(), - builder, - getter(property)); + code.addLine("%s(%s.%s());", + putAllMethod(property), + builder, + getter(property)); } @Override diff --git a/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java index 4f2e4c9e3..537f6608d 100644 --- a/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java @@ -369,8 +369,10 @@ public void addMergeFromBuilder(Block code, String builder) { @Override public void addMergeFromSuperBuilder(Block code, String builder) { - Excerpt base = new Block(code).add(builder); - code.addLine("%s(%s.%s());", addAllMethod(property), base, getter(property)); + code.addLine("%s(%s.%s());", + addAllMethod(property), + builder, + getter(property)); } @Override diff --git a/src/main/java/org/inferred/freebuilder/processor/MapPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/MapPropertyFactory.java index fe341f2f5..f7741384d 100644 --- a/src/main/java/org/inferred/freebuilder/processor/MapPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/MapPropertyFactory.java @@ -323,9 +323,8 @@ public void addMergeFromBuilder(Block code, String builder) { @Override public void addMergeFromSuperBuilder(Block code, String builder) { - code.addLine("%s(((%s) %s).%s());", + code.addLine("%s(%s.%s());", putAllMethod(property), - metadata.getGeneratedBuilder(), builder, getter(property)); } diff --git a/src/main/java/org/inferred/freebuilder/processor/MultisetPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/MultisetPropertyFactory.java index e6dc39402..6f9d77439 100644 --- a/src/main/java/org/inferred/freebuilder/processor/MultisetPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/MultisetPropertyFactory.java @@ -331,9 +331,8 @@ public void addMergeFromBuilder(Block code, String builder) { @Override public void addMergeFromSuperBuilder(Block code, String builder) { - code.addLine("%s(((%s) %s).%s());", + code.addLine("%s(%s.%s());", addAllMethod(property), - metadata.getGeneratedBuilder(), builder, getter(property)); } diff --git a/src/main/java/org/inferred/freebuilder/processor/SetMultimapPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/SetMultimapPropertyFactory.java index 3b871d189..55b3d9088 100644 --- a/src/main/java/org/inferred/freebuilder/processor/SetMultimapPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/SetMultimapPropertyFactory.java @@ -385,9 +385,8 @@ public void addMergeFromBuilder(Block code, String builder) { @Override public void addMergeFromSuperBuilder(Block code, String builder) { - code.addLine("%s(((%s) %s).%s());", + code.addLine("%s(%s.%s());", putAllMethod(property), - metadata.getGeneratedBuilder(), builder, getter(property)); } diff --git a/src/main/java/org/inferred/freebuilder/processor/SetPropertyFactory.java b/src/main/java/org/inferred/freebuilder/processor/SetPropertyFactory.java index 861577e09..a78532fde 100644 --- a/src/main/java/org/inferred/freebuilder/processor/SetPropertyFactory.java +++ b/src/main/java/org/inferred/freebuilder/processor/SetPropertyFactory.java @@ -372,9 +372,8 @@ public void addMergeFromBuilder(Block code, String builder) { @Override public void addMergeFromSuperBuilder(Block code, String builder) { - code.addLine("%s(((%s) %s).%s());", + code.addLine("%s(%s.%s());", addAllMethod(property), - metadata.getGeneratedBuilder(), builder, getter(property)); }