/* * Copyright 2015 MiLaboratory.com * * 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 com.milaboratory.primitivio; import com.milaboratory.primitivio.annotations.CustomSerializer; import com.milaboratory.primitivio.annotations.Serializable; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Objects; import static com.milaboratory.primitivio.Util.findSerializableParent; public final class SerializersManager { final DefaultSerializersProvider defaultSerializersProvider = new DefaultSerializersProviderImpl(); final HashMap<Class<?>, Serializer> registeredHelpers = new HashMap<>(); public <T> Serializer<? super T> getSerializer(Class<T> type) { Serializer serializer = registeredHelpers.get(type); if (serializer == null) { Class<?> parent = findSerializableParent(type, true, false); serializer = registeredHelpers.get(parent); if (serializer != null) registeredHelpers.put(type, serializer); } if (serializer != null) return serializer; return createAndRegisterSerializer(type); } public void registerCustomSerializer(Class<?> type, Serializer<?> customSerializer) { registeredHelpers.put(type, customSerializer); } private Serializer createAndRegisterSerializer(Class<?> type) { Class<?> root = findRoot(type); Serializer serializer; if (root == null) { serializer = defaultSerializersProvider.createSerializer(type, this); if (serializer == null) throw new RuntimeException("" + type + " is not serializable."); else root = type; } else serializer = createSerializer0(root, false); registeredHelpers.put(root, serializer); if (type != root) registeredHelpers.put(type, serializer); if (serializer instanceof CustomSerializerImpl) for (Class<?> subType : ((CustomSerializerImpl) serializer).infoByClass.keySet()) registeredHelpers.put(subType, serializer); return serializer; } private Serializer createSerializer0(Class<?> type, boolean nested) { Serializable annotation = type.getAnnotation(Serializable.class); if (annotation == null) throw new IllegalArgumentException("" + type + " is not serializable."); Serializer defaultSerializer = annotation.by() == Serializer.class ? null : instantiate(annotation.by()); if (annotation.asJson()) { if (defaultSerializer != null) throw new RuntimeException("'asJson' and 'by' parameters are not compatible."); defaultSerializer = new JSONSerializer(type); } CustomSerializer[] css = annotation.custom(); if (css.length > 0) { if (nested) throw new RuntimeException("Nested custom serializers in " + type + "."); HashMap<Class<?>, CustomSerializerImpl.TypeInfo> infoByClass = new HashMap<>(); // Adding default serializer if (defaultSerializer != null) infoByClass.put(type, new CustomSerializerImpl.TypeInfo((byte) 0, defaultSerializer)); // Adding custom serializers for (CustomSerializer cs : css) infoByClass.put(cs.type(), new CustomSerializerImpl.TypeInfo(cs.id(), createSerializer0(cs.type(), true))); return new CustomSerializerImpl(infoByClass); } else { if (defaultSerializer == null) throw new RuntimeException("No serializer for " + type); return defaultSerializer; } } static Serializer instantiate(Class<? extends Serializer> cl) { boolean initialAccessibility = true; Constructor<? extends Serializer> constructor = null; try { constructor = cl.getConstructor(); initialAccessibility = constructor.isAccessible(); if (!initialAccessibility) constructor.setAccessible(true); return cl.newInstance(); } catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) { throw new RuntimeException(e); } finally { if (!initialAccessibility && constructor != null) constructor.setAccessible(false); } } static Class<?> findRoot(Class<?> type) { List<Class<?>> serializableClasses = getAllSerializableInTree(type); if (serializableClasses.isEmpty()) return null; Class<?> realRoot = findRealRoot(serializableClasses.get(0)), tmp; for (int i = 1; i < serializableClasses.size(); i++) { tmp = findRealRoot(serializableClasses.get(i)); if (!Objects.equals(tmp, realRoot)) throw new IllegalArgumentException("Conflict between " + realRoot + " and " + tmp + " through " + serializableClasses.get(i)); } return realRoot; } /* Utility methods for root calculation */ private static List<Class<?>> getAllSerializableInTree(Class<?> type) { List<Class<?>> list = new ArrayList<>(); addAllSerializableInTree(type, list); return list; } private static void addAllSerializableInTree(Class<?> type, List<Class<?>> list) { if (type.getAnnotation(Serializable.class) != null) list.add(type); Class<?> superclass = type.getSuperclass(); if (superclass != null) addAllSerializableInTree(superclass, list); for (Class<?> cInterface : type.getInterfaces()) addAllSerializableInTree(cInterface, list); } private static Class<?> findRealRoot(Class<?> type) { Class<?> tmp = findSerializableParent(type, false, true); if (tmp != null) return tmp; tmp = findSerializableParent(type, false, false); return tmp; } }