Skip to content

Commit 03ee63f

Browse files
committed
#36 provided way to make filtration of processed fields during map operations and write object operations
1 parent 49ff68e commit 03ee63f

File tree

8 files changed

+681
-254
lines changed

8 files changed

+681
-254
lines changed

jbbp/src/main/java/com/igormaznitsa/jbbp/io/AbstractMappedClassFieldObserver.java

Lines changed: 85 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,14 @@
1818

1919
import com.igormaznitsa.jbbp.exceptions.JBBPException;
2020
import com.igormaznitsa.jbbp.exceptions.JBBPIllegalArgumentException;
21-
import com.igormaznitsa.jbbp.mapper.Bin;
22-
import com.igormaznitsa.jbbp.mapper.BinType;
23-
import com.igormaznitsa.jbbp.mapper.JBBPMapper;
24-
import com.igormaznitsa.jbbp.mapper.MappedFieldRecord;
21+
import com.igormaznitsa.jbbp.mapper.*;
2522
import com.igormaznitsa.jbbp.model.JBBPFieldInt;
2623
import com.igormaznitsa.jbbp.model.JBBPFieldLong;
2724
import com.igormaznitsa.jbbp.model.JBBPFieldShort;
2825
import com.igormaznitsa.jbbp.model.JBBPFieldString;
2926
import com.igormaznitsa.jbbp.utils.BinAnnotationWrapper;
3027
import com.igormaznitsa.jbbp.utils.JBBPUtils;
28+
3129
import java.lang.reflect.Array;
3230
import java.lang.reflect.Field;
3331
import java.util.List;
@@ -68,7 +66,7 @@ private static Object readFieldValue(final Object obj, final MappedFieldRecord r
6866
private static void assertFieldArray(final Field field) {
6967
if (!field.getType().isArray()) {
7068
throw new IllegalArgumentException(
71-
"Detected non-array field marked to be written as an array [" + field + ']');
69+
"Detected non-array field marked to be written as an array [" + field + ']');
7270
}
7371
}
7472

@@ -83,35 +81,81 @@ private static void assertFieldArray(final Field field) {
8381
* @since 2.0.2
8482
*/
8583
protected void processObject(
86-
final Object obj,
87-
final Field field,
88-
final BinAnnotationWrapper binAnnotationWrapper,
89-
final Object customFieldProcessor
84+
final Object obj,
85+
final Field field,
86+
final BinAnnotationWrapper binAnnotationWrapper,
87+
final Object customFieldProcessor
88+
) {
89+
this.processObject(obj, field, binAnnotationWrapper, null, customFieldProcessor);
90+
}
91+
92+
/**
93+
* Process an object. It works only with classes and fields marked by Bin annotations. <b>It doesn't process classes and fields marked by DslBinCustom annotations.</b>
94+
*
95+
* @param obj an object which is an instance of a mapped class, must not be null
96+
* @param field a field where the object has been found, it can be null for first call
97+
* @param binAnnotationWrapper wrapper to replace Bin annotation values for processing fields, can be null to be ignored
98+
* @param binFieldFilter filter for mapped fields, alows to exclude some of them, can be null
99+
* @param customFieldProcessor a processor for custom fields, it can be null
100+
* @see Bin
101+
* @since 2.0.4
102+
*/
103+
protected void processObject(
104+
final Object obj,
105+
final Field field,
106+
final BinAnnotationWrapper binAnnotationWrapper,
107+
final BinFieldFilter binFieldFilter,
108+
final Object customFieldProcessor
90109
) {
91110
JBBPUtils.assertNotNull(obj, "Object must not be null");
92111

93-
final List<MappedFieldRecord> orderedFields = JBBPMapper.findAffectedFields(obj);
112+
final List<MappedFieldRecord> orderedFields = JBBPMapper.findAffectedFields(obj, binFieldFilter);
94113

95114
final Bin clazzAnno = obj.getClass().getAnnotation(Bin.class);
96115
final Bin fieldAnno = field == null ? null : field.getAnnotation(Bin.class);
97116

98-
this.onStructStart(obj, field, clazzAnno == null ? fieldAnno : clazzAnno);
117+
final Bin binAnno = clazzAnno == null ? fieldAnno : clazzAnno;
99118

100-
for (final MappedFieldRecord rec : orderedFields) {
101-
final Bin binAnno = binAnnotationWrapper == null ? rec.binAnnotation :
102-
binAnnotationWrapper.setWrapped(rec.binAnnotation);
119+
if (binFieldFilter == null || binFieldFilter.isAllowed(binAnno, field)) {
120+
this.onStructStart(obj, field, binAnno);
103121

104-
if (binAnno.custom() && customFieldProcessor == null) {
105-
throw new JBBPIllegalArgumentException(
106-
"Class '" + obj.getClass().getName() + "' contains field '" +
107-
rec.mappingField.getName() +
108-
"' which is custom one, you must provide JBBPCustomFieldWriter instance to save it.");
122+
for (final MappedFieldRecord rec : orderedFields) {
123+
final Bin annotation = binAnnotationWrapper == null ? rec.binAnnotation :
124+
binAnnotationWrapper.setWrapped(rec.binAnnotation);
125+
126+
if (binFieldFilter == null || binFieldFilter.isAllowed(annotation, rec.mappingField)) {
127+
if (annotation.custom() && customFieldProcessor == null) {
128+
throw new JBBPIllegalArgumentException(
129+
"Class '" + obj.getClass().getName() + "' contains field '" +
130+
rec.mappingField.getName() +
131+
"' which is custom one, you must provide JBBPCustomFieldWriter instance to save it.");
132+
}
133+
processObjectField(obj, rec, annotation, customFieldProcessor, binFieldFilter);
134+
}
109135
}
110136

111-
processObjectField(obj, rec, binAnno, customFieldProcessor);
137+
this.onStructEnd(obj, field, binAnno);
112138
}
139+
}
113140

114-
this.onStructEnd(obj, field, clazzAnno == null ? fieldAnno : clazzAnno);
141+
/**
142+
* Inside auxiliary method to process a field of an object.
143+
*
144+
* @param obj the object which field under processing, must not be null
145+
* @param fieldRecord internal record about the field, must not be null
146+
* @param annotation the annotation to be used as data source about the field,
147+
* must not be null
148+
* @param customFieldProcessor an object which will be provided for processing
149+
* of custom fields, must not be null if object contains custom fields
150+
* @since 2.0.4
151+
*/
152+
protected void processObjectField(
153+
final Object obj,
154+
final MappedFieldRecord fieldRecord,
155+
final Bin annotation,
156+
final Object customFieldProcessor
157+
) {
158+
this.processObjectField(obj, fieldRecord, annotation, customFieldProcessor, null);
115159
}
116160

117161
/**
@@ -123,23 +167,26 @@ protected void processObject(
123167
* must not be null
124168
* @param customFieldProcessor an object which will be provided for processing
125169
* of custom fields, must not be null if object contains custom fields
126-
* @since 2.0.2
170+
* @param binFieldFilter filter allows to exclude some fields from process, can be null
171+
* @since 2.0.4
127172
*/
128173
protected void processObjectField(
129-
final Object obj,
130-
final MappedFieldRecord fieldRecord,
131-
final Bin annotation,
132-
final Object customFieldProcessor
174+
final Object obj,
175+
final MappedFieldRecord fieldRecord,
176+
final Bin annotation,
177+
final Object customFieldProcessor,
178+
final BinFieldFilter binFieldFilter
133179
) {
134180
final Field field = fieldRecord.mappingField;
135181

136182
if (annotation.custom()) {
137183
this.onFieldCustom(obj, field, annotation, customFieldProcessor,
138-
readFieldValue(obj, fieldRecord));
184+
readFieldValue(obj, fieldRecord));
139185
} else {
140186
final Class<?> fieldType = field.getType();
141187
final BinAnnotationWrapper wrapper =
142-
annotation instanceof BinAnnotationWrapper ? (BinAnnotationWrapper) annotation : null;
188+
annotation instanceof BinAnnotationWrapper ? (BinAnnotationWrapper) annotation : null;
189+
143190
final BinType type;
144191
if (annotation.type() == BinType.UNDEFINED) {
145192
type = BinType.findCompatible(fieldType);
@@ -154,7 +201,7 @@ protected void processObjectField(
154201
final JBBPBitNumber bitNumber = annotation.bitNumber();
155202
if (fieldType == boolean.class) {
156203
this.onFieldBits(obj, field, annotation, bitNumber,
157-
((Boolean) readFieldValue(obj, fieldRecord)) ? 0xFF : 0x00);
204+
((Boolean) readFieldValue(obj, fieldRecord)) ? 0xFF : 0x00);
158205
} else {
159206
byte value = ((Number) readFieldValue(obj, fieldRecord)).byteValue();
160207
if (reverseBits) {
@@ -169,7 +216,7 @@ protected void processObjectField(
169216
onFieldBool(obj, field, annotation, (Boolean) readFieldValue(obj, fieldRecord));
170217
} else {
171218
onFieldBool(obj, field, annotation,
172-
((Number) readFieldValue(obj, fieldRecord)).longValue() != 0);
219+
((Number) readFieldValue(obj, fieldRecord)).longValue() != 0);
173220
}
174221
}
175222
break;
@@ -214,7 +261,7 @@ protected void processObjectField(
214261
}
215262
if (reverseBits) {
216263
value =
217-
Float.intBitsToFloat((int) JBBPFieldInt.reverseBits(Float.floatToIntBits(value)));
264+
Float.intBitsToFloat((int) JBBPFieldInt.reverseBits(Float.floatToIntBits(value)));
218265
}
219266
this.onFieldFloat(obj, field, annotation, value);
220267
}
@@ -253,13 +300,13 @@ protected void processObjectField(
253300

254301
if (reverseBits) {
255302
value =
256-
Double.longBitsToDouble(JBBPFieldLong.reverseBits(Double.doubleToLongBits(value)));
303+
Double.longBitsToDouble(JBBPFieldLong.reverseBits(Double.doubleToLongBits(value)));
257304
}
258305
this.onFieldDouble(obj, field, annotation, value);
259306
}
260307
break;
261308
case STRUCT: {
262-
processObject(readFieldValue(obj, fieldRecord), field, wrapper, customFieldProcessor);
309+
processObject(readFieldValue(obj, fieldRecord), field, wrapper, binFieldFilter, customFieldProcessor);
263310
}
264311
break;
265312
default: {
@@ -276,7 +323,7 @@ protected void processObjectField(
276323
if (fieldType.getComponentType() == boolean.class) {
277324
for (int i = 0; i < len; i++) {
278325
this.onFieldBits(obj, field, annotation, bitNumber,
279-
(Boolean) Array.get(array, i) ? 0xFF : 0x00);
326+
(Boolean) Array.get(array, i) ? 0xFF : 0x00);
280327
}
281328
} else {
282329
for (int i = 0; i < len; i++) {
@@ -386,7 +433,7 @@ protected void processObjectField(
386433
float value = Array.getFloat(array, i);
387434
if (reverseBits) {
388435
value = Float
389-
.intBitsToFloat((int) JBBPFieldInt.reverseBits(Float.floatToIntBits(value)));
436+
.intBitsToFloat((int) JBBPFieldInt.reverseBits(Float.floatToIntBits(value)));
390437
}
391438
this.onFieldFloat(obj, field, annotation, value);
392439
}
@@ -445,7 +492,7 @@ protected void processObjectField(
445492
double value = ((Number) Array.get(array, i)).doubleValue();
446493
if (reverseBits) {
447494
value = Double
448-
.longBitsToDouble(JBBPFieldLong.reverseBits(Double.doubleToLongBits(value)));
495+
.longBitsToDouble(JBBPFieldLong.reverseBits(Double.doubleToLongBits(value)));
449496
}
450497
this.onFieldDouble(obj, field, annotation, value);
451498
}
@@ -457,14 +504,14 @@ protected void processObjectField(
457504
final int len = Array.getLength(array);
458505
this.onArrayStart(obj, field, annotation, len);
459506
for (int i = 0; i < len; i++) {
460-
this.processObject(Array.get(array, i), field, wrapper, customFieldProcessor);
507+
this.processObject(Array.get(array, i), field, wrapper, binFieldFilter, customFieldProcessor);
461508
}
462509
this.onArrayEnd(obj, field, annotation);
463510
}
464511
break;
465512
default: {
466513
throw new Error(
467-
"Unexpected situation for field type, contact developer [" + type + ']');
514+
"Unexpected situation for field type, contact developer [" + type + ']');
468515
}
469516
}
470517
}

jbbp/src/main/java/com/igormaznitsa/jbbp/io/JBBPOut.java

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818

1919
import com.igormaznitsa.jbbp.exceptions.JBBPIOException;
2020
import com.igormaznitsa.jbbp.mapper.Bin;
21+
import com.igormaznitsa.jbbp.mapper.BinFieldFilter;
2122
import com.igormaznitsa.jbbp.mapper.JBBPMapper;
2223
import com.igormaznitsa.jbbp.model.JBBPFieldShort;
2324
import com.igormaznitsa.jbbp.utils.BinAnnotationWrapper;
2425
import com.igormaznitsa.jbbp.utils.JBBPUtils;
26+
2527
import java.io.ByteArrayOutputStream;
2628
import java.io.IOException;
2729
import java.io.OutputStream;
@@ -85,12 +87,12 @@ private JBBPOut(final OutputStream outStream, final JBBPByteOrder byteOrder,
8587
JBBPUtils.assertNotNull(bitOrder, "Bit order must not be null");
8688

8789
this.outStream = outStream instanceof JBBPBitOutputStream ? (JBBPBitOutputStream) outStream :
88-
new JBBPBitOutputStream(outStream, bitOrder);
90+
new JBBPBitOutputStream(outStream, bitOrder);
8991
this.bitOrder = this.outStream.getBitOrder();
9092
if (this.bitOrder != bitOrder) {
9193
throw new IllegalArgumentException(
92-
"Detected JBBPBitOutputStream as argument with already defined different bit order [" +
93-
this.bitOrder + ']');
94+
"Detected JBBPBitOutputStream as argument with already defined different bit order [" +
95+
this.bitOrder + ']');
9496
}
9597
this.byteOrder = byteOrder;
9698

@@ -147,7 +149,7 @@ public static JBBPOut BeginBin() {
147149
*/
148150
public static JBBPOut BeginBin(final int initialSize) {
149151
return new JBBPOut(new ByteArrayOutputStream(initialSize), DEFAULT_BYTE_ORDER,
150-
DEFAULT_BIT_ORDER);
152+
DEFAULT_BIT_ORDER);
151153
}
152154

153155
/**
@@ -1016,7 +1018,27 @@ protected void assertNotEnded() {
10161018
* @since 1.1
10171019
*/
10181020
public JBBPOut Bin(final Object object) throws IOException {
1019-
return this.Bin(object, null, null);
1021+
return this.Bin(object, null, null, null);
1022+
}
1023+
1024+
/**
1025+
* Save fields of an object marked by Bin annotation. Fields will be ordered
1026+
* through {@link Bin#order()} field, NB! By default Java doesn't keep field
1027+
* outOrder. Ordered fields of class will be saved into internal cache for speed
1028+
* but the cache can be reset through {@link JBBPMapper#clearFieldCache()}
1029+
* <b>Warning!</b> it doesn't affect byte order provided in Bin annotations of object.
1030+
*
1031+
* @param object an object to be saved into stream, must not be null
1032+
* @param binFieldFilter filter to exclude some fields from process, can be null
1033+
* @return the context
1034+
* @throws IOException it will be thrown for any transport error
1035+
* @see JBBPMapper#clearFieldCache()
1036+
* @see #BinForceByteOrder(Object)
1037+
* @see Bin
1038+
* @since 2.0.4
1039+
*/
1040+
public JBBPOut Bin(final Object object, final BinFieldFilter binFieldFilter) throws IOException {
1041+
return this.Bin(object, null, null, binFieldFilter);
10201042
}
10211043

10221044
/**
@@ -1039,6 +1061,27 @@ public JBBPOut Bin(final Object object, final JBBPCustomFieldWriter customFieldW
10391061
return this.Bin(object, null, customFieldWriter);
10401062
}
10411063

1064+
/**
1065+
* Save fields of an object marked by Bin annotation. Fields will be ordered
1066+
* through {@link Bin#order()} field, NB! By default Java doesn't keep field
1067+
* outOrder. Ordered fields of class will be saved into internal cache for speed
1068+
* but the cache can be reset through {@link JBBPMapper#clearFieldCache()}
1069+
* <b>Warning!</b> it doesn't affect byte order provided in Bin annotations of object.
1070+
*
1071+
* @param object an object to be saved into stream, must not be null
1072+
* @param customFieldWriter a custom field writer to be used for saving of
1073+
* custom fields of the object, it can be null
1074+
* @param binFieldFilter filter to exclude fields from process, can be null
1075+
* @return the context
1076+
* @see JBBPMapper#clearFieldCache()
1077+
* @see Bin
1078+
* @see #BinForceByteOrder(Object, JBBPCustomFieldWriter)
1079+
* @since 2.0.4
1080+
*/
1081+
public JBBPOut Bin(final Object object, final JBBPCustomFieldWriter customFieldWriter, final BinFieldFilter binFieldFilter) {
1082+
return this.Bin(object, null, customFieldWriter, binFieldFilter);
1083+
}
1084+
10421085
/**
10431086
* Save fields of object but bin annotation wrapper can be provided to replace some annnotation field values in <b>all</b> field annotations.
10441087
*
@@ -1049,10 +1092,29 @@ public JBBPOut Bin(final Object object, final JBBPCustomFieldWriter customFieldW
10491092
* @return the context
10501093
* @since 2.0.2
10511094
*/
1052-
public JBBPOut Bin(final Object object, final BinAnnotationWrapper binAnnotationWrapper,
1095+
public JBBPOut Bin(final Object object,
1096+
final BinAnnotationWrapper binAnnotationWrapper,
10531097
final JBBPCustomFieldWriter customFieldWriter) {
1098+
return this.Bin(object, binAnnotationWrapper, customFieldWriter, null);
1099+
}
1100+
1101+
/**
1102+
* Save fields of object but bin annotation wrapper can be provided to replace some annnotation field values in <b>all</b> field annotations.
1103+
*
1104+
* @param object an object to be saved into stream, must not be null
1105+
* @param binAnnotationWrapper wrapper for all bin annotations, can be null
1106+
* @param customFieldWriter a custom field writer to be used for saving of
1107+
* custom fields of the object, it can be null
1108+
* @param binFieldFilter filter to exclude some fields from process, can be null
1109+
* @return the context
1110+
* @since 2.0.4
1111+
*/
1112+
public JBBPOut Bin(final Object object,
1113+
final BinAnnotationWrapper binAnnotationWrapper,
1114+
final JBBPCustomFieldWriter customFieldWriter,
1115+
final BinFieldFilter binFieldFilter) {
10541116
if (this.processCommands) {
1055-
this.processObject(object, null, binAnnotationWrapper, customFieldWriter);
1117+
this.processObject(object, null, binAnnotationWrapper, binFieldFilter, customFieldWriter);
10561118
}
10571119
return this;
10581120
}
@@ -1086,7 +1148,7 @@ public JBBPOut BinForceByteOrder(final Object object) throws IOException {
10861148
public JBBPOut BinForceByteOrder(final Object object,
10871149
final JBBPCustomFieldWriter customFieldWriter) {
10881150
return this
1089-
.Bin(object, new BinAnnotationWrapper().setByteOrder(this.byteOrder), customFieldWriter);
1151+
.Bin(object, new BinAnnotationWrapper().setByteOrder(this.byteOrder), customFieldWriter);
10901152
}
10911153

10921154
@Override

0 commit comments

Comments
 (0)