package com.jsoniter.output;
import com.jsoniter.*;
import com.jsoniter.CodegenAccess;
import com.jsoniter.spi.*;
import java.lang.reflect.Method;
import java.util.*;
class CodegenImplObject {
public static CodegenResult genObject(Class clazz) {
CodegenResult ctx = new CodegenResult();
ClassDescriptor desc = JsoniterSpi.getEncodingClassDescriptor(clazz, false);
HashMap<String, Binding> bindings = new HashMap<String, Binding>();
for (Binding binding : desc.allEncoderBindings()) {
for (String toName : binding.toNames) {
bindings.put(toName, binding);
}
}
ArrayList<String> toNames = new ArrayList<String>(bindings.keySet());
Collections.sort(toNames, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int x = CodegenAccess.calcHash(o1);
int y = CodegenAccess.calcHash(o2);
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
});
ctx.append(String.format("public static void encode_(%s obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {", clazz.getCanonicalName()));
if (hasFieldOutput(desc)) {
int notFirst = 0;
ctx.buffer('{');
for (String toName : toNames) {
notFirst = genField(ctx, bindings.get(toName), toName, notFirst);
}
for (Method unwrapper : desc.unWrappers) {
notFirst = appendComma(ctx, notFirst);
ctx.append(String.format("obj.%s(stream);", unwrapper.getName()));
}
ctx.buffer('}');
} else {
ctx.buffer("{}");
}
ctx.append("}");
return ctx;
}
private static boolean hasFieldOutput(ClassDescriptor desc) {
if (!desc.unWrappers.isEmpty()) {
return true;
}
for (Binding binding : desc.allEncoderBindings()) {
if (binding.toNames.length > 0) {
return true;
}
}
return false;
}
private static int genField(CodegenResult ctx, Binding binding, String toName, int notFirst) {
String fieldCacheKey = binding.encoderCacheKey();
Encoder encoder = JsoniterSpi.getEncoder(fieldCacheKey);
boolean isCollectionValueNullable = binding.isCollectionValueNullable;
Class valueClazz;
String valueAccessor;
if (binding.field != null) {
valueClazz = binding.field.getType();
valueAccessor = "obj." + binding.field.getName();
} else {
valueClazz = binding.method.getReturnType();
valueAccessor = "obj." + binding.method.getName() + "()";
}
if (!supportCollectionValueNullable(valueClazz)) {
isCollectionValueNullable = true;
}
boolean nullable = !valueClazz.isPrimitive();
if (!binding.isNullable) {
nullable = false;
}
if (nullable) {
if (binding.shouldOmitNull) {
if (notFirst == 0) { // no previous field
notFirst = 2; // maybe
ctx.append("boolean notFirst = false;");
}
ctx.append(String.format("if (%s != null) {", valueAccessor));
notFirst = appendComma(ctx, notFirst);
ctx.append(CodegenResult.bufferToWriteOp("\"" + toName + "\":"));
} else {
notFirst = appendComma(ctx, notFirst);
ctx.buffer('"');
ctx.buffer(toName);
ctx.buffer('"');
ctx.buffer(':');
ctx.append(String.format("if (%s == null) { stream.writeNull(); } else {", valueAccessor));
}
} else {
notFirst = appendComma(ctx, notFirst);
ctx.buffer('"');
ctx.buffer(toName);
ctx.buffer('"');
ctx.buffer(':');
}
if (encoder == null) {
CodegenImplNative.genWriteOp(ctx, valueAccessor, binding.valueType, nullable, isCollectionValueNullable);
} else {
ctx.append(String.format("com.jsoniter.output.CodegenAccess.writeVal(\"%s\", %s, stream);",
fieldCacheKey, valueAccessor));
}
if (nullable) {
ctx.append("}");
}
return notFirst;
}
private static int appendComma(CodegenResult ctx, int notFirst) {
if (notFirst == 1) { // definitely not first
ctx.buffer(',');
} else if (notFirst == 2) { // maybe not first, previous field is omitNull
ctx.append("if (notFirst) { stream.write(','); } else { notFirst = true; }");
} else { // this is the first, do not write comma
notFirst = 1;
}
return notFirst;
}
private static boolean supportCollectionValueNullable(Class clazz) {
if (clazz.isArray()) {
return true;
}
if (Map.class.isAssignableFrom(clazz)) {
return true;
}
if (Collection.class.isAssignableFrom(clazz)) {
return true;
}
return false;
}
}