All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datakernel.serializer.GsonSubclassesAdapter Maven / Gradle / Ivy

Go to download

Composable asynchronous/reactive streams with powerful data processing capabilities.

The newest version!
/*
 * Copyright (C) 2015 SoftIndex LLC.
 *
 * 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 io.datakernel.serializer;

import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.gson.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

public final class GsonSubclassesAdapter implements JsonSerializer, JsonDeserializer {
	private final ImmutableBiMap> classTags;
	private final ImmutableMap> classCreators;
	private final ImmutableBiMap> subclassNames;
	private final String subclassField;

	public GsonSubclassesAdapter(Map> classTags,
	                             Map> classCreators,
	                             String subclassField, Map> subclassNames) {
		this.classTags = ImmutableBiMap.copyOf(classTags);
		this.classCreators = ImmutableMap.copyOf(classCreators);
		this.subclassField = subclassField;
		this.subclassNames = ImmutableBiMap.copyOf(subclassNames);
	}

	public static class Builder {
		private final Map> classTags = new HashMap<>();
		private final Map> classCreators = new HashMap<>();
		private final Map> subclassNames = new HashMap<>();
		private String subclassField = "_type";

		public Builder classTag(String classTag, Class type, InstanceCreator instanceCreator) {
			this.classTags.put(classTag, type);
			this.classCreators.put(classTag, instanceCreator);
			return this;
		}

		public Builder classTag(String classTag, Class type) {
			this.classTags.put(classTag, type);
			return this;
		}

		public Builder subclassField(String subclassField) {
			this.subclassField = subclassField;
			return this;
		}

		public Builder subclass(String subclassName, Class subclass) {
			this.subclassNames.put(subclassName, subclass);
			return this;
		}

		public GsonSubclassesAdapter build() {
			return new GsonSubclassesAdapter<>(classTags, classCreators, subclassField, subclassNames);
		}
	}

	public static  Builder builder() {
		return new Builder<>();
	}

	private static Object newInstance(Class type) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
		boolean isStatic = (type.getModifiers() & Modifier.STATIC) != 0;
		Class enclosingClass = type.getEnclosingClass();
		if (isStatic || enclosingClass == null) {
			Constructor ctor = type.getDeclaredConstructor();
			ctor.setAccessible(true);
			return ctor.newInstance();
		}
		Object enclosingInstance = newInstance(enclosingClass);
		Constructor ctor = type.getDeclaredConstructor(enclosingClass);
		ctor.setAccessible(true);
		return ctor.newInstance(enclosingInstance);
	}

	@SuppressWarnings("unchecked")
	@Override
	public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) {
		Class aClass = (Class) src.getClass();
		String classTag = classTags.inverse().get(aClass);
		if (classTag != null) {
			return new JsonPrimitive(classTag);
		}
		String subclassName = subclassNames.inverse().get(aClass);
		if (subclassName != null) {
			JsonObject result = new JsonObject();
			result.addProperty(subclassField, subclassName);
			JsonObject element = (JsonObject) context.serialize(src, src.getClass());
			for (Map.Entry entry : element.entrySet()) {
				result.add(entry.getKey(), entry.getValue());
			}
			return result;
		}
		return new JsonPrimitive(src.getClass().getName());
	}

	@SuppressWarnings("unchecked")
	@Override
	public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
		if (json.isJsonPrimitive()) {
			if (!((JsonPrimitive) json).isString()) {
				throw new JsonParseException("Inner class name is expected");
			}
			String className = json.getAsString();
			InstanceCreator creator = classCreators.get(className);
			if (creator != null) {
				return creator.createInstance(typeOfT);
			}
			try {
				Class aClass = classTags.get(className);
				if (aClass == null) {
					aClass = Class.forName(className);
				}
				return (T) newInstance(aClass);
			} catch (InvocationTargetException | ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
				throw new JsonParseException(e);
			}
		}
		JsonObject object = json.getAsJsonObject();
		String subclassName = object.get(subclassField).getAsString();
		return context.deserialize(json, subclassNames.get(subclassName));
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy