package com.faforever.client.preferences.gson; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonNull; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.FloatProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ListProperty; import javafx.beans.property.LongProperty; import javafx.beans.property.MapProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.Property; import javafx.beans.property.SetProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleFloatProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleListProperty; import javafx.beans.property.SimpleLongProperty; import javafx.beans.property.SimpleMapProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleSetProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.value.WritableObjectValue; import javafx.collections.FXCollections; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.Set; public class PropertyTypeAdapter implements JsonSerializer<Property>, JsonDeserializer<Property> { private class CustomType implements ParameterizedType { private final Class<?> rawType; private final Type[] typeArguments; public CustomType(Class<?> rawType, Type[] typeArguments) { this.rawType = rawType; this.typeArguments = typeArguments; } @Override public Type[] getActualTypeArguments() { return typeArguments; } @Override public Type getRawType() { return rawType; } @Override public Type getOwnerType() { return null; } } public static final PropertyTypeAdapter INSTANCE = new PropertyTypeAdapter(); private PropertyTypeAdapter() { } @Override public JsonElement serialize(Property src, Type typeOfSrc, JsonSerializationContext context) { if (src.getValue() == null) { return JsonNull.INSTANCE; } if (src instanceof StringProperty) { return new JsonPrimitive(((StringProperty) src).get()); } if (src instanceof IntegerProperty) { return new JsonPrimitive(((IntegerProperty) src).get()); } if (src instanceof DoubleProperty) { return new JsonPrimitive(((DoubleProperty) src).get()); } if (src instanceof LongProperty) { return new JsonPrimitive(((LongProperty) src).get()); } if (src instanceof FloatProperty) { return new JsonPrimitive(((FloatProperty) src).get()); } if (src instanceof BooleanProperty) { return new JsonPrimitive(((BooleanProperty) src).get()); } if (src instanceof WritableObjectValue) { return context.serialize(((WritableObjectValue) src).get()); } throw new IllegalStateException("Unhandled object type: " + src.getClass()); } @Override public Property deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (typeOfT instanceof Class) { Class clazz = (Class) typeOfT; if (StringProperty.class.isAssignableFrom(clazz)) { return new SimpleStringProperty(json.getAsString()); } if (IntegerProperty.class.isAssignableFrom(clazz)) { return new SimpleIntegerProperty(json.getAsInt()); } if (DoubleProperty.class.isAssignableFrom(clazz)) { return new SimpleDoubleProperty(json.getAsDouble()); } if (LongProperty.class.isAssignableFrom(clazz)) { return new SimpleLongProperty(json.getAsLong()); } if (FloatProperty.class.isAssignableFrom(clazz)) { return new SimpleFloatProperty(json.getAsFloat()); } if (BooleanProperty.class.isAssignableFrom(clazz)) { return new SimpleBooleanProperty(json.getAsBoolean()); } if (SetProperty.class.isAssignableFrom(clazz)) { return new SimpleSetProperty<>(context.deserialize(json, Set.class)); } if (MapProperty.class.isAssignableFrom(clazz)) { return new SimpleSetProperty<>(context.deserialize(json, Map.class)); } } if (typeOfT instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) typeOfT; Type rawType = parameterizedType.getRawType(); if (rawType == ObjectProperty.class) { return new SimpleObjectProperty<>(context.deserialize(json, parameterizedType.getActualTypeArguments()[0])); } else if (rawType == ListProperty.class) { CustomType type = new CustomType(List.class, parameterizedType.getActualTypeArguments()); return new SimpleListProperty<>(FXCollections.observableList(context.deserialize(json, type))); } else if (rawType == SetProperty.class) { CustomType type = new CustomType(Set.class, parameterizedType.getActualTypeArguments()); // Why is this the only call that needs paramaterization? return new SimpleSetProperty<>(FXCollections.observableSet(context.<Set<Object>>deserialize(json, type))); } else if (rawType == MapProperty.class) { CustomType type = new CustomType(Map.class, parameterizedType.getActualTypeArguments()); return new SimpleMapProperty<>(FXCollections.observableMap(context.deserialize(json, type))); } } throw new IllegalStateException("Unhandled object type: " + typeOfT); } }