Skip to content

Commit cf98015

Browse files
committed
support setter
1 parent 5e87258 commit cf98015

File tree

11 files changed

+280
-78
lines changed

11 files changed

+280
-78
lines changed

src/main/java/com/jsoniter/Codegen.java

Lines changed: 71 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,23 @@
55
import javassist.CtMethod;
66
import javassist.CtNewMethod;
77

8-
import java.io.IOException;
98
import java.lang.reflect.*;
109
import java.math.BigDecimal;
1110
import java.math.BigInteger;
1211
import java.util.*;
1312

1413
class Codegen {
1514
static boolean strictMode = false;
15+
final static Map<String, String> DEFAULT_VALUES = new HashMap<String, String>() {{
16+
put("float", "0.0f");
17+
put("double", "0.0d");
18+
put("boolean", "false");
19+
put("byte", "0");
20+
put("short", "0");
21+
put("int", "0");
22+
put("char", "0");
23+
put("long", "0");
24+
}};
1625
final static Map<String, String> NATIVE_READS = new HashMap<String, String>() {{
1726
put("float", "iter.readFloat()");
1827
put("double", "iter.readDouble()");
@@ -42,7 +51,6 @@ class Codegen {
4251
add(Vector.class);
4352
}};
4453
static volatile Map<String, Decoder> cache = new HashMap<String, Decoder>();
45-
static List<Extension> extensions = new ArrayList<Extension>();
4654
static ClassPool pool = ClassPool.getDefault();
4755

4856
static Decoder getDecoder(String cacheKey, Type type, Type... typeArgs) {
@@ -141,17 +149,6 @@ private static String genSource(String cacheKey, Class clazz, Type[] typeArgs) {
141149
return genCollection(clazz, compType);
142150
}
143151
}
144-
// TODO: re-enable this optimization
145-
// if (clazz.getFields().length == 0) {
146-
// StringBuilder lines = new StringBuilder();
147-
// append(lines, "public static Object decode_(com.jsoniter.Jsoniter iter) {");
148-
// append(lines, "if (iter.readNull()) { return null; }");
149-
// append(lines, "{{clazz}} obj = {{newInst}};");
150-
// append(lines, "iter.skip();");
151-
// append(lines, "return obj;");
152-
// append(lines, "}");
153-
// return lines.toString().replace("{{clazz}}", clazz.getCanonicalName()).replace("{{newInst}}", genNewInstCode(clazz, getCtor(clazz)));
154-
// }
155152
if (strictMode) {
156153
return genObjectUsingSlice(clazz, cacheKey);
157154
} else {
@@ -207,19 +204,31 @@ public static void addNewDecoder(String cacheKey, Decoder decoder) {
207204
}
208205

209206
private static String genObjectUsingHash(Class clazz, String cacheKey) {
210-
CustomizedConstructor ctor = getCtor(clazz);
211-
List<Binding> fields = getFields(clazz);
207+
CustomizedConstructor ctor = ExtensionManager.getCtor(clazz);
208+
List<Binding> fields = ExtensionManager.getFields(clazz);
209+
List<CustomizedSetter> setters = ExtensionManager.getSetters(clazz);
212210
ArrayList<Binding> allBindings = new ArrayList<Binding>(fields);
213211
allBindings.addAll(ctor.parameters);
212+
for (CustomizedSetter setter : setters) {
213+
allBindings.addAll(setter.parameters);
214+
}
215+
if (allBindings.isEmpty()) {
216+
return genObjectUsingSkip(clazz, ctor);
217+
}
214218
StringBuilder lines = new StringBuilder();
215219
append(lines, "public static Object decode_(com.jsoniter.Jsoniter iter) {");
216220
append(lines, "if (iter.readNull()) { return null; }");
217221
for (Binding parameter : ctor.parameters) {
218-
append(lines, String.format("%s _%s_ = null;", getTypeName(parameter.valueType), parameter.name));
222+
appendVarDef(lines, parameter);
219223
}
220224
append(lines, "if (!com.jsoniter.CodegenAccess.readObjectStart(iter)) { return {{newInst}}; }");
221225
for (Binding field : fields) {
222-
append(lines, String.format("%s _%s_;", getTypeName(field.valueType), field.name));
226+
appendVarDef(lines, field);
227+
}
228+
for (CustomizedSetter setter : setters) {
229+
for (Binding param : setter.parameters) {
230+
appendVarDef(lines, param);
231+
}
223232
}
224233
append(lines, "switch (com.jsoniter.CodegenAccess.readObjectFieldAsHash(iter)) {");
225234
HashSet<Integer> knownHashes = new HashSet<Integer>();
@@ -270,6 +279,30 @@ private static String genObjectUsingHash(Class clazz, String cacheKey) {
270279
for (Binding field : fields) {
271280
append(lines, String.format("obj.%s = _%s_;", field.name, field.name));
272281
}
282+
for (CustomizedSetter setter : setters) {
283+
lines.append("obj.");
284+
lines.append(setter.methodName);
285+
appendInvocation(lines, setter.parameters);
286+
lines.append(";\n");
287+
}
288+
append(lines, "return obj;");
289+
append(lines, "}");
290+
return lines.toString()
291+
.replace("{{clazz}}", clazz.getCanonicalName())
292+
.replace("{{newInst}}", genNewInstCode(clazz, ctor));
293+
}
294+
295+
private static void appendVarDef(StringBuilder lines, Binding parameter) {
296+
String typeName = getTypeName(parameter.valueType);
297+
append(lines, String.format("%s _%s_ = %s;", typeName, parameter.name, DEFAULT_VALUES.get(typeName)));
298+
}
299+
300+
private static String genObjectUsingSkip(Class clazz, CustomizedConstructor ctor) {
301+
StringBuilder lines = new StringBuilder();
302+
append(lines, "public static Object decode_(com.jsoniter.Jsoniter iter) {");
303+
append(lines, "if (iter.readNull()) { return null; }");
304+
append(lines, "{{clazz}} obj = {{newInst}};");
305+
append(lines, "iter.skip();");
273306
append(lines, "return obj;");
274307
append(lines, "}");
275308
return lines.toString()
@@ -284,48 +317,23 @@ private static String genNewInstCode(Class clazz, CustomizedConstructor ctor) {
284317
} else {
285318
code.append(String.format("%s.%s", clazz.getCanonicalName(), ctor.staticMethodName));
286319
}
320+
List<Binding> params = ctor.parameters;
321+
appendInvocation(code, params);
322+
return code.toString();
323+
}
324+
325+
private static void appendInvocation(StringBuilder code, List<Binding> params) {
287326
code.append("(");
288327
boolean isFirst = true;
289-
for (Binding ctorParam : ctor.parameters) {
328+
for (Binding ctorParam : params) {
290329
if (isFirst) {
291330
isFirst = false;
292331
} else {
293332
code.append(",");
294333
}
295-
code.append("_" + ctorParam.name + "_");
334+
code.append(String.format("_%s_", ctorParam.name));
296335
}
297336
code.append(")");
298-
return code.toString();
299-
}
300-
301-
private static CustomizedConstructor getCtor(Class clazz) {
302-
for (Extension extension : extensions) {
303-
CustomizedConstructor ctor = extension.getConstructor(clazz);
304-
if (ctor != null) {
305-
return ctor;
306-
}
307-
}
308-
return CustomizedConstructor.DEFAULT_INSTANCE;
309-
}
310-
311-
private static List<Binding> getFields(Class clazz) {
312-
ArrayList<Binding> bindings = new ArrayList<Binding>();
313-
for (Field field : clazz.getFields()) {
314-
Binding binding = new Binding();
315-
binding.fromNames = new String[]{field.getName()};
316-
for (Extension extension : extensions) {
317-
String[] fromNames = extension.getAlternativeFieldNames(binding);
318-
if (fromNames != null) {
319-
binding.fromNames = fromNames;
320-
break;
321-
}
322-
}
323-
binding.name = field.getName();
324-
binding.valueType = field.getType();
325-
binding.clazz = clazz;
326-
bindings.add(binding);
327-
}
328-
return bindings;
329337
}
330338

331339
private static String genObjectUsingSlice(Class clazz, String cacheKey) {
@@ -368,12 +376,12 @@ private static String genObjectUsingSlice(Class clazz, String cacheKey) {
368376
append(lines, "}");
369377
return lines.toString()
370378
.replace("{{clazz}}", clazz.getCanonicalName())
371-
.replace("{{newInst}}", genNewInstCode(clazz, getCtor(clazz)));
379+
.replace("{{newInst}}", genNewInstCode(clazz, ExtensionManager.getCtor(clazz)));
372380
}
373381

374382
private static Map<Integer, Object> buildTriTree(Class clazz) {
375383
Map<Integer, Object> trieTree = new HashMap<Integer, Object>();
376-
for (Binding field : getFields(clazz)) {
384+
for (Binding field : ExtensionManager.getFields(clazz)) {
377385
for (String fromName : field.fromNames) {
378386
byte[] fromNameBytes = fromName.getBytes();
379387
Map<Byte, Object> current = (Map<Byte, Object>) trieTree.get(fromNameBytes.length);
@@ -397,16 +405,18 @@ private static Map<Integer, Object> buildTriTree(Class clazz) {
397405
}
398406

399407
private static Decoder createFieldDecoder(String fieldCacheKey, Binding field) {
400-
for (Extension extension : extensions) {
401-
Decoder decoder = extension.createDecoder(field);
402-
if (decoder != null) {
403-
addNewDecoder(fieldCacheKey, decoder);
404-
break;
405-
}
408+
// directly registered field decoder
409+
Decoder decoder = cache.get(fieldCacheKey);
410+
if (decoder != null) {
411+
return decoder;
406412
}
407-
// the decoder can be just created by the factory
408-
// or it can be registered directly
409-
return cache.get(fieldCacheKey);
413+
// provided by extension
414+
decoder = ExtensionManager.createFieldDecoder(fieldCacheKey, field);
415+
if (decoder != null) {
416+
addNewDecoder(fieldCacheKey, decoder);
417+
return decoder;
418+
}
419+
return null;
410420
}
411421

412422
private static void addFieldDispatch(StringBuilder lines, int len, int i, Map<Byte, Object> current, String cacheKey, List<Byte> bytesToCompare) {
@@ -672,10 +682,6 @@ private static void append(StringBuilder lines, String str) {
672682
lines.append("\n");
673683
}
674684

675-
public static void registerExtension(Extension extension) {
676-
extensions.add(extension);
677-
}
678-
679685
public static Decoder.IntDecoder getIntDecoder(String cacheKey) {
680686
return (Decoder.IntDecoder) cache.get(cacheKey);
681687
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.jsoniter;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
public class CustomizedSetter {
7+
/**
8+
* which method to call to set value
9+
*/
10+
public String methodName;
11+
12+
/**
13+
* the parameters to bind
14+
*/
15+
public List<Binding> parameters = new ArrayList<Binding>();
16+
}
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.jsoniter;
22

3-
import java.lang.reflect.Field;
4-
import java.lang.reflect.Type;
53
import java.util.List;
64

75
public class EmptyExtension implements Extension {
@@ -12,12 +10,17 @@ public Decoder createDecoder(Binding field) {
1210
}
1311

1412
@Override
15-
public String[] getAlternativeFieldNames(Binding field) {
13+
public String[] getBindFrom(Binding field) {
1614
return null;
1715
}
1816

1917
@Override
2018
public CustomizedConstructor getConstructor(Class clazz) {
2119
return null;
2220
}
21+
22+
@Override
23+
public List<CustomizedSetter> getSetters(Class clazz) {
24+
return null;
25+
}
2326
}
Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.jsoniter;
22

3-
import java.lang.reflect.Field;
4-
import java.lang.reflect.Type;
53
import java.util.List;
64

75
public interface Extension {
@@ -14,17 +12,26 @@ public interface Extension {
1412
Decoder createDecoder(Binding field);
1513

1614
/**
17-
* Customize the field map to
15+
* Customize the binding source
1816
*
1917
* @param field the field reflection object
20-
* @return null, if fallback to default behavior
18+
* @return null, if fallback to default behavior. empty array to disable this binding
2119
*/
22-
String[] getAlternativeFieldNames(Binding field);
20+
String[] getBindFrom(Binding field);
2321

2422
/**
25-
* customize which constructor to call
23+
* Customize which constructor to call
24+
*
2625
* @param clazz the instance class to create
2726
* @return null, if fallback to default behavior
2827
*/
2928
CustomizedConstructor getConstructor(Class clazz);
29+
30+
/**
31+
* Customize setters to call after instance is created and fields set
32+
*
33+
* @param clazz the class that is binding
34+
* @return null, if fallback to default behavior
35+
*/
36+
List<CustomizedSetter> getSetters(Class clazz);
3037
}

0 commit comments

Comments
 (0)