Merge PR #3403 by @eviltak - serialization work
commit
639752cd95
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2018 MovingBlocks
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.terasology.persistence.typeHandling.gson;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.terasology.persistence.typeHandling.extensionTypes.ColorTypeHandler;
|
||||
import org.terasology.rendering.nui.Color;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class GsonTypeHandlerAdapterTest {
|
||||
private static final String OBJECT_JSON_ARRAY = "{\"color\":[222,173,190,239],\"i\":-123}";
|
||||
private static final String OBJECT_JSON_HEX = "{\"color\":DEADBEEF,\"i\":-123}";
|
||||
private static final TestClass OBJECT = new TestClass(new Color(0xDEADBEEF), -123);
|
||||
|
||||
private final Gson gson = GsonFactory.createGsonWithTypeHandlers(
|
||||
TypeHandlerEntry.of(Color.class, new ColorTypeHandler())
|
||||
);
|
||||
|
||||
/**
|
||||
* {@link GsonTypeHandlerAdapter#read(JsonReader)} is tested by deserializing an object from JSON
|
||||
* via Gson with a registered {@link GsonTypeHandlerAdapterFactory} which creates instances of
|
||||
* {@link GsonTypeHandlerAdapter}.
|
||||
*/
|
||||
@Test
|
||||
public void testRead() {
|
||||
// Deserialize object with color as JSON array
|
||||
TestClass deserializedObject = gson.fromJson(OBJECT_JSON_ARRAY, TestClass.class);
|
||||
|
||||
Assert.assertEquals(OBJECT, deserializedObject);
|
||||
|
||||
// Deserialize object with color as hex string
|
||||
deserializedObject = gson.fromJson(OBJECT_JSON_HEX, TestClass.class);
|
||||
|
||||
Assert.assertEquals(OBJECT, deserializedObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link GsonTypeHandlerAdapter#write(JsonWriter, Object)} is tested by serializing an object to JSON
|
||||
* via Gson with a registered {@link GsonTypeHandlerAdapterFactory} which creates instances of
|
||||
* {@link GsonTypeHandlerAdapter}.
|
||||
*/
|
||||
@Test
|
||||
public void testWrite() {
|
||||
String serializedObject = gson.toJson(OBJECT);
|
||||
|
||||
Assert.assertEquals(OBJECT_JSON_ARRAY, serializedObject);
|
||||
}
|
||||
|
||||
private static class TestClass {
|
||||
private final Color color;
|
||||
private final int i;
|
||||
|
||||
private TestClass(Color color, int i) {
|
||||
this.color = color;
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TestClass testClass = (TestClass) o;
|
||||
return i == testClass.i &&
|
||||
Objects.equals(color, testClass.color);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright 2017 MovingBlocks
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.terasology.persistence.typeHandling.gson;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.Gson;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.terasology.math.geom.Rect2i;
|
||||
import org.terasology.math.geom.Vector4f;
|
||||
import org.terasology.persistence.typeHandling.TypeSerializationLibrary;
|
||||
import org.terasology.reflection.copy.CopyStrategyLibrary;
|
||||
import org.terasology.reflection.reflect.ReflectionReflectFactory;
|
||||
import org.terasology.rendering.nui.Color;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class GsonTypeSerializationLibraryAdapterFactoryTest {
|
||||
private static final TestClass OBJECT = new TestClass(
|
||||
new Color(0xDEADBEEF),
|
||||
ImmutableSet.of(Vector4f.zero(), Vector4f.one()),
|
||||
ImmutableMap.of(
|
||||
"someRect",
|
||||
Rect2i.createFromMinAndSize(-3, -3, 10, 10)
|
||||
),
|
||||
ImmutableMap.of(0, 1, 1, 0),
|
||||
-0xDECAF
|
||||
);
|
||||
|
||||
private static final String OBJECT_JSON = "{\"color\":[222,173,190,239],\"vector4fs\":[[0.0,0.0,0.0,0.0]," +
|
||||
"[1.0,1.0,1.0,1.0]],\"rect2iMap\":{\"someRect\":{\"min\":[-3,-3],\"size\":[10,10]}},\"i\":-912559}";
|
||||
|
||||
private final ReflectionReflectFactory reflectFactory = new ReflectionReflectFactory();
|
||||
private final CopyStrategyLibrary copyStrategyLibrary = new CopyStrategyLibrary(reflectFactory);
|
||||
private final TypeSerializationLibrary typeSerializationLibrary =
|
||||
TypeSerializationLibrary.createDefaultLibrary(reflectFactory, copyStrategyLibrary);
|
||||
|
||||
private final Gson gson = GsonFactory.createGsonWithTypeSerializationLibrary(typeSerializationLibrary);
|
||||
|
||||
@Test
|
||||
public void testSerialize() {
|
||||
String serializedObject = gson.toJson(OBJECT);
|
||||
|
||||
Assert.assertEquals(OBJECT_JSON, serializedObject);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeserialize() {
|
||||
TestClass deserializedObject = gson.fromJson(OBJECT_JSON, TestClass.class);
|
||||
|
||||
Assert.assertEquals(OBJECT, deserializedObject);
|
||||
}
|
||||
|
||||
private static class TestClass {
|
||||
private final Color color;
|
||||
private final Set<Vector4f> vector4fs;
|
||||
private final Map<String, Rect2i> rect2iMap;
|
||||
|
||||
// Will not be serialized
|
||||
private final Map<Integer, Integer> intMap;
|
||||
|
||||
private final int i;
|
||||
|
||||
private TestClass(Color color, Set<Vector4f> vector4fs, Map<String, Rect2i> rect2iMap,
|
||||
Map<Integer, Integer> intMap, int i) {
|
||||
this.color = color;
|
||||
this.vector4fs = vector4fs;
|
||||
this.rect2iMap = rect2iMap;
|
||||
this.intMap = intMap;
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TestClass testClass = (TestClass) o;
|
||||
return i == testClass.i &&
|
||||
Objects.equals(color, testClass.color) &&
|
||||
Objects.equals(vector4fs, testClass.vector4fs) &&
|
||||
Objects.equals(rect2iMap, testClass.rect2iMap);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2018 MovingBlocks
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.terasology.persistence.typeHandling.gson;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import org.terasology.persistence.typeHandling.TypeSerializationLibrary;
|
||||
|
||||
/**
|
||||
* Class containing static factory methods for generating {@link Gson} objects that follow Terasology
|
||||
* serialization rules and support Terasology TypeHandlers.
|
||||
*/
|
||||
public class GsonFactory {
|
||||
/**
|
||||
* Create a {@link GsonBuilder} with options set to comply with Terasology JSON serialization rules.
|
||||
*/
|
||||
public static GsonBuilder createDefaultGsonBuilder() {
|
||||
return new GsonBuilder()
|
||||
.setExclusionStrategies(new GsonMapExclusionStrategy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Gson} object which uses type handlers loaded from the given
|
||||
* {@link TypeSerializationLibrary} and complies with Terasology JSON serialization rules.
|
||||
*
|
||||
* @param typeSerializationLibrary The {@link TypeSerializationLibrary} to load type handler
|
||||
* definitions from
|
||||
*/
|
||||
public static Gson createGsonWithTypeSerializationLibrary(TypeSerializationLibrary typeSerializationLibrary) {
|
||||
TypeAdapterFactory typeAdapterFactory =
|
||||
new GsonTypeSerializationLibraryAdapterFactory(typeSerializationLibrary);
|
||||
|
||||
return createDefaultGsonBuilder()
|
||||
.registerTypeAdapterFactory(typeAdapterFactory)
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Gson} object which uses the given type handlers and complies with Terasology
|
||||
* JSON serialization rules.
|
||||
*
|
||||
* @param typeHandlerEntries The type handlers to use during serialization.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Gson createGsonWithTypeHandlers(TypeHandlerEntry<?>... typeHandlerEntries) {
|
||||
GsonTypeHandlerAdapterFactory typeAdapterFactory = new GsonTypeHandlerAdapterFactory();
|
||||
|
||||
for (TypeHandlerEntry typeHandlerEntry : typeHandlerEntries) {
|
||||
typeAdapterFactory.addTypeHandler(typeHandlerEntry);
|
||||
}
|
||||
|
||||
return createDefaultGsonBuilder()
|
||||
.registerTypeAdapterFactory(typeAdapterFactory)
|
||||
.create();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2017 MovingBlocks
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.terasology.persistence.typeHandling.gson;
|
||||
|
||||
import com.google.gson.ExclusionStrategy;
|
||||
import com.google.gson.FieldAttributes;
|
||||
import org.terasology.utilities.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
public class GsonMapExclusionStrategy implements ExclusionStrategy {
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes f) {
|
||||
Type fieldType = f.getDeclaredType();
|
||||
Class<?> fieldClass = ReflectionUtil.getClassOfType(fieldType);
|
||||
|
||||
if (Map.class.isAssignableFrom(fieldClass)) {
|
||||
Type mapKeyType = ReflectionUtil.getTypeParameter(fieldType, 0);
|
||||
|
||||
return String.class != mapKeyType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2018 MovingBlocks
|
||||
* Copyright (C) 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* GsonTypeHandlerAdapter is a less featureful clone of the package-private
|
||||
* class TreeTypeAdapter in Gson 2.6.2.
|
||||
*/
|
||||
package org.terasology.persistence.typeHandling.gson;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import org.terasology.persistence.typeHandling.TypeHandler;
|
||||
import org.terasology.utilities.ReflectionUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Adapts a {@link TypeHandler} as a Gson {@link TypeAdapter}. Instances of {@link GsonTypeHandlerAdapter},
|
||||
* when registered as type adapters in a {@link Gson} object, can be used to (de)serialize objects
|
||||
* to JSON (via Gson) with the rules specified by the {@link GsonTypeHandlerAdapter#typeHandler}.
|
||||
*
|
||||
* Since instances of {@link GsonTypeHandlerAdapter} require a {@link Gson} object and a
|
||||
* {@link TypeToken}, it is recommended to register {@link GsonTypeHandlerAdapter} type adapters as a
|
||||
* type adapter factory via a {@link com.google.gson.TypeAdapterFactory} like
|
||||
* {@link GsonTypeHandlerAdapterFactory}.
|
||||
*/
|
||||
public final class GsonTypeHandlerAdapter<T> extends TypeAdapter<T> {
|
||||
|
||||
private final TypeHandler<T> typeHandler;
|
||||
private final JsonSerializer<T> serializer;
|
||||
private final JsonDeserializer<T> deserializer;
|
||||
private final Gson gson;
|
||||
private final TypeToken<T> typeToken;
|
||||
|
||||
GsonTypeHandlerAdapter(TypeHandler<T> typeHandler,
|
||||
Gson gson, TypeToken<T> typeToken) {
|
||||
this.typeHandler = typeHandler;
|
||||
|
||||
this.serializer = (src, typeOfSrc, context) ->
|
||||
((GsonPersistedData) typeHandler.serialize(src, new GsonSerializationContext(context)))
|
||||
.getElement();
|
||||
|
||||
this.deserializer = (json, typeOfT, context) ->
|
||||
typeHandler.deserialize(new GsonPersistedData(json), new GsonDeserializationContext(context));
|
||||
|
||||
this.gson = gson;
|
||||
this.typeToken = typeToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read(JsonReader in) throws IOException {
|
||||
JsonElement value = Streams.parse(in);
|
||||
if (value.isJsonNull()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return deserializer.deserialize(value, typeToken.getType(), (JsonDeserializationContext) ReflectionUtil.readField(gson, "deserializationContext"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, T value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
JsonElement tree = serializer.serialize(value, typeToken.getType(), (JsonSerializationContext) ReflectionUtil.readField(gson, "serializationContext"));
|
||||
Streams.write(tree, out);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2018 MovingBlocks
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.terasology.persistence.typeHandling.gson;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.terasology.persistence.typeHandling.TypeHandler;
|
||||
import org.terasology.persistence.typeHandling.TypeSerializationLibrary;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A Gson {@link TypeAdapterFactory} that creates a {@link GsonTypeHandlerAdapter} for each
|
||||
* {@link TypeHandler} registered in the {@link #typeHandlerMap}.
|
||||
*/
|
||||
public class GsonTypeHandlerAdapterFactory implements TypeAdapterFactory {
|
||||
private Map<Class<?>, TypeHandler<?>> typeHandlerMap;
|
||||
|
||||
public GsonTypeHandlerAdapterFactory() {
|
||||
typeHandlerMap = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link TypeHandler} to the {@link #typeHandlerMap} for the given type.
|
||||
*
|
||||
* @param typeHandlerEntry The {@link TypeHandlerEntry} encapsulating the {@link TypeHandler} for
|
||||
* the given type.
|
||||
*/
|
||||
public <T> void addTypeHandler(TypeHandlerEntry<T> typeHandlerEntry) {
|
||||
addTypeHandler(typeHandlerEntry.type, typeHandlerEntry.typeHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link TypeHandler} to the {@link #typeHandlerMap} for the given type.
|
||||
* @param type The {@link Class} of the type.
|
||||
* @param typeHandler The {@link TypeHandler} for the type.
|
||||
*/
|
||||
public <T> void addTypeHandler(Class<T> type, TypeHandler<T> typeHandler) {
|
||||
typeHandlerMap.put(type, typeHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean stating whether the {@link #typeHandlerMap} contains a type handler for the given type.
|
||||
* @param type The {@link Class} of the given type.
|
||||
*/
|
||||
public boolean containsTypeHandlerFor(Class<?> type) {
|
||||
return typeHandlerMap.containsKey(type);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||
Class<? super T> rawType = type.getRawType();
|
||||
|
||||
if (!containsTypeHandlerFor(rawType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new GsonTypeHandlerAdapter<>((TypeHandler<T>) typeHandlerMap.get(rawType), gson, type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2018 MovingBlocks
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.terasology.persistence.typeHandling.gson;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.terasology.persistence.typeHandling.TypeHandler;
|
||||
import org.terasology.persistence.typeHandling.TypeSerializationLibrary;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* A Gson {@link TypeAdapterFactory} that dynamically looks up the {@link TypeHandler} from a
|
||||
* {@link TypeSerializationLibrary} for each type encountered, and creates a {@link GsonTypeHandlerAdapter} with
|
||||
* the retrieved {@link TypeHandler}.
|
||||
*/
|
||||
public class GsonTypeSerializationLibraryAdapterFactory implements TypeAdapterFactory {
|
||||
private final TypeSerializationLibrary typeSerializationLibrary;
|
||||
|
||||
public GsonTypeSerializationLibraryAdapterFactory(TypeSerializationLibrary typeSerializationLibrary) {
|
||||
this.typeSerializationLibrary = typeSerializationLibrary;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||
Type rawType = type.getType();
|
||||
|
||||
TypeHandler<T> typeHandler = (TypeHandler<T>) typeSerializationLibrary.getHandlerFor(rawType);
|
||||
|
||||
if (typeHandler == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new GsonTypeHandlerAdapter<>(typeHandler, gson, type);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.terasology.persistence.typeHandling.gson;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
|
@ -26,12 +27,16 @@ import org.terasology.persistence.typeHandling.TypeHandler;
|
|||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Adapts a {@link TypeHandler} as a legacy Gson {@link JsonSerializer} and {@link JsonDeserializer}.
|
||||
* Instances of {@link LegacyGsonTypeHandlerAdapter}, when registered as type adapters in a {@link Gson}
|
||||
* object, can be used to (de)serialize objects to JSON (via Gson) with the rules specified by
|
||||
* the {@link #typeHandler}.
|
||||
*/
|
||||
public class JsonTypeHandlerAdapter<T> implements JsonDeserializer<T>, JsonSerializer<T> {
|
||||
public class LegacyGsonTypeHandlerAdapter<T> implements JsonDeserializer<T>, JsonSerializer<T> {
|
||||
|
||||
private TypeHandler<T> typeHandler;
|
||||
|
||||
public JsonTypeHandlerAdapter(TypeHandler<T> handler) {
|
||||
public LegacyGsonTypeHandlerAdapter(TypeHandler<T> handler) {
|
||||
this.typeHandler = handler;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2018 MovingBlocks
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.terasology.persistence.typeHandling.gson;
|
||||
|
||||
import org.terasology.persistence.typeHandling.TypeHandler;
|
||||
|
||||
/**
|
||||
* A class containing a {@link TypeHandler} and the {@link Class} of the type it handles.
|
||||
*
|
||||
* @param <T> The type handled by the {@link TypeHandler} contained in the {@link TypeHandlerEntry}.
|
||||
*/
|
||||
public class TypeHandlerEntry<T> {
|
||||
public final Class<T> type;
|
||||
public final TypeHandler<T> typeHandler;
|
||||
|
||||
private TypeHandlerEntry(Class<T> type, TypeHandler<T> typeHandler) {
|
||||
this.type = type;
|
||||
this.typeHandler = typeHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link TypeHandlerEntry} for a {@link TypeHandler} of the type {@link U}.
|
||||
*/
|
||||
public static <U> TypeHandlerEntry<U> of(Class<U> type, TypeHandler<U> typeHandler) {
|
||||
return new TypeHandlerEntry<>(type, typeHandler);
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ import org.terasology.math.Border;
|
|||
import org.terasology.persistence.ModuleContext;
|
||||
import org.terasology.persistence.typeHandling.TypeSerializationLibrary;
|
||||
import org.terasology.persistence.typeHandling.extensionTypes.AssetTypeHandler;
|
||||
import org.terasology.persistence.typeHandling.gson.JsonTypeHandlerAdapter;
|
||||
import org.terasology.persistence.typeHandling.gson.LegacyGsonTypeHandlerAdapter;
|
||||
import org.terasology.persistence.typeHandling.mathTypes.BorderTypeHandler;
|
||||
import org.terasology.reflection.metadata.ClassMetadata;
|
||||
import org.terasology.reflection.metadata.FieldMetadata;
|
||||
|
@ -106,7 +106,7 @@ public class UIFormat extends AbstractAssetFileFormat<UIData> {
|
|||
.registerTypeAdapter(UIData.class, new UIDataTypeAdapter())
|
||||
.registerTypeHierarchyAdapter(UIWidget.class, new UIWidgetTypeAdapter(nuiManager));
|
||||
for (Class<?> handledType : library.getCoreTypes()) {
|
||||
gsonBuilder.registerTypeAdapter(handledType, new JsonTypeHandlerAdapter<>(library.getHandlerFor(handledType)));
|
||||
gsonBuilder.registerTypeAdapter(handledType, new LegacyGsonTypeHandlerAdapter<>(library.getHandlerFor(handledType)));
|
||||
}
|
||||
|
||||
// override the String TypeAdapter from the serialization library
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.terasology.assets.format.AssetDataFile;
|
|||
import org.terasology.assets.module.annotations.RegisterAssetFileFormat;
|
||||
import org.terasology.persistence.ModuleContext;
|
||||
import org.terasology.persistence.typeHandling.extensionTypes.ColorTypeHandler;
|
||||
import org.terasology.persistence.typeHandling.gson.JsonTypeHandlerAdapter;
|
||||
import org.terasology.persistence.typeHandling.gson.LegacyGsonTypeHandlerAdapter;
|
||||
import org.terasology.reflection.metadata.ClassLibrary;
|
||||
import org.terasology.reflection.metadata.ClassMetadata;
|
||||
import org.terasology.registry.CoreRegistry;
|
||||
|
@ -66,7 +66,7 @@ public class UISkinFormat extends AbstractAssetFileFormat<UISkinData> {
|
|||
.registerTypeAdapter(Font.class, new AssetTypeAdapter<>(Font.class))
|
||||
.registerTypeAdapter(UISkinData.class, new UISkinTypeAdapter())
|
||||
.registerTypeAdapter(TextureRegion.class, new TextureRegionTypeAdapter())
|
||||
.registerTypeAdapter(Color.class, new JsonTypeHandlerAdapter<>(new ColorTypeHandler()))
|
||||
.registerTypeAdapter(Color.class, new LegacyGsonTypeHandlerAdapter<>(new ColorTypeHandler()))
|
||||
.registerTypeAdapter(Optional.class, new OptionalTextureRegionTypeAdapter())
|
||||
.create();
|
||||
}
|
||||
|
|
|
@ -199,4 +199,21 @@ public final class ReflectionUtil {
|
|||
return getTypeParameterForSuperInterface(targetClass.getGenericSuperclass(), superClass, index);
|
||||
}
|
||||
|
||||
public static Object readField(Object object, String fieldName) {
|
||||
Class<?> cls = object.getClass();
|
||||
for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
|
||||
try {
|
||||
final Field field = c.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(object);
|
||||
} catch (final NoSuchFieldException e) {
|
||||
// Try parent
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot access field " + cls.getName() + "." + fieldName, e);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot find field " + cls.getName() + "." + fieldName);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue