package org.terasology.entitySystem.persistence;
import com.google.gson.*;
import com.google.protobuf.ByteString;
import com.google.protobuf.Descriptors;
import gnu.trove.list.TByteList;
import gnu.trove.list.array.TByteArrayList;
import org.terasology.protobuf.EntityData;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Locale;
import java.util.Map;
/**
* @author Immortius <immortius@gmail.com>
*/
public class EntityDataJSONFormat {
public static void write(EntityData.World world, BufferedWriter writer) throws IOException {
newGson().toJson(world, writer);
}
public static void write(EntityData.Prefab prefab, BufferedWriter writer) throws IOException {
newGson().toJson(prefab, writer);
}
public static EntityData.World readWorld(BufferedReader reader) throws IOException {
try {
return newGson().fromJson(reader, EntityData.World.class);
} catch (JsonSyntaxException e) {
throw new IOException("Failed to load world", e);
}
}
public static EntityData.Prefab readPrefab(BufferedReader reader) throws IOException {
try {
return newGson().fromJson(reader, EntityData.Prefab.class);
} catch (JsonSyntaxException e) {
throw new IOException("Failed to load prefab", e);
}
}
private static Gson newGson() {
return new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(EntityData.World.class, new WorldHandler())
.registerTypeAdapter(EntityData.Entity.class, new EntityHandler())
.registerTypeAdapter(EntityData.Prefab.class, new PrefabHandler())
.registerTypeAdapter(EntityData.Component.class, new ComponentHandler())
.registerTypeAdapter(EntityData.Component.Builder.class, new ComponentBuilderHandler())
.registerTypeAdapter(EntityData.Value.class, new ValueHandler())
.create();
}
private static class WorldHandler implements JsonSerializer<EntityData.World>, JsonDeserializer<EntityData.World> {
@Override
public JsonElement serialize(EntityData.World src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
for (Map.Entry<Descriptors.FieldDescriptor, Object> field : src.getAllFields().entrySet()) {
result.add(field.getKey().getName(), context.serialize(field.getValue()));
}
return result;
}
@Override
public EntityData.World deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
EntityData.World.Builder world = EntityData.World.newBuilder();
if (json.isJsonObject()) {
JsonObject jsonObject = json.getAsJsonObject();
JsonArray prefabArray = jsonObject.getAsJsonArray("prefab");
if (prefabArray != null) {
for (JsonElement prefabElem : prefabArray) {
world.addPrefab((EntityData.Prefab) context.deserialize(prefabElem, EntityData.Prefab.class));
}
}
JsonArray entityArray = jsonObject.getAsJsonArray("entity");
if (entityArray != null) {
for (JsonElement entityElem : entityArray) {
world.addEntity((EntityData.Entity) context.deserialize(entityElem, EntityData.Entity.class));
}
}
JsonPrimitive nextId = jsonObject.getAsJsonPrimitive("next_entity_id");
if (nextId != null) {
world.setNextEntityId(nextId.getAsInt());
}
JsonArray freedIdArray = jsonObject.getAsJsonArray("freed_entity_id");
if (freedIdArray != null) {
for (JsonElement freedId : freedIdArray) {
world.addFreedEntityId(freedId.getAsInt());
}
}
}
return world.build();
}
}
private static class ComponentHandler implements JsonSerializer<EntityData.Component> {
@Override
public JsonElement serialize(EntityData.Component src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
for (EntityData.NameValue field : src.getFieldList()) {
result.add(field.getName(), context.serialize(field.getValue()));
}
return result;
}
}
private static class ComponentBuilderHandler implements JsonDeserializer<EntityData.Component.Builder> {
@Override
public EntityData.Component.Builder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
EntityData.Component.Builder component = EntityData.Component.newBuilder();
JsonObject jsonObject = json.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
EntityData.NameValue.Builder nameValue = EntityData.NameValue.newBuilder();
nameValue.setName(entry.getKey());
EntityData.Value value = (EntityData.Value) context.deserialize(entry.getValue(), EntityData.Value.class);
nameValue.setValue(value);
component.addField(nameValue);
}
return component;
}
}
private static class EntityHandler implements JsonSerializer<EntityData.Entity>, JsonDeserializer<EntityData.Entity> {
@Override
public JsonElement serialize(EntityData.Entity src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
if (src.hasId()) {
result.addProperty("id", src.getId());
}
if (src.hasParentPrefab() && !src.getParentPrefab().isEmpty()) {
result.addProperty("parentPrefab", src.getParentPrefab());
}
for (EntityData.Component component : src.getComponentList()) {
result.add(component.getType(), context.serialize(component));
}
if (src.getRemovedComponentCount() > 0) {
JsonArray removedComponentArray = new JsonArray();
for (String removedComponent : src.getRemovedComponentList()) {
removedComponentArray.add(new JsonPrimitive(removedComponent));
}
result.add("removedComponent", removedComponentArray);
}
return result;
}
@Override
public EntityData.Entity deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
EntityData.Entity.Builder entity = EntityData.Entity.newBuilder();
JsonObject jsonObject = json.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
String name = entry.getKey().toLowerCase(Locale.ENGLISH);
// JAVA7: Make this a switch statement
if (name.equals("parentprefab")) {
if (entry.getValue().isJsonPrimitive()) {
entity.setParentPrefab(entry.getValue().getAsString());
}
} else if (name.equals("id")) {
if (entry.getValue().isJsonPrimitive()) {
entity.setId(entry.getValue().getAsInt());
}
} else if (name.equals("removedcomponent")) {
if (entry.getValue().isJsonArray()) {
for (JsonElement element : entry.getValue().getAsJsonArray()) {
entity.addRemovedComponent(element.getAsString());
}
}
} else {
EntityData.Component.Builder component = context.deserialize(entry.getValue(), EntityData.Component.Builder.class);
component.setType(entry.getKey());
entity.addComponent(component);
}
}
return entity.build();
}
}
private static class PrefabHandler implements JsonSerializer<EntityData.Prefab>, JsonDeserializer<EntityData.Prefab> {
@Override
public JsonElement serialize(EntityData.Prefab src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
if (src.hasName()) {
result.addProperty("name", src.getName());
}
if (src.getParentNameCount() > 0) {
result.add("parent", context.serialize(src.getParentNameList()));
}
for (EntityData.Component component : src.getComponentList()) {
result.add(component.getType(), context.serialize(component));
}
return result;
}
@Override
public EntityData.Prefab deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
EntityData.Prefab.Builder prefab = EntityData.Prefab.newBuilder();
JsonObject jsonObject = json.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
String name = entry.getKey().toLowerCase(Locale.ENGLISH);
// JAVA7: Make this a switch statement
if (name.equals("name")) {
if (entry.getValue().isJsonPrimitive())
prefab.setName(entry.getValue().getAsString());
} else if (name.equals("parent")) {
if (entry.getValue().isJsonPrimitive()) {
prefab.addParentName(entry.getValue().getAsString());
} else if (entry.getValue().isJsonArray()) {
for (JsonElement element : entry.getValue().getAsJsonArray()) {
prefab.addParentName(element.getAsString());
}
}
} else {
EntityData.Component.Builder component = context.deserialize(entry.getValue(), EntityData.Component.Builder.class);
component.setType(entry.getKey());
prefab.addComponent(component);
}
}
return prefab.build();
}
}
private static class ValueHandler implements JsonSerializer<EntityData.Value>, JsonDeserializer<EntityData.Value> {
@Override
public JsonElement serialize(EntityData.Value src, Type typeOfSrc, JsonSerializationContext context) {
if (src.getBooleanCount() > 1) {
return context.serialize(src.getBooleanList());
} else if (src.getBooleanCount() == 1) {
return context.serialize(src.getBoolean(0));
} else if (src.getDoubleCount() > 1) {
return context.serialize(src.getDoubleList());
} else if (src.getDoubleCount() == 1) {
return context.serialize(src.getDouble(0));
} else if (src.getFloatCount() > 1) {
return context.serialize(src.getFloatList());
} else if (src.getFloatCount() == 1) {
return context.serialize(src.getFloat(0));
} else if (src.getIntegerCount() > 1) {
return context.serialize(src.getIntegerList());
} else if (src.getIntegerCount() == 1) {
return context.serialize(src.getInteger(0));
} else if (src.getLongCount() > 1) {
return context.serialize(src.getLongList());
} else if (src.getLongCount() == 1) {
return context.serialize(src.getLong(0));
} else if (src.getStringCount() > 1) {
return context.serialize(src.getStringList());
} else if (src.getStringCount() == 1) {
return context.serialize(src.getString(0));
} else if (src.getValueCount() > 0) {
return context.serialize(src.getValueList());
} else if (src.hasBytes()) {
return context.serialize(src.getBytes().toByteArray());
} else if (src.getNameValueCount() > 0) {
JsonObject obj = new JsonObject();
for (EntityData.NameValue nameValue : src.getNameValueList()) {
obj.add(nameValue.getName(), context.serialize(nameValue.getValue()));
}
return obj;
}
return JsonNull.INSTANCE;
}
@Override
public EntityData.Value deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
EntityData.Value.Builder value = EntityData.Value.newBuilder();
if (json.isJsonPrimitive()) {
extractPrimitive(value, json);
} else if (json.isJsonObject()) {
extractMap(json, context, value);
} else if (json.isJsonArray()) {
JsonArray jsonArray = json.getAsJsonArray();
TByteList byteList = new TByteArrayList();
for (JsonElement element : jsonArray) {
if (element.isJsonArray()) {
value.addValue((EntityData.Value) context.deserialize(element, EntityData.Value.class));
} else if (json.isJsonObject()) {
extractMap(json, context, value);
} else if (element.isJsonPrimitive()) {
extractPrimitive(value, element);
if (element.getAsJsonPrimitive().isNumber()) {
try {
byteList.add(element.getAsByte());
} catch (NumberFormatException nfe) {
}
}
}
}
value.setBytes(ByteString.copyFrom(byteList.toArray()));
}
return value.build();
}
private void extractMap(JsonElement json, JsonDeserializationContext context, EntityData.Value.Builder value) {
JsonObject nameValueObject = json.getAsJsonObject();
for (Map.Entry<String, JsonElement> nameValue : nameValueObject.entrySet()) {
EntityData.Value innerValue = (EntityData.Value) context.deserialize(nameValue.getValue(), EntityData.Value.class);
value.addNameValue(EntityData.NameValue.newBuilder().setName(nameValue.getKey()).setValue(innerValue));
}
}
private void extractPrimitive(EntityData.Value.Builder value, JsonElement element) {
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (primitive.isNumber()) {
value.addDouble(primitive.getAsDouble());
value.addFloat(primitive.getAsFloat());
try {
value.addLong(primitive.getAsLong());
value.addInteger(primitive.getAsInt());
} catch (NumberFormatException e) {
}
}
if (primitive.isBoolean()) {
value.addBoolean(primitive.getAsBoolean());
}
if (primitive.isString()) {
value.addString(primitive.getAsString());
}
}
}
}