Skip to content

Commit f8128ab

Browse files
authored
Simplify accessor use for fields (#61)
* Added the ability to use getters / setters Added a parameter to AerospikeBin which forces the mapper to use the getters/setters for a field instead of directly setting the field. This is useful when the getters/setters have logic in them beyond simply setting the attribute. This could have been done previously by excluding the field and marking the getters and setters explicitly, but this was clunky.
1 parent c9012f4 commit f8128ab

File tree

5 files changed

+319
-14
lines changed

5 files changed

+319
-14
lines changed

src/main/java/com/aerospike/mapper/annotations/AerospikeBin.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@
1515
* The name of the bin to use. If not specified, the field name is used for the bin name.
1616
*/
1717
String name() default "";
18+
boolean useAccessors() default false;
1819
}

src/main/java/com/aerospike/mapper/tools/ClassCacheEntry.java

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121

2222
import com.aerospike.client.AerospikeException;
2323
import com.aerospike.client.Bin;
24+
import com.aerospike.client.Key;
2425
import com.aerospike.client.Record;
26+
import com.aerospike.client.Value;
2527
import com.aerospike.client.cdt.MapOrder;
2628
import com.aerospike.client.policy.BatchPolicy;
2729
import com.aerospike.client.policy.Policy;
@@ -137,8 +139,8 @@ public ClassCacheEntry<T> construct() {
137139
this.overrideSettings(config);
138140
}
139141

140-
this.loadFieldsFromClass(clazz, this.mapAll, config);
141-
this.loadPropertiesFromClass(clazz, config);
142+
this.loadFieldsFromClass();
143+
this.loadPropertiesFromClass();
142144
this.superClazz = ClassCache.getInstance().loadClass(this.clazz.getSuperclass(), this.mapper);
143145
this.binCount = this.values.size() + (superClazz != null ? superClazz.binCount : 0);
144146
if (this.binCount == 0) {
@@ -538,7 +540,7 @@ private PropertyDefinition getOrCreateProperty(String name, Map<String, Property
538540
return thisProperty;
539541
}
540542

541-
private void loadPropertiesFromClass(@NotNull Class<?> clazz, ClassConfig config) {
543+
private void loadPropertiesFromClass() {
542544
Map<String, PropertyDefinition> properties = new HashMap<>();
543545
PropertyDefinition keyProperty = null;
544546
KeyConfig keyConfig = config != null ? config.getKey() : null;
@@ -617,10 +619,10 @@ private void loadPropertiesFromClass(@NotNull Class<?> clazz, ClassConfig config
617619
}
618620
}
619621

620-
private void loadFieldsFromClass(Class<?> clazz, boolean mapAll, ClassConfig config) {
622+
private void loadFieldsFromClass() {
621623
KeyConfig keyConfig = config != null ? config.getKey() : null;
622624
String keyField = keyConfig == null ? null : keyConfig.getField();
623-
for (Field thisField : clazz.getDeclaredFields()) {
625+
for (Field thisField : this.clazz.getDeclaredFields()) {
624626
boolean isKey = false;
625627
BinConfig thisBin = getBinFromField(thisField);
626628
if (thisField.isAnnotationPresent(AerospikeKey.class) || (!StringUtils.isBlank(keyField) && keyField.equals(thisField.getName()))) {
@@ -643,7 +645,6 @@ private void loadFieldsFromClass(Class<?> clazz, boolean mapAll, ClassConfig con
643645

644646
if (this.mapAll || thisField.isAnnotationPresent(AerospikeBin.class) || thisBin != null) {
645647
// This field needs to be mapped
646-
thisField.setAccessible(true);
647648
AerospikeBin bin = thisField.getAnnotation(AerospikeBin.class);
648649
String binName = bin == null ? null : ParserUtils.getInstance().get(bin.name());
649650
if (thisBin != null && !StringUtils.isBlank(thisBin.getDerivedName())) {
@@ -652,25 +653,81 @@ private void loadFieldsFromClass(Class<?> clazz, boolean mapAll, ClassConfig con
652653
String name;
653654
if (StringUtils.isBlank(binName)) {
654655
name = thisField.getName();
655-
}
656-
else {
656+
} else {
657657
name = binName;
658658
}
659659
if (isKey) {
660660
this.keyName = name;
661661
}
662-
662+
663663
if (this.values.get(name) != null) {
664664
throw new AerospikeException("Class " + clazz.getName() + " cannot define the mapped name " + name + " more than once");
665665
}
666-
AnnotatedType annotatedType = new AnnotatedType(config, thisField);
667-
TypeMapper typeMapper = TypeUtils.getMapper(thisField.getType(), annotatedType, this.mapper);
668-
ValueType valueType = new ValueType.FieldValue(thisField, typeMapper, annotatedType);
669-
values.put(name, valueType);
666+
if ((bin != null && bin.useAccessors()) || (thisBin != null && thisBin.getUseAccessors() != null && thisBin.getUseAccessors())) {
667+
validateAccessorsForField(name, thisField);
668+
} else {
669+
thisField.setAccessible(true);
670+
AnnotatedType annotatedType = new AnnotatedType(config, thisField);
671+
TypeMapper typeMapper = TypeUtils.getMapper(thisField.getType(), annotatedType, this.mapper);
672+
ValueType valueType = new ValueType.FieldValue(thisField, typeMapper, annotatedType);
673+
values.put(name, valueType);
674+
}
670675
}
671676
}
672677
}
673678

679+
private Method findMethodWithNameAndParams(String name, Class<?> ... params) {
680+
try {
681+
Method method = this.clazz.getDeclaredMethod(name, params);
682+
// TODO: Should this ascend the inheritance hierarchy using getMethod on superclasses?
683+
return method;
684+
} catch (NoSuchMethodException nsme) {
685+
return null;
686+
}
687+
}
688+
689+
private void validateAccessorsForField(String binName, Field thisField) {
690+
String fieldName = thisField.getName();
691+
String methodNameBase = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
692+
String getterName = "get" + methodNameBase;
693+
String setterName = "set" + methodNameBase;
694+
695+
Method getter = findMethodWithNameAndParams(getterName);
696+
if (getter == null) {
697+
throw new AerospikeException(String.format(
698+
"Expected to find getter for field %s on class %s due to it being configured to useAccessors, but no method with the signature \"%s %s()\" was found",
699+
fieldName,
700+
this.clazz.getSimpleName(),
701+
thisField.getType().getSimpleName(),
702+
getterName));
703+
}
704+
705+
Method setter = findMethodWithNameAndParams(setterName, thisField.getType());
706+
if (setter == null) {
707+
setter = findMethodWithNameAndParams(setterName, thisField.getType(), Key.class);
708+
}
709+
if (setter == null) {
710+
setter = findMethodWithNameAndParams(setterName, thisField.getType(), Value.class);
711+
}
712+
if (setter == null) {
713+
throw new AerospikeException(String.format(
714+
"Expected to find setter for field %s on class %s due to it being configured to useAccessors, but no method with the name \"%s\" was found",
715+
fieldName,
716+
this.clazz.getSimpleName(),
717+
setterName));
718+
}
719+
720+
AnnotatedType annotatedType = new AnnotatedType(config, thisField);
721+
TypeMapper typeMapper = TypeUtils.getMapper(thisField.getType(), annotatedType, this.mapper);
722+
PropertyDefinition property = new PropertyDefinition(binName, mapper);
723+
property.setGetter(getter);
724+
property.setSetter(setter);
725+
property.validate(clazz.getName(), config, false);
726+
727+
ValueType value = new ValueType.MethodValue(property, typeMapper, annotatedType);
728+
values.put(binName, value);
729+
}
730+
674731
public Object translateKeyToAerospikeKey(Object key) {
675732
return this.key.getTypeMapper().toAerospikeFormat(key);
676733
}

src/main/java/com/aerospike/mapper/tools/PropertyDefinition.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ else if (param.getType().isAssignableFrom(Value.class)){
104104
throw new AerospikeException(String.format("Property %s on class %s has a setter with 2 arguments, but the second one is neither a Key or a Value", this.name, className));
105105
}
106106
}
107-
else if (setter.getParameterCount() != 1) {
107+
else if (setter.getParameterCount() != 1 && setter.getParameterCount() != 2) {
108108
throw new AerospikeException(String.format("Setter for property %s on class %s must take 1 or 2 arguments", this.name, className));
109109
}
110110
setterClazz = setter.getParameterTypes()[0];

src/main/java/com/aerospike/mapper/tools/configuration/BinConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
public class BinConfig {
88
private String name;
99
private String field;
10+
private Boolean useAccessors;
1011
private String getter;
1112
private String setter;
1213
private Boolean exclude;
@@ -20,6 +21,9 @@ public String getName() {
2021
public String getField() {
2122
return field;
2223
}
24+
public Boolean getUseAccessors() {
25+
return useAccessors;
26+
}
2327
public String getGetter() {
2428
return getter;
2529
}

0 commit comments

Comments
 (0)