Skip to content

Commit

Permalink
#567 beanFriendlyModifiables style option
Browse files Browse the repository at this point in the history
  • Loading branch information
elucash committed Mar 6, 2017
1 parent 95b2f03 commit e4e4093
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 45 deletions.
@@ -0,0 +1,24 @@
package org.immutables.fixture.modifiable;

import java.util.List;
import java.util.Map;
import org.immutables.value.Value;

@Value.Immutable
@Value.Modifiable
@Value.Style(
beanFriendlyModifiables = true,
create = "new",
get = {"get*", "is*"})
public interface BeanFriendly {

boolean isPrimary();

int getId();

String getDescription();

List<String> getNames();

Map<String, String> getOptions();
}
Expand Up @@ -6,7 +6,7 @@


@Value.Immutable @Value.Immutable
@Value.Modifiable @Value.Modifiable
@Value.Style(chainableModifiableSetters = false, create = "new") @Value.Style(beanFriendlyModifiables = true, create = "new")
public interface VoidSetters { public interface VoidSetters {
int getAa(); int getAa();


Expand Down
@@ -0,0 +1,42 @@
package org.immutables.fixture.modifiable;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.beans.BeanInfo;
import java.beans.Introspector;
import org.junit.Test;
import static org.immutables.check.Checkers.check;

public class BeanFriendlyTest {

@Test
public void modifiableAsJavaBean() throws Exception {
ImmutableSet<String> rwProperties = ImmutableSet.of("primary", "id", "description", "names", "options");

BeanInfo beanInfo = Introspector.getBeanInfo(ModifiableBeanFriendly.class);

for (java.beans.PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {

This comment has been minimized.

Copy link
@anuraaga

anuraaga Mar 6, 2017

Contributor

Thanks a lot for this commit! Code looks good, but I think this test doesn't actually test whether the bean properties were detected. This iterates on the detected properties, whereas we should iterate on the expected properties to make sure they're actually present. It's why my version collected these into a map before checking and would recommend something along those lines.

This comment has been minimized.

Copy link
@elucash

elucash Mar 6, 2017

Author Member

agree that if it will find none there will be no assertion failed, will add better check

if (rwProperties.contains(pd.getName())) {
check(pd.getReadMethod()).notNull();
check(pd.getWriteMethod()).notNull();
}
}

ModifiableBeanFriendly bean = new ModifiableBeanFriendly();
bean.setPrimary(true);
bean.setDescription("description");
bean.setId(1000);
bean.setNames(ImmutableList.of("name"));
bean.addNames("name2");
bean.putOptions("foo", "bar");

// This bean can become immutable.
BeanFriendly immutableBean = bean.toImmutable();
check(immutableBean.isPrimary());
check(immutableBean.getDescription()).is("description");
check(immutableBean.getId()).is(1000);
check(immutableBean.getNames()).isOf("name", "name2");
check(immutableBean.getOptions()).is(ImmutableMap.of("foo", "bar"));
}
}
Expand Up @@ -99,7 +99,7 @@ Use @Value.Modifiable cannot be used with @Value.Immutable which implements Ordi
* Clears the object by setting all attributes to their initial values. * Clears the object by setting all attributes to their initial values.
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
public [thisReturnType type] [type.names.clear]() { public [thisSetterReturnType type] [type.names.clear]() {
[for l in positions.longs] [for l in positions.longs]
[disambiguateField type 'initBits'][emptyIfZero l.index] = [literal.hex l.occupation]; [disambiguateField type 'initBits'][emptyIfZero l.index] = [literal.hex l.occupation];
[/for] [/for]
Expand All @@ -109,7 +109,7 @@ Use @Value.Modifiable cannot be used with @Value.Immutable which implements Ordi
[for v in type.implementedAttributes if not v.generateDerived] [for v in type.implementedAttributes if not v.generateDerived]
[clearField v true] [clearField v true]
[/for] [/for]
[thisReturn type] [thisSetterReturn type]
} }
[generateFrom type] [generateFrom type]
[generateSetters type] [generateSetters type]
Expand Down Expand Up @@ -341,10 +341,10 @@ public static[type.generics.def] [thisReturnType type] [type.names.create]() {
* @param instance The instance from which to copy values * @param instance The instance from which to copy values
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
public [thisReturnType type] [type.names.from]([s.type] instance) { public [thisSetterReturnType type] [type.names.from]([s.type] instance) {
[im.requireNonNull type](instance, "instance"); [im.requireNonNull type](instance, "instance");
from((Object) instance); from((Object) instance);
[thisReturn type] [thisSetterReturn type]
} }
[/for] [/for]


Expand Down Expand Up @@ -382,12 +382,12 @@ private void from(Object object) {
* @param instance The instance from which to copy values * @param instance The instance from which to copy values
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
public [thisReturnType type] [type.names.from]([type.typeAbstract] instance) { public [thisSetterReturnType type] [type.names.from]([type.typeAbstract] instance) {
[im.requireNonNull type](instance, "instance"); [im.requireNonNull type](instance, "instance");
[for v in type.settableAttributes] [for v in type.settableAttributes]
[buildFromAttribute v] [buildFromAttribute v]
[/for] [/for]
[thisReturn type] [thisSetterReturn type]
} }
[/if] [/if]
[/if] [/if]
Expand Down Expand Up @@ -474,7 +474,7 @@ public [thisReturnType type] [type.names.from]([type.typeAbstract] instance) {
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
[deprecation v] [deprecation v]
public [thisReturnType type] [v.names.add]([v.unwrappedElementType] element) { public [thisSetterReturnType type] [v.names.add]([v.unwrappedElementType] element) {
[if v.nullableCollector] [if v.nullableCollector]
if ([v.name] == null) { if ([v.name] == null) {
[defineOrResetField v false] [defineOrResetField v false]
Expand All @@ -491,7 +491,7 @@ public [thisReturnType type] [v.names.add]([v.unwrappedElementType] element) {
[/if] [/if]
[/if] [/if]
[nondefaultSetter v] [nondefaultSetter v]
[thisReturn type] [thisSetterReturn type]
} }


/** /**
Expand All @@ -501,7 +501,7 @@ public [thisReturnType type] [v.names.add]([v.unwrappedElementType] element) {
*/ */
[deprecation v] [deprecation v]
[varargsSafety v] [varargsSafety v]
public final [thisReturnType type] [v.names.add]([v.unwrappedElementType]... elements) { public final [thisSetterReturnType type] [v.names.add]([v.unwrappedElementType]... elements) {
for ([v.unwrappedElementType] element : elements) { for ([v.unwrappedElementType] element : elements) {
[if v.unwrappedElementPrimitiveType] [if v.unwrappedElementPrimitiveType]
[v.names.add](element); [v.names.add](element);
Expand All @@ -510,7 +510,7 @@ public final [thisReturnType type] [v.names.add]([v.unwrappedElementType]... ele
[/if] [/if]
} }
[nondefaultSetter v] [nondefaultSetter v]
[thisReturn type] [thisSetterReturn type]
} }


/** /**
Expand All @@ -519,7 +519,7 @@ public final [thisReturnType type] [v.names.add]([v.unwrappedElementType]... ele
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
[deprecation v] [deprecation v]
public [thisSetterReturnType type] [v.names.set]([v.atNullability]Iterable<[v.consumedElementType]> elements) { public [thisSetterReturnType type] [v.names.set]([v.atNullability][if type.beanFriendlyModifiable][v.type][else]Iterable<[v.consumedElementType]>[/if] elements) {
[if v.nullable] [if v.nullable]
if (elements == null) { if (elements == null) {
[v.name] = null; [v.name] = null;
Expand All @@ -536,7 +536,8 @@ public [thisSetterReturnType type] [v.names.set]([v.atNullability]Iterable<[v.co
[else] [else]
[v.name].clear(); [v.name].clear();
[/if] [/if]
[if type.generateChainableSetters]return [/if][v.names.addAll](elements); [v.names.addAll](elements);
[thisSetterReturn type]
} }


/** /**
Expand All @@ -545,7 +546,7 @@ public [thisSetterReturnType type] [v.names.set]([v.atNullability]Iterable<[v.co
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
[deprecation v] [deprecation v]
public [thisReturnType type] [v.names.addAll](Iterable<[v.consumedElementType]> elements) { public [thisSetterReturnType type] [v.names.addAll](Iterable<[v.consumedElementType]> elements) {
[if v.nullableCollector] [if v.nullableCollector]
if ([v.name] == null) { if ([v.name] == null) {
[defineOrResetField v false] [defineOrResetField v false]
Expand All @@ -555,7 +556,7 @@ public [thisReturnType type] [v.names.addAll](Iterable<[v.consumedElementType]>
[v.names.add](element); [v.names.add](element);
} }
[nondefaultSetter v] [nondefaultSetter v]
[thisReturn type] [thisSetterReturn type]
} }
[else if v.optionalType] [else if v.optionalType]


Expand Down Expand Up @@ -594,8 +595,9 @@ public [thisSetterReturnType type] [v.names.set]([v.rawType][if not v.jdkSpecial
*/ */
[varargsSafety v] [varargsSafety v]
[deprecation v] [deprecation v]
public final [thisReturnType type] [v.names.put]([uK] key, [uV]... values) { public final [thisSetterReturnType type] [v.names.put]([uK] key, [uV]... values) {
return [v.names.putAll](key, [im.arrayAsListSecondary v 'values']); [v.names.putAll](key, [im.arrayAsListSecondary v 'values']);
[thisSetterReturn type]
} }


/** /**
Expand All @@ -605,15 +607,15 @@ public final [thisReturnType type] [v.names.put]([uK] key, [uV]... values) {
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
[deprecation v] [deprecation v]
public [thisReturnType type] [v.names.putAll]([uK] key, Iterable<[wV]> values) { public [thisSetterReturnType type] [v.names.putAll]([uK] key, Iterable<[wV]> values) {
[if v.nullableCollector] [if v.nullableCollector]
if ([v.name] == null) { if ([v.name] == null) {
[defineOrResetField v false] [defineOrResetField v false]
} }
[/if] [/if]
[v.name].putAll(key, values); [v.name].putAll(key, values);
[nondefaultSetter v] [nondefaultSetter v]
[thisReturn type] [thisSetterReturn type]
} }
[/if] [/if]


Expand All @@ -624,7 +626,7 @@ public [thisReturnType type] [v.names.putAll]([uK] key, Iterable<[wV]> values) {
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
[deprecation v] [deprecation v]
public [thisReturnType type] [v.names.put]([uK] key, [uV] value) { public [thisSetterReturnType type] [v.names.put]([uK] key, [uV] value) {
[if v.nullableCollector] [if v.nullableCollector]
if ([v.name] == null) { if ([v.name] == null) {
[defineOrResetField v false] [defineOrResetField v false]
Expand All @@ -634,7 +636,7 @@ public [thisReturnType type] [v.names.put]([uK] key, [uV] value) {
[if not v.unwrappedElementPrimitiveType][im.requireNonNull type](key, "[v.name] key")[else]key[/if], [if not v.unwrappedElementPrimitiveType][im.requireNonNull type](key, "[v.name] key")[else]key[/if],
[if not v.unwrappedSecondaryElementPrimitiveType][im.requireNonNull type](value, "[v.name] value")[else]value[/if]); [if not v.unwrappedSecondaryElementPrimitiveType][im.requireNonNull type](value, "[v.name] value")[else]value[/if]);
[nondefaultSetter v] [nondefaultSetter v]
[thisReturn type] [thisSetterReturn type]
} }


/** /**
Expand All @@ -644,12 +646,12 @@ public [thisReturnType type] [v.names.put]([uK] key, [uV] value) {
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
[deprecation v] [deprecation v]
public [thisReturnType type] [v.names.set]([v.atNullability][if v.multimapType][guava].collect.Multimap[else]java.util.Map[/if]<[gE], ? extends [wV]> entries) { public [thisSetterReturnType type] [v.names.set]([v.atNullability][if type.beanFriendlyModifiable][v.type][else][if v.multimapType][guava].collect.Multimap[else]java.util.Map[/if]<[gE], ? extends [wV]>[/if] entries) {
[if v.nullable] [if v.nullable]
if (entries == null) { if (entries == null) {
[v.name] = null; [v.name] = null;
[nondefaultSetter v] [nondefaultSetter v]
[thisReturn type] [thisSetterReturn type]
} }
[/if] [/if]
[if v.nullableCollector] [if v.nullableCollector]
Expand All @@ -661,7 +663,8 @@ public [thisReturnType type] [v.names.set]([v.atNullability][if v.multimapType][
[else] [else]
[v.name].clear(); [v.name].clear();
[/if] [/if]
return [v.names.putAll](entries); [v.names.putAll](entries);
[thisSetterReturn type]
} }


/** /**
Expand All @@ -671,7 +674,7 @@ public [thisReturnType type] [v.names.set]([v.atNullability][if v.multimapType][
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
[deprecation v] [deprecation v]
public [thisReturnType type] [v.names.putAll]([if v.multimapType][guava].collect.Multimap[else]java.util.Map[/if]<[gE], ? extends [wV]> entries) { public [thisSetterReturnType type] [v.names.putAll]([if v.multimapType][guava].collect.Multimap[else]java.util.Map[/if]<[gE], ? extends [wV]> entries) {
[if v.nullableCollector] [if v.nullableCollector]
if ([v.name] == null) { if ([v.name] == null) {
[defineOrResetField v false] [defineOrResetField v false]
Expand All @@ -683,7 +686,7 @@ public [thisReturnType type] [v.names.putAll]([if v.multimapType][guava].collect
[im.requireNonNull type](entry.getValue(), "[v.name] value")); [im.requireNonNull type](entry.getValue(), "[v.name] value"));
} }
[nondefaultSetter v] [nondefaultSetter v]
[thisReturn type] [thisSetterReturn type]
} }
[/for] [/for]
[else if v.arrayType] [else if v.arrayType]
Expand All @@ -698,11 +701,11 @@ public [thisReturnType type] [v.names.putAll]([if v.multimapType][guava].collect
*/ */
[varargsSafety v] [varargsSafety v]
[deprecation v] [deprecation v]
public final [thisReturnType type] [v.names.set]([v.elementType]... elements) { public final [thisSetterReturnType type] [v.names.set]([v.elementType]... elements) {
this.[v.name] = [if v.nullable]elements == null ? null : [/if]elements.clone(); this.[v.name] = [if v.nullable]elements == null ? null : [/if]elements.clone();
[mandatorySetter v] [mandatorySetter v]
[nondefaultSetter v] [nondefaultSetter v]
[thisReturn type] [thisSetterReturn type]
} }
[else] [else]


Expand All @@ -715,7 +718,7 @@ public final [thisReturnType type] [v.names.set]([v.elementType]... elements) {
* @return {@code this} for use in a chained invocation * @return {@code this} for use in a chained invocation
*/ */
[deprecation v] [deprecation v]
public [thisReturnType type] [v.names.set]([v.atNullability][v.type] [v.name]) { public [thisSetterReturnType type] [v.names.set]([v.atNullability][v.type] [v.name]) {
[if v.attributeValueKindModifyFrom] [if v.attributeValueKindModifyFrom]
this.[v.name] = [if v.nullable][v.name] == null ? null : [/if]([v.name] instanceof [v.attributeValueType.constitution.typeModifiable.absoluteRaw] ? ([v.attributeValueType.constitution.typeModifiable.absolute]) [v.name] : [v.attributeValueType.constitution.factoryCreate]().[v.attributeValueType.names.from]([v.name])); this.[v.name] = [if v.nullable][v.name] == null ? null : [/if]([v.name] instanceof [v.attributeValueType.constitution.typeModifiable.absoluteRaw] ? ([v.attributeValueType.constitution.typeModifiable.absolute]) [v.name] : [v.attributeValueType.constitution.factoryCreate]().[v.attributeValueType.names.from]([v.name]));
[else if v.primitive or v.nullable] [else if v.primitive or v.nullable]
Expand All @@ -725,7 +728,7 @@ public [thisReturnType type] [v.names.set]([v.atNullability][v.type] [v.name]) {
[/if] [/if]
[nondefaultSetter v] [nondefaultSetter v]
[mandatorySetter v] [mandatorySetter v]
[thisReturn type] [thisSetterReturn type]
} }
[/if] [/if]
[/for] [/for]
Expand Down Expand Up @@ -1059,9 +1062,9 @@ import [starImport];


[template thisReturnType Type type][type.names.typeModifiable][type.generics.args][/template] [template thisReturnType Type type][type.names.typeModifiable][type.generics.args][/template]


[template thisSetterReturn Type type]return[if type.generateChainableSetters] this[/if];[/template] [template thisSetterReturn Type type]return[if not type.beanFriendlyModifiable] this[/if];[/template]


[template thisSetterReturnType Type type][if type.generateChainableSetters][type.names.typeModifiable][type.generics.args][else]void[/if][/template] [template thisSetterReturnType Type type][if not type.beanFriendlyModifiable][type.names.typeModifiable][type.generics.args][else]void[/if][/template]


[template invokeSuper Attribute a][for di = a.defaultInterface][if di][di].[/if][/for]super[/template] [template invokeSuper Attribute a][for di = a.defaultInterface][if di][di].[/if][/for]super[/template]


Expand Down
Expand Up @@ -1761,7 +1761,7 @@ public StyleInfo apply(StyleMirror input) {
ImmutableSet.copyOf(input.immutableCopyOfRoutinesName()), ImmutableSet.copyOf(input.immutableCopyOfRoutinesName()),
input.stagedBuilder(), input.stagedBuilder(),
input.builtinContainerAttributes(), input.builtinContainerAttributes(),
input.chainableModifiableSetters()); input.beanFriendlyModifiables());
} }
} }


Expand Down
Expand Up @@ -294,7 +294,7 @@ public Class<?>[] immutableCopyOfRoutines() {


@Value.Parameter @Value.Parameter
@Override @Override
public abstract boolean chainableModifiableSetters(); public abstract boolean beanFriendlyModifiables();


@Value.Lazy @Value.Lazy
public Styles getStyles() { public Styles getStyles() {
Expand Down
Expand Up @@ -206,7 +206,7 @@ private ValueMirrors() {}


boolean builtinContainerAttributes() default true; boolean builtinContainerAttributes() default true;


boolean chainableModifiableSetters() default true; boolean beanFriendlyModifiables() default false;


public enum ImplementationVisibility { public enum ImplementationVisibility {
PUBLIC, PUBLIC,
Expand All @@ -221,7 +221,7 @@ public enum BuilderVisibility {
SAME, SAME,
PACKAGE PACKAGE
} }

public enum ValidationMethod { public enum ValidationMethod {
NONE, NONE,
SIMPLE, SIMPLE,
Expand Down
Expand Up @@ -228,8 +228,8 @@ public boolean isGenerateBuildOrThrow() {
return !constitution.style().buildOrThrow().isEmpty(); return !constitution.style().buildOrThrow().isEmpty();
} }


public boolean isGenerateChainableSetters() { public boolean isBeanFriendlyModifiable() {
return constitution.style().chainableModifiableSetters(); return constitution.style().beanFriendlyModifiables();
} }


private boolean noGuavaInClasspath() { private boolean noGuavaInClasspath() {
Expand Down

0 comments on commit e4e4093

Please sign in to comment.