package de.jpaw.bonaparte.vertx;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.joda.time.Instant;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.vertx.java.core.json.JsonArray;
import org.vertx.java.core.json.JsonObject;
import de.jpaw.bonaparte.core.AbstractMessageComposer;
import de.jpaw.bonaparte.core.BonaCustom;
import de.jpaw.bonaparte.core.MimeTypes;
import de.jpaw.bonaparte.core.StaticMeta;
import de.jpaw.bonaparte.enums.BonaNonTokenizableEnum;
import de.jpaw.bonaparte.enums.BonaTokenizableEnum;
import de.jpaw.bonaparte.pojos.meta.AlphanumericElementaryDataItem;
import de.jpaw.bonaparte.pojos.meta.BasicNumericElementaryDataItem;
import de.jpaw.bonaparte.pojos.meta.BinaryElementaryDataItem;
import de.jpaw.bonaparte.pojos.meta.EnumDataItem;
import de.jpaw.bonaparte.pojos.meta.FieldDefinition;
import de.jpaw.bonaparte.pojos.meta.MiscElementaryDataItem;
import de.jpaw.bonaparte.pojos.meta.NumericElementaryDataItem;
import de.jpaw.bonaparte.pojos.meta.ObjectReference;
import de.jpaw.bonaparte.pojos.meta.TemporalElementaryDataItem;
import de.jpaw.bonaparte.pojos.meta.XEnumDataItem;
import de.jpaw.enums.XEnum;
import de.jpaw.util.ByteArray;
/** Composer which serializes all data into a vertx JsonObject */
public class JsonObjectComposer extends AbstractMessageComposer<RuntimeException> {
protected static final DateTimeFormatter LOCAL_DATE_ISO = ISODateTimeFormat.basicDate();
protected static final DateTimeFormatter LOCAL_DATETIME_ISO = ISODateTimeFormat.basicDateTime();
protected static final DateTimeFormatter LOCAL_TIME_ISO = ISODateTimeFormat.basicTime();
protected JsonObject obj = null;
protected JsonArray arr = null;
protected boolean inArray = false;
protected boolean writeNulls = false;
protected String rememberedArrayFieldName = null;
// static converter method for convenience
public static JsonObject toJsonObject(BonaCustom o, boolean writeNulls) {
JsonObjectComposer composer = new JsonObjectComposer(writeNulls);
composer.writeRecord(o);
return composer.getObject();
}
public static JsonObject toJsonObject(BonaCustom o) {
return toJsonObject(o, false);
}
// retrieve the converted object
public JsonObject getObject() {
return obj;
}
public JsonObjectComposer() {
}
public JsonObjectComposer(boolean writeNulls) {
this.writeNulls = writeNulls;
}
@Override
public void writeNull(FieldDefinition di) { // nulls are not written, unless we are in an array
if (inArray) {
arr.addObject(null);
} else if (writeNulls) {
obj.putObject(di.getName(), null);
}
}
@Override
public void writeNullCollection(FieldDefinition di) {
}
@Override
public void startTransmission() {
}
@Override
public void startRecord() {
// reset to consistent state for sanity
obj = null;
arr = null;
inArray = false;
}
@Override
public void startObject(ObjectReference di, BonaCustom o) {
obj = new JsonObject();
obj.putString(MimeTypes.JSON_FIELD_PQON, o.ret$PQON()); // insert the actual object type
}
@Override
public void terminateObject(ObjectReference di, BonaCustom o) {
}
@Override
public void startArray(FieldDefinition di, int currentMembers, int sizeOfElement) {
if (inArray)
throw new IllegalArgumentException("Nested arrays are not supported");
inArray = true;
arr = new JsonArray();
rememberedArrayFieldName = di.getName();
}
@Override
public void startMap(FieldDefinition di, int currentMembers) {
throw new IllegalArgumentException("Maps are not supported for Json conversion.");
}
@Override
public void writeSuperclassSeparator() {
}
@Override
public void terminateMap() {
}
@Override
public void terminateArray() {
if (!inArray) {
throw new IllegalArgumentException("Cannot terminate array, as non is open");
}
obj.putArray(rememberedArrayFieldName, arr);
inArray = false;
arr = null;
}
@Override
public void terminateRecord() {
}
@Override
public void terminateTransmission() {
}
@Override
public void writeRecord(BonaCustom o) {
startRecord();
// addField(StaticMeta.OUTER_BONAPORTABLE, o);
// start a new object
startObject(StaticMeta.OUTER_BONAPORTABLE, o);
o.serializeSub(this);
terminateObject(StaticMeta.OUTER_BONAPORTABLE, o);
terminateRecord();
}
@Override
public void addField(ObjectReference di, BonaCustom o) {
if (inArray)
arr.addObject(toJsonObject(o, writeNulls));
else if (o != null) {
obj.putObject(di.getName(), toJsonObject(o, writeNulls));
} else {
obj.putObject(di.getName(), null);
}
}
@Override
public void addField(MiscElementaryDataItem di, boolean b) {
if (inArray)
arr.addBoolean(b);
else
obj.putBoolean(di.getName(), b);
}
@Override
public void addField(MiscElementaryDataItem di, char c) {
writeNonNull(di, String.valueOf(c));
}
@Override
public void addField(BasicNumericElementaryDataItem di, double d) {
if (inArray)
arr.addNumber(d);
else
obj.putNumber(di.getName(), d);
}
@Override
public void addField(BasicNumericElementaryDataItem di, float f) {
if (inArray)
arr.addNumber(f);
else
obj.putNumber(di.getName(), f);
}
@Override
public void addField(BasicNumericElementaryDataItem di, byte n) {
if (inArray)
arr.addNumber(n);
else
obj.putNumber(di.getName(), n);
}
@Override
public void addField(BasicNumericElementaryDataItem di, short n) {
if (inArray)
arr.addNumber(n);
else
obj.putNumber(di.getName(), n);
}
@Override
public void addField(BasicNumericElementaryDataItem di, int n) {
if (inArray)
arr.addNumber(n);
else
obj.putNumber(di.getName(), n);
}
@Override
public void addField(BasicNumericElementaryDataItem di, long n) {
if (inArray)
arr.addNumber(n);
else
obj.putNumber(di.getName(), n);
}
protected void writeNonNull(FieldDefinition di, String s) {
if (inArray)
arr.add(s);
else
obj.putString(di.getName(), s);
}
@Override
public void addField(AlphanumericElementaryDataItem di, String s) {
if (s == null) {
writeNull(di);
} else {
writeNonNull(di, s);
}
}
@Override
public void addField(MiscElementaryDataItem di, UUID n) {
if (n == null) {
writeNull(di);
} else {
writeNonNull(di, n.toString());
}
}
@Override
public void addField(BinaryElementaryDataItem di, ByteArray b) {
if (inArray)
arr.addBinary(b == null ? null : b.getBytes());
else if (b != null)
obj.putBinary(di.getName(), b.getBytes());
else
writeNull(di);
}
@Override
public void addField(BinaryElementaryDataItem di, byte[] b) {
if (inArray)
arr.addBinary(b);
else if (b != null)
obj.putBinary(di.getName(), b);
else
writeNull(di);
}
@Override
public void addField(BasicNumericElementaryDataItem di, BigInteger n) {
if (inArray)
arr.addNumber(n);
else if (n != null)
obj.putNumber(di.getName(), n);
else
writeNull(di);
}
@Override
public void addField(NumericElementaryDataItem di, BigDecimal n) {
if (inArray)
arr.addNumber(n);
else if (n != null)
obj.putNumber(di.getName(), n);
else
writeNull(di);
}
@Override
public void addField(TemporalElementaryDataItem di, LocalDate t) {
if (t == null) {
writeNull(di);
} else {
writeNonNull(di, LOCAL_DATE_ISO.print(t));
}
}
@Override
public void addField(TemporalElementaryDataItem di, LocalDateTime t) {
if (t == null) {
writeNull(di);
} else {
writeNonNull(di, LOCAL_DATETIME_ISO.print(t));
}
}
@Override
public void addField(TemporalElementaryDataItem di, LocalTime t) {
if (t == null) {
writeNull(di);
} else {
writeNonNull(di, LOCAL_TIME_ISO.print(t));
}
}
@Override
public void addField(TemporalElementaryDataItem di, Instant t) {
if (t == null) {
writeNull(di);
} else {
writeNonNull(di, LOCAL_DATETIME_ISO.print(t));
}
}
@Override
public void addEnum(EnumDataItem di, BasicNumericElementaryDataItem ord, BonaNonTokenizableEnum n) {
if (n == null) {
writeNull(di);
} else {
if (inArray)
arr.addNumber(n.ordinal());
else
obj.putNumber(di.getName(), n.ordinal());
}
}
@Override
public void addEnum(EnumDataItem di, AlphanumericElementaryDataItem token, BonaTokenizableEnum n) {
if (n == null) {
writeNull(di);
} else {
writeNonNull(di, n.getToken());
}
}
@Override
public void addEnum(XEnumDataItem di, AlphanumericElementaryDataItem token, XEnum<?> n) {
if (n == null) {
writeNull(di);
} else {
writeNonNull(di, n.getToken());
}
}
@Override
public boolean addExternal(ObjectReference di, Object o) {
return false; // perform conversion by default
}
@Override
public void addField(ObjectReference di, Map<String, Object> o) {
JsonObject ob = o == null ? null : new JsonObject(o);
if (inArray)
arr.addObject(ob);
else
obj.putObject(di.getName(), ob);
}
@Override
public void addField(ObjectReference di, List<Object> o) throws RuntimeException {
JsonArray ob = obj == null ? null : new JsonArray(o);
if (inArray)
arr.addArray(ob);
else
obj.putArray(di.getName(), ob);
}
@Override
public void addField(ObjectReference di, Object o) {
if (inArray)
arr.add(o);
else
obj.putValue(di.getName(), o);
}
}