From 193da6b0442b1608c60c2a7e231796a0b2c5b0a0 Mon Sep 17 00:00:00 2001 From: "grant@iowntheinter.net" Date: Wed, 6 Nov 2024 23:14:01 -0700 Subject: [PATCH 1/3] add configurable boolean encoding --- .../com/aerospike/mapper/tools/ValueType.java | 6 +- .../tools/configuration/ClassConfig.java | 23 +++- .../mapper/tools/mappers/BooleanMapper.java | 27 ++++- .../mapper/tools/utils/TypeUtils.java | 3 +- .../com/aerospike/mapper/BooleanTest.java | 104 ++++++++++++++++++ 5 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 src/test/java/com/aerospike/mapper/BooleanTest.java diff --git a/src/main/java/com/aerospike/mapper/tools/ValueType.java b/src/main/java/com/aerospike/mapper/tools/ValueType.java index 6331f69..2453e7d 100644 --- a/src/main/java/com/aerospike/mapper/tools/ValueType.java +++ b/src/main/java/com/aerospike/mapper/tools/ValueType.java @@ -97,7 +97,11 @@ public void set(final Object obj, final Object value) throws ReflectiveOperation DeferredObjectSetter objectSetter = new DeferredObjectSetter(setter, (DeferredObject) value); DeferredObjectLoader.add(objectSetter); } else { - this.field.set(obj, value); + if (this.field.getType().isAssignableFrom(Boolean.class) && value instanceof Long) { + this.field.set(obj, (Long) value != 0); + } else { + this.field.set(obj, value); + } } } diff --git a/src/main/java/com/aerospike/mapper/tools/configuration/ClassConfig.java b/src/main/java/com/aerospike/mapper/tools/configuration/ClassConfig.java index 034e0bf..d9e53c2 100644 --- a/src/main/java/com/aerospike/mapper/tools/configuration/ClassConfig.java +++ b/src/main/java/com/aerospike/mapper/tools/configuration/ClassConfig.java @@ -9,6 +9,7 @@ import com.aerospike.mapper.annotations.AerospikeEmbed; import com.aerospike.mapper.annotations.AerospikeReference; import com.aerospike.mapper.tools.ConfigurationUtils; +import com.aerospike.mapper.tools.mappers.BooleanMapper; import com.fasterxml.jackson.annotation.JsonProperty; public class ClassConfig { @@ -26,11 +27,17 @@ public class ClassConfig { private String factoryClass; private String factoryMethod; private final List bins; + private BooleanMapper.Encoding boolEncoding = BooleanMapper.Encoding.Numeric; + public ClassConfig() { bins = new ArrayList<>(); } + public BooleanMapper.Encoding getBoolEncoding() { + return boolEncoding; + } + public String getClassName() { return className; } @@ -163,16 +170,21 @@ private void setKey(KeyConfig key) { private void setShortName(String shortName) { this.shortName = shortName; } - + + private void setBoolEncoding( BooleanMapper.Encoding boolEncoding) { + this.boolEncoding = boolEncoding; + } + public static class Builder { private final Class clazz; private final ClassConfig classConfig; + public Builder(final Class clazz) { this.clazz = clazz; this.classConfig = new ClassConfig(); this.classConfig.setClassName(clazz.getName()); } - + private void validateFieldExists(String fieldName) { if (!ConfigurationUtils.validateFieldOnClass(this.clazz, fieldName)) { throw new AerospikeException(String.format("Field %s does not exist on class %s or its superclasses", fieldName, this.clazz)); @@ -199,6 +211,11 @@ public Builder withTtl(int ttl) { return this; } + public Builder withBoolEncoding(BooleanMapper.Encoding boolEncoding) { + this.classConfig.setBoolEncoding(boolEncoding); + return this; + } + public Builder withVersion(int version) { this.classConfig.setVersion(version); return this; @@ -223,7 +240,7 @@ public Builder withShortName(boolean sendKey) { this.classConfig.setSendKey(sendKey); return this; } - + public Builder withFactoryClassAndMethod(@NotNull Class factoryClass, @NotNull String factoryMethod) { this.classConfig.setFactoryClass(factoryClass.getName()); this.classConfig.setFactoryMethod(factoryMethod); diff --git a/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java b/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java index 55958d7..8151e08 100644 --- a/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java +++ b/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java @@ -1,17 +1,42 @@ package com.aerospike.mapper.tools.mappers; +import com.aerospike.mapper.tools.ClassCache; import com.aerospike.mapper.tools.TypeMapper; +import com.aerospike.mapper.tools.configuration.ClassConfig; import static com.aerospike.client.Value.UseBoolBin; public class BooleanMapper extends TypeMapper { + public enum Encoding { + Numeric, + Object + } + + private final ClassConfig classConfig; + + public BooleanMapper(final ClassConfig config) { + super(); + classConfig = config; + } + @Override public Object toAerospikeFormat(Object value) { + final boolean useObjectEncoding; + if (ClassCache.getInstance().hasClassConfig(Boolean.class)) { + useObjectEncoding = ClassCache.getInstance() + .getClassConfig(Boolean.class) + .getBoolEncoding() + .equals(Encoding.Object); + } else { + useObjectEncoding = false; + } + + if (value == null) { return null; } - if (UseBoolBin) { + if (UseBoolBin && useObjectEncoding) { return value; } return ((Boolean) value) ? 1 : 0; diff --git a/src/main/java/com/aerospike/mapper/tools/utils/TypeUtils.java b/src/main/java/com/aerospike/mapper/tools/utils/TypeUtils.java index b5d84fb..d4a5b34 100644 --- a/src/main/java/com/aerospike/mapper/tools/utils/TypeUtils.java +++ b/src/main/java/com/aerospike/mapper/tools/utils/TypeUtils.java @@ -111,7 +111,8 @@ private static TypeMapper getMapper(Class clazz, AnnotatedType type, IBaseAer } else if (Integer.class.isAssignableFrom(clazz) || Integer.TYPE.isAssignableFrom(clazz)) { typeMapper = new IntMapper(); } else if (Boolean.class.isAssignableFrom(clazz) || Boolean.TYPE.isAssignableFrom(clazz)) { - typeMapper = new BooleanMapper(); + ClassConfig config = ClassCache.getInstance().getClassConfig(clazz); + typeMapper = new BooleanMapper(config); } else if (Float.class.isAssignableFrom(clazz) || Float.TYPE.isAssignableFrom(clazz)) { typeMapper = new FloatMapper(); } else if (clazz.isEnum()) { diff --git a/src/test/java/com/aerospike/mapper/BooleanTest.java b/src/test/java/com/aerospike/mapper/BooleanTest.java new file mode 100644 index 0000000..e55f622 --- /dev/null +++ b/src/test/java/com/aerospike/mapper/BooleanTest.java @@ -0,0 +1,104 @@ +package com.aerospike.mapper; + +import com.aerospike.client.Key; +import com.aerospike.client.Record; +import com.aerospike.client.policy.Policy; +import com.aerospike.mapper.annotations.AerospikeKey; +import com.aerospike.mapper.annotations.AerospikeRecord; +import com.aerospike.mapper.tools.AeroMapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BooleanTest extends AeroMapperBaseTest { + @AerospikeRecord(namespace = "test", set = "B") + public static class B { + @AerospikeKey + public int id; + public Boolean boolValue; + } + + @Test + public void testNumericEncoding() throws JsonProcessingException { + B b = new B(); + b.boolValue = true; + b.id = 1; + String config = + "---\n" + + "classes:\n" + + " - class: com.aerospike.mapper.BooleanTest$B\n" + + " namespace: test\n" + + " set: B\n" + + " key:\n" + + " field: id\n" + + " - class: java.lang.Boolean\n" + + " boolEncoding: Numeric\n"; + + AeroMapper mapper = new AeroMapper.Builder(client).withConfiguration(config).build(); + mapper.save(b); + + B b2 = mapper.read(B.class, 1); + + assertEquals(b.id, b2.id); + assertEquals(b.boolValue, b2.boolValue); + final Record rec = mapper.getClient().get(new Policy(), new Key("test", "B", 1)); + final Object rawRepresentation = rec.bins.get("boolValue"); + assertEquals(Long.class, rawRepresentation.getClass()); + } + + @Test + public void testObjectEncoding() throws JsonProcessingException { + B b = new B(); + b.boolValue = true; + b.id = 1; + String config = + "---\n" + + "classes:\n" + + " - class: com.aerospike.mapper.BooleanTest$B\n" + + " namespace: test\n" + + " set: B\n" + + " key:\n" + + " field: id\n" + + " - class: java.lang.Boolean\n" + + " boolEncoding: Object\n"; + + AeroMapper mapper = new AeroMapper.Builder(client).withConfiguration(config).build(); + mapper.save(b); + + B b2 = mapper.read(B.class, 1); + + assertEquals(b.id, b2.id); + assertEquals(b.boolValue, b2.boolValue); + final Record rec = mapper.getClient().get(new Policy(), new Key("test", "B", 1)); + final Object rawRepresentation = rec.bins.get("boolValue"); + assertEquals(Boolean.class, rawRepresentation.getClass()); + } + + @Test + public void testNumericByDefault() throws JsonProcessingException { + B b = new B(); + b.boolValue = true; + b.id = 1; + String config = + "---\n" + + "classes:\n" + + " - class: com.aerospike.mapper.BooleanTest$B\n" + + " namespace: test\n" + + " set: B\n" + + " key:\n" + + " field: id\n"; + + AeroMapper mapper = new AeroMapper.Builder(client).withConfiguration(config).build(); + mapper.save(b); + + B b2 = mapper.read(B.class, 1); + + assertEquals(b.id, b2.id); + assertEquals(b.boolValue, b2.boolValue); + final Record rec = mapper.getClient().get(new Policy(), new Key("test", "B", 1)); + final Object rawRepresentation = rec.bins.get("boolValue"); + assertEquals(Long.class, rawRepresentation.getClass()); + } + +} From 48743f811732dee4e4ce04e62d1e0495de3ed0cc Mon Sep 17 00:00:00 2001 From: "grant@iowntheinter.net" Date: Wed, 6 Nov 2024 23:25:36 -0700 Subject: [PATCH 2/3] change option name from Object to Boolean --- .../com/aerospike/mapper/tools/mappers/BooleanMapper.java | 4 ++-- src/test/java/com/aerospike/mapper/BooleanTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java b/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java index 8151e08..bc9634c 100644 --- a/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java +++ b/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java @@ -10,7 +10,7 @@ public class BooleanMapper extends TypeMapper { public enum Encoding { Numeric, - Object + Boolean } private final ClassConfig classConfig; @@ -27,7 +27,7 @@ public Object toAerospikeFormat(Object value) { useObjectEncoding = ClassCache.getInstance() .getClassConfig(Boolean.class) .getBoolEncoding() - .equals(Encoding.Object); + .equals(Encoding.Boolean); } else { useObjectEncoding = false; } diff --git a/src/test/java/com/aerospike/mapper/BooleanTest.java b/src/test/java/com/aerospike/mapper/BooleanTest.java index e55f622..82dd6a7 100644 --- a/src/test/java/com/aerospike/mapper/BooleanTest.java +++ b/src/test/java/com/aerospike/mapper/BooleanTest.java @@ -61,7 +61,7 @@ public void testObjectEncoding() throws JsonProcessingException { " key:\n" + " field: id\n" + " - class: java.lang.Boolean\n" + - " boolEncoding: Object\n"; + " boolEncoding: Boolean\n"; AeroMapper mapper = new AeroMapper.Builder(client).withConfiguration(config).build(); mapper.save(b); From f3582f01fc58274241984d1ba6f72117e28c36ab Mon Sep 17 00:00:00 2001 From: "grant@iowntheinter.net" Date: Thu, 7 Nov 2024 10:31:16 -0700 Subject: [PATCH 3/3] set UseBoolBin instead of casting, fix test case to default to Long --- src/main/java/com/aerospike/mapper/tools/ValueType.java | 6 +----- .../com/aerospike/mapper/tools/mappers/BooleanMapper.java | 7 +++---- .../java/com/aerospike/mapper/DefaultFieldValuesTest.java | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/aerospike/mapper/tools/ValueType.java b/src/main/java/com/aerospike/mapper/tools/ValueType.java index 2453e7d..6331f69 100644 --- a/src/main/java/com/aerospike/mapper/tools/ValueType.java +++ b/src/main/java/com/aerospike/mapper/tools/ValueType.java @@ -97,11 +97,7 @@ public void set(final Object obj, final Object value) throws ReflectiveOperation DeferredObjectSetter objectSetter = new DeferredObjectSetter(setter, (DeferredObject) value); DeferredObjectLoader.add(objectSetter); } else { - if (this.field.getType().isAssignableFrom(Boolean.class) && value instanceof Long) { - this.field.set(obj, (Long) value != 0); - } else { - this.field.set(obj, value); - } + this.field.set(obj, value); } } diff --git a/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java b/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java index bc9634c..181171a 100644 --- a/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java +++ b/src/main/java/com/aerospike/mapper/tools/mappers/BooleanMapper.java @@ -22,21 +22,20 @@ public BooleanMapper(final ClassConfig config) { @Override public Object toAerospikeFormat(Object value) { - final boolean useObjectEncoding; if (ClassCache.getInstance().hasClassConfig(Boolean.class)) { - useObjectEncoding = ClassCache.getInstance() + UseBoolBin = ClassCache.getInstance() .getClassConfig(Boolean.class) .getBoolEncoding() .equals(Encoding.Boolean); } else { - useObjectEncoding = false; + UseBoolBin = false; } if (value == null) { return null; } - if (UseBoolBin && useObjectEncoding) { + if (UseBoolBin) { return value; } return ((Boolean) value) ? 1 : 0; diff --git a/src/test/java/com/aerospike/mapper/DefaultFieldValuesTest.java b/src/test/java/com/aerospike/mapper/DefaultFieldValuesTest.java index 7577f8c..fb16b6a 100644 --- a/src/test/java/com/aerospike/mapper/DefaultFieldValuesTest.java +++ b/src/test/java/com/aerospike/mapper/DefaultFieldValuesTest.java @@ -87,7 +87,7 @@ public void testBooleanValue() { UseBoolBin = true; mapper.save(obj); record = client.get(null, key); - assertTrue(record.bins.get("bool2") instanceof Boolean); + assertTrue(record.bins.get("bool2") instanceof Long); assertFalse(record.getBoolean("bool2")); dfc = mapper.read(DefaultFieldsClass.class, "dfc"); assertFalse(dfc.bool2);