10
10
import java .util .HashMap ;
11
11
import java .util .List ;
12
12
import java .util .Map ;
13
- import java .util .Map .Entry ;
14
13
import java .util .concurrent .atomic .AtomicReference ;
15
14
16
15
/**
17
- * Converts between {@link Map } properties and byte arrays using FlexBuffers.
16
+ * Converts between {@link Object } properties and byte arrays using FlexBuffers.
18
17
* <p>
19
- * All keys must have the same type (see {@link #convertToKey(String)}),
20
- * value types are limited to those supported by FlexBuffers.
18
+ * Types are limited to those supported by FlexBuffers, including that map keys must be {@link String}.
19
+ * (There are subclasses available that auto-convert {@link Integer} and {@link Long} key maps,
20
+ * see {@link #convertToKey}.)
21
21
* <p>
22
22
* If any item requires 64 bits for storage in the FlexBuffers Map/Vector (a large Long, a Double)
23
- * all integers are restored as Long, otherwise Integer.
23
+ * all integers are restored as {@link Long}, otherwise {@link Integer}.
24
+ * So e.g. when storing only a {@link Long} value of {@code 1L}, the value restored from the
25
+ * database will be of type {@link Integer}.
26
+ * (There are subclasses available that always restore as {@link Long}, see {@link #shouldRestoreAsLong}.)
27
+ * <p>
28
+ * Values of type {@link Float} are always restored as {@link Double}.
29
+ * Cast to {@link Float} to obtain the original value.
24
30
*/
25
- public abstract class FlexMapConverter implements PropertyConverter <Map < Object , Object > , byte []> {
31
+ public class FlexObjectConverter implements PropertyConverter <Object , byte []> {
26
32
27
33
private static final AtomicReference <FlexBuffersBuilder > cachedBuilder = new AtomicReference <>();
28
34
29
35
@ Override
30
- public byte [] convertToDatabaseValue (Map < Object , Object > map ) {
31
- if (map == null ) return null ;
36
+ public byte [] convertToDatabaseValue (Object value ) {
37
+ if (value == null ) return null ;
32
38
33
39
FlexBuffersBuilder builder = cachedBuilder .getAndSet (null );
34
40
if (builder == null ) {
@@ -40,7 +46,7 @@ public byte[] convertToDatabaseValue(Map<Object, Object> map) {
40
46
);
41
47
}
42
48
43
- addMap (builder , null , map );
49
+ addValue (builder , value );
44
50
45
51
ByteBuffer buffer = builder .finish ();
46
52
@@ -56,16 +62,59 @@ public byte[] convertToDatabaseValue(Map<Object, Object> map) {
56
62
return out ;
57
63
}
58
64
65
+ private void addValue (FlexBuffersBuilder builder , Object value ) {
66
+ if (value instanceof Map ) {
67
+ //noinspection unchecked
68
+ addMap (builder , null , (Map <Object , Object >) value );
69
+ } else if (value instanceof List ) {
70
+ //noinspection unchecked
71
+ addVector (builder , null , (List <Object >) value );
72
+ } else if (value instanceof String ) {
73
+ builder .putString ((String ) value );
74
+ } else if (value instanceof Boolean ) {
75
+ builder .putBoolean ((Boolean ) value );
76
+ } else if (value instanceof Byte ) {
77
+ // Will always be restored as Integer.
78
+ builder .putInt (((Byte ) value ).intValue ());
79
+ } else if (value instanceof Short ) {
80
+ // Will always be restored as Integer.
81
+ builder .putInt (((Short ) value ).intValue ());
82
+ } else if (value instanceof Integer ) {
83
+ builder .putInt ((Integer ) value );
84
+ } else if (value instanceof Long ) {
85
+ builder .putInt ((Long ) value );
86
+ } else if (value instanceof Float ) {
87
+ builder .putFloat ((Float ) value );
88
+ } else if (value instanceof Double ) {
89
+ builder .putFloat ((Double ) value );
90
+ } else if (value instanceof byte []) {
91
+ builder .putBlob ((byte []) value );
92
+ } else {
93
+ throw new IllegalArgumentException (
94
+ "Values of this type are not supported: " + value .getClass ().getSimpleName ());
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Checks Java map key is of the expected type, otherwise throws.
100
+ */
101
+ protected void checkMapKeyType (Object rawKey ) {
102
+ if (!(rawKey instanceof String )) {
103
+ throw new IllegalArgumentException ("Map keys must be String" );
104
+ }
105
+ }
106
+
59
107
private void addMap (FlexBuffersBuilder builder , String mapKey , Map <Object , Object > map ) {
60
108
int mapStart = builder .startMap ();
61
109
62
- for (Entry <Object , Object > entry : map .entrySet ()) {
110
+ for (Map .Entry <Object , Object > entry : map .entrySet ()) {
111
+ Object rawKey = entry .getKey ();
63
112
Object value = entry .getValue ();
64
- if (entry . getKey () == null || value == null ) {
113
+ if (rawKey == null || value == null ) {
65
114
throw new IllegalArgumentException ("Map keys or values must not be null" );
66
115
}
67
-
68
- String key = entry . getKey () .toString ();
116
+ checkMapKeyType ( rawKey );
117
+ String key = rawKey .toString ();
69
118
if (value instanceof Map ) {
70
119
//noinspection unchecked
71
120
addMap (builder , key , (Map <Object , Object >) value );
@@ -76,6 +125,12 @@ private void addMap(FlexBuffersBuilder builder, String mapKey, Map<Object, Objec
76
125
builder .putString (key , (String ) value );
77
126
} else if (value instanceof Boolean ) {
78
127
builder .putBoolean (key , (Boolean ) value );
128
+ } else if (value instanceof Byte ) {
129
+ // Will always be restored as Integer.
130
+ builder .putInt (key , ((Byte ) value ).intValue ());
131
+ } else if (value instanceof Short ) {
132
+ // Will always be restored as Integer.
133
+ builder .putInt (key , ((Short ) value ).intValue ());
79
134
} else if (value instanceof Integer ) {
80
135
builder .putInt (key , (Integer ) value );
81
136
} else if (value instanceof Long ) {
@@ -99,6 +154,9 @@ private void addVector(FlexBuffersBuilder builder, String vectorKey, List<Object
99
154
int vectorStart = builder .startVector ();
100
155
101
156
for (Object item : list ) {
157
+ if (item == null ) {
158
+ throw new IllegalArgumentException ("List elements must not be null" );
159
+ }
102
160
if (item instanceof Map ) {
103
161
//noinspection unchecked
104
162
addMap (builder , null , (Map <Object , Object >) item );
@@ -109,6 +167,12 @@ private void addVector(FlexBuffersBuilder builder, String vectorKey, List<Object
109
167
builder .putString ((String ) item );
110
168
} else if (item instanceof Boolean ) {
111
169
builder .putBoolean ((Boolean ) item );
170
+ } else if (item instanceof Byte ) {
171
+ // Will always be restored as Integer.
172
+ builder .putInt (((Byte ) item ).intValue ());
173
+ } else if (item instanceof Short ) {
174
+ // Will always be restored as Integer.
175
+ builder .putInt (((Short ) item ).intValue ());
112
176
} else if (item instanceof Integer ) {
113
177
builder .putInt ((Integer ) item );
114
178
} else if (item instanceof Long ) {
@@ -129,20 +193,42 @@ private void addVector(FlexBuffersBuilder builder, String vectorKey, List<Object
129
193
}
130
194
131
195
@ Override
132
- public Map < Object , Object > convertToEntityProperty (byte [] databaseValue ) {
196
+ public Object convertToEntityProperty (byte [] databaseValue ) {
133
197
if (databaseValue == null ) return null ;
134
198
135
- FlexBuffers .Map map = FlexBuffers .getRoot (new ArrayReadWriteBuf (databaseValue , databaseValue .length )).asMap ();
136
-
137
- return buildMap (map );
199
+ FlexBuffers .Reference value = FlexBuffers .getRoot (new ArrayReadWriteBuf (databaseValue , databaseValue .length ));
200
+ if (value .isMap ()) {
201
+ return buildMap (value .asMap ());
202
+ } else if (value .isVector ()) {
203
+ return buildList (value .asVector ());
204
+ } else if (value .isString ()) {
205
+ return value .asString ();
206
+ } else if (value .isBoolean ()) {
207
+ return value .asBoolean ();
208
+ } else if (value .isInt ()) {
209
+ if (shouldRestoreAsLong (value )) {
210
+ return value .asLong ();
211
+ } else {
212
+ return value .asInt ();
213
+ }
214
+ } else if (value .isFloat ()) {
215
+ // Always return as double; if original was float consumer can cast to obtain original value.
216
+ return value .asFloat ();
217
+ } else if (value .isBlob ()) {
218
+ return value .asBlob ().getBytes ();
219
+ } else {
220
+ throw new IllegalArgumentException ("FlexBuffers type is not supported: " + value .getType ());
221
+ }
138
222
}
139
223
140
224
/**
141
225
* Converts a FlexBuffers string map key to the Java map key (e.g. String to Integer).
142
226
* <p>
143
227
* This required conversion restricts all keys (root and embedded maps) to the same type.
144
228
*/
145
- abstract Object convertToKey (String keyValue );
229
+ Object convertToKey (String keyValue ) {
230
+ return keyValue ;
231
+ }
146
232
147
233
/**
148
234
* Returns true if the width in bytes stored in the private parentWidth field of FlexBuffers.Reference is 8.
@@ -190,7 +276,7 @@ private Map<Object, Object> buildMap(FlexBuffers.Map map) {
190
276
resultMap .put (key , value .asInt ());
191
277
}
192
278
} else if (value .isFloat ()) {
193
- // Always return as double; if original was float casting will give original value.
279
+ // Always return as double; if original was float consumer can cast to obtain original value.
194
280
resultMap .put (key , value .asFloat ());
195
281
} else if (value .isBlob ()) {
196
282
resultMap .put (key , value .asBlob ().getBytes ());
@@ -230,7 +316,7 @@ private List<Object> buildList(FlexBuffers.Vector vector) {
230
316
list .add (item .asInt ());
231
317
}
232
318
} else if (item .isFloat ()) {
233
- // Always return as double; if original was float casting will give original value.
319
+ // Always return as double; if original was float consumer can cast to obtain original value.
234
320
list .add (item .asFloat ());
235
321
} else if (item .isBlob ()) {
236
322
list .add (item .asBlob ().getBytes ());
@@ -242,5 +328,4 @@ private List<Object> buildList(FlexBuffers.Vector vector) {
242
328
243
329
return list ;
244
330
}
245
-
246
331
}
0 commit comments