Skip to content

Commit 0be9bee

Browse files
committed
added support of getters and setters into mapping
1 parent 9db8d4f commit 0be9bee

File tree

7 files changed

+144
-13
lines changed

7 files changed

+144
-13
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ For instance I have been very actively using the framework in [the ZX-Poly emula
1919
- __reworked object mapping system, removed hacks to instantiate classes, now only mapping to objects allowed, support of private fields mapping is removed__
2020
- __minimal JDK version now 1.8+__
2121
- __minimal Android API now 3.0+__
22+
- added support of getters and setters into mapping
2223
- added `Object newInstance(Class)` method support of mapped classes to generate instances for local classes
2324
- added generating of `makeFIELD()` method for structure types in Java class converter
2425
- refactoring

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
- __reworked object mapping system, removed hacks to instantiate classes, now only mapping to objects allowed, support of private fields mapping is removed__
33
- __minimal JDK version now 1.8+__
44
- __minimal Android API now 3.0+__
5+
- added support of getters and setters into mapping
56
- added `Object newInstance(Class)` method support of mapped classes to generate instances for local classes
67
- added generating of `makeFIELD()` method for structure types in Java class converter
78
- refactoring

jbbp/src/main/java/com/igormaznitsa/jbbp/mapper/JBBPMapper.java

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@
2323
import com.igormaznitsa.jbbp.model.JBBPFieldStruct;
2424
import com.igormaznitsa.jbbp.utils.Function;
2525
import com.igormaznitsa.jbbp.utils.JBBPUtils;
26+
import com.igormaznitsa.jbbp.utils.NullableTriple;
2627
import com.igormaznitsa.jbbp.utils.ReflectUtils;
2728
import java.lang.reflect.Field;
29+
import java.lang.reflect.Method;
2830
import java.lang.reflect.Modifier;
2931
import java.util.ArrayList;
3032
import java.util.Collections;
3133
import java.util.List;
34+
import java.util.Locale;
3235
import java.util.Map;
3336
import java.util.concurrent.ConcurrentHashMap;
3437

@@ -339,7 +342,6 @@ public static List<MappedFieldRecord> findAffectedFields(final Object instance)
339342
if (fieldAnno == null) {
340343
if (Modifier.isTransient(fieldModifiers)
341344
|| Modifier.isStatic(fieldModifiers)
342-
|| Modifier.isPrivate(fieldModifiers)
343345
|| Modifier.isFinal(fieldModifiers)) {
344346
continue;
345347
}
@@ -349,8 +351,6 @@ public static List<MappedFieldRecord> findAffectedFields(final Object instance)
349351
disallowedModifier = "STATIC";
350352
} else if (Modifier.isFinal(fieldModifiers)) {
351353
disallowedModifier = "FINAL";
352-
} else if (Modifier.isPrivate(fieldModifiers)) {
353-
disallowedModifier = "PRIVATE";
354354
} else {
355355
disallowedModifier = null;
356356
}
@@ -359,12 +359,22 @@ public static List<MappedFieldRecord> findAffectedFields(final Object instance)
359359
}
360360
}
361361

362-
if (!ReflectUtils.isPotentiallyAccessibleField(mappingField)) {
362+
final NullableTriple<Method, Method, Method> auxMethods = findAuxFieldMethods(processingClazz, mappingField);
363+
364+
final Method fieldGenerator = auxMethods.getA();
365+
final Method fieldGetter = auxMethods.getB();
366+
final Method fieldSetter = auxMethods.getC();
367+
368+
if (fieldSetter == null && Modifier.isPrivate(mappingField.getModifiers())) {
369+
throw new JBBPMapperException("Detected private field, there must be provided setter for mapping", null, processingClazz, mappingField, null);
370+
}
371+
372+
if (fieldGetter == null && !ReflectUtils.isPotentiallyAccessibleField(mappingField)) {
363373
mappingField = ReflectUtils.makeAccessible(mappingField);
364374
}
365375

366376
try {
367-
result.add(new MappedFieldRecord(mappingField, null, null, mappingClass, mappedAnno));
377+
result.add(new MappedFieldRecord(mappingField, fieldGenerator, fieldSetter, fieldGetter, mappingClass, mappedAnno));
368378
} catch (IllegalStateException ex) {
369379
throw new JBBPMapperException(ex.getMessage(), null, mappingClass, mappingField, ex);
370380
}
@@ -379,4 +389,42 @@ public static List<MappedFieldRecord> findAffectedFields(final Object instance)
379389
return result;
380390
}
381391

392+
private static NullableTriple<Method, Method, Method> findAuxFieldMethods(final Class<?> klazz, final Field field) {
393+
final String lowerCasedFieldName = field.getName().toLowerCase(Locale.ENGLISH);
394+
final String generatorName = "make" + lowerCasedFieldName;
395+
final String getterName = "get" + lowerCasedFieldName;
396+
final String setterName = "set" + lowerCasedFieldName;
397+
398+
Method generator = null;
399+
Method setter = null;
400+
Method getter = null;
401+
402+
for (final Method m : klazz.getMethods()) {
403+
final Class<?>[] args = m.getParameterTypes();
404+
final String lcMethodName = m.getName().toLowerCase(Locale.ENGLISH);
405+
406+
if (!Modifier.isPublic(m.getModifiers()) || Modifier.isStatic(m.getModifiers())) {
407+
continue;
408+
}
409+
410+
if (args.length == 0) {
411+
if (generator == null && lcMethodName.equals(generatorName)
412+
) {
413+
generator = m;
414+
}
415+
if (getter == null && lcMethodName.equals(getterName)) {
416+
getter = m;
417+
}
418+
}
419+
420+
if (args.length == 1 && setter == null && lcMethodName.equals(setterName) && field.getType().isAssignableFrom(args[0])) {
421+
setter = m;
422+
}
423+
424+
if (generator != null && setter != null && getter != null) {
425+
break;
426+
}
427+
}
428+
return new NullableTriple<>(generator, getter, setter);
429+
}
382430
}

jbbp/src/main/java/com/igormaznitsa/jbbp/mapper/MappedFieldRecord.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,25 @@ public final class MappedFieldRecord implements Comparable<MappedFieldRecord> {
114114
mapNumericField(instance, record.setter, record.mappingField, (JBBPNumericField) binField, record.binAnnotation.bitOrder() == JBBPBitOrder.MSB0);
115115
} else if (binField instanceof JBBPFieldString) {
116116
if (record.mappingField.getType().isPrimitive()) {
117-
throw new JBBPMapperException("Can't map a string to a primitive mapping field", binField, record.mappingClass, record.mappingField, null);
117+
throw new JBBPMapperException("Can't map string to a primitive mapping field", binField, record.mappingClass, record.mappingField, null);
118118
} else {
119119
setFieldValue(instance, record.setter, record.mappingField, binField, ((JBBPFieldString) binField).getAsString());
120120
}
121121
} else if (binField instanceof JBBPFieldStruct) {
122122
if (record.mappingField.getType().isPrimitive()) {
123-
throw new JBBPMapperException("Can't map a structure to a primitive mapping field", binField, record.mappingClass, record.mappingField, null);
123+
throw new JBBPMapperException("Can't map structure to a primitive mapping field", binField, record.mappingClass, record.mappingField, null);
124124
} else {
125125
final Object curValue = getFieldValue(instance, record.getter, record.mappingField);
126126
if (curValue == null) {
127-
setFieldValue(instance, record.setter, record.mappingField, binField, JBBPMapper.map((JBBPFieldStruct) binField, tryMakeInstance(record.mappingField.getType(), binField, instance, record.mappingField, instantiators), customFieldProcessor));
127+
if (record.classMemberGenerator == null) {
128+
setFieldValue(instance, record.setter, record.mappingField, binField, JBBPMapper.map((JBBPFieldStruct) binField, tryMakeInstance(record.mappingField.getType(), binField, instance, record.mappingField, instantiators), customFieldProcessor));
129+
} else {
130+
try {
131+
JBBPMapper.map((JBBPFieldStruct) binField, record.classMemberGenerator.invoke(instance));
132+
} catch (Exception ex) {
133+
throw new JBBPMapperException("Can't map field which member generatet by instance", binField, record.mappingClass, record.mappingField, ex);
134+
}
135+
}
128136
} else {
129137
setFieldValue(instance, record.setter, record.mappingField, binField, JBBPMapper.map((JBBPFieldStruct) binField, curValue, customFieldProcessor));
130138
}
@@ -148,6 +156,7 @@ public final class MappedFieldRecord implements Comparable<MappedFieldRecord> {
148156
public final Class<?> mappingClass;
149157
public final Method setter;
150158
public final Method getter;
159+
public final Method classMemberGenerator;
151160
public final Bin binAnnotation;
152161
public final boolean bitWideField;
153162
public final String fieldName;
@@ -157,10 +166,12 @@ public final class MappedFieldRecord implements Comparable<MappedFieldRecord> {
157166
public final FieldProcessor proc;
158167

159168
MappedFieldRecord(final Field mappingField,
169+
final Method classMemberGenerator,
160170
final Method setter,
161171
final Method getter,
162172
final Class<?> mappingClass,
163173
final Bin binAnnotation) {
174+
this.classMemberGenerator = classMemberGenerator;
164175
this.setter = setter;
165176
this.getter = getter;
166177

@@ -393,9 +404,9 @@ private static void mapNumericField(final Object mappingClassInstance, final Met
393404
*/
394405
private static Object getFieldValue(final Object classInstance, final Method getter, final Field classField) {
395406
try {
396-
if (getter == null)
407+
if (getter == null) {
397408
return classField.get(classInstance);
398-
else {
409+
} else {
399410
return getter.invoke(classInstance);
400411
}
401412
} catch (IllegalArgumentException ex) {
@@ -412,17 +423,17 @@ private static Object getFieldValue(final Object classInstance, final Method get
412423
* fields!
413424
*
414425
* @param classInstance a class instance
415-
* @param setter setter for the field, can be null
426+
* @param setter setter for the field, can be null
416427
* @param classField a mapping class field which should be set by the value,
417428
* must not be null
418429
* @param binField a parsed bin field which value will be set, can be null
419430
* @param value a value to be set to the class field
420431
*/
421432
static void setFieldValue(final Object classInstance, final Method setter, final Field classField, final JBBPAbstractField binField, final Object value) {
422433
try {
423-
if (setter == null)
434+
if (setter == null) {
424435
classField.set(classInstance, value);
425-
else {
436+
} else {
426437
setter.invoke(classInstance, value);
427438
}
428439
} catch (IllegalArgumentException ex) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.igormaznitsa.jbbp.utils;
2+
3+
/**
4+
* Auxiliary class to keep three values. Any value can be null.
5+
*
6+
* @param <A> first value type
7+
* @param <B> second value type
8+
* @param <C> third value type
9+
* @since 2.0.0
10+
*/
11+
public final class NullableTriple<A, B, C> {
12+
private final A a;
13+
private final B b;
14+
private final C c;
15+
16+
public NullableTriple(A a, B b, C c) {
17+
this.a = a;
18+
this.b = b;
19+
this.c = c;
20+
}
21+
22+
public A getA() {
23+
return this.a;
24+
}
25+
26+
public B getB() {
27+
return this.b;
28+
}
29+
30+
public C getC() {
31+
return this.c;
32+
}
33+
34+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2017 Igor Maznitsa.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.igormaznitsa.jbbp.mapper;
18+
19+
class ClassWithPrivateFieldsAndSetterGetter {
20+
@Bin
21+
private int field;
22+
23+
public int getField() {
24+
return this.field;
25+
}
26+
27+
public void setField(int value) {
28+
this.field = value;
29+
}
30+
}

jbbp/src/test/java/com/igormaznitsa/jbbp/mapper/JBBPMapperTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,12 @@ void testMap_privateFieldInPackagelevelClass() {
542542
assertThrows(JBBPMapperException.class, () -> JBBPParser.prepare("int field;").parse(new byte[] {1, 2, 3, 4}).mapTo(new ClassWithPrivateFields()));
543543
}
544544

545+
@Test
546+
void testMap_privateFieldWithSetterInPackagelevelClass() throws Exception {
547+
final ClassWithPrivateFieldsAndSetterGetter instance = JBBPParser.prepare("int field;").parse(new byte[] {1, 2, 3, 4}).mapTo(new ClassWithPrivateFieldsAndSetterGetter());
548+
assertEquals(0x1020304, instance.getField());
549+
}
550+
545551
@Test
546552
void testMap_customMappingFields_Class() throws Exception {
547553
final class Mapped {

0 commit comments

Comments
 (0)