package net.kennux.cubicworld.serialization; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; import net.kennux.cubicworld.serialization.annotations.SerializerField; import net.kennux.cubicworld.util.ConsoleHelper; /** * <pre> * The serializer base class. * This serializer can get used to serialize object whose classes got the @SerializerField annotation on it's members. * </pre> * @author KennuX * */ @SuppressWarnings("rawtypes") public class Serializer { /** * The field used to store serializer field info. * @author KennuX * */ private static class SerializerFieldInfo { public Field field; public SerializationTypes type; public SerializerFieldInfo (Field field, SerializationTypes type) { this.field = field; this.type = type; } } /** * Holds all serializer instances. */ private static HashMap<Class, ISerializer> serializers = new HashMap<Class, ISerializer>(); /** * Generates a serializer just in time and saves it to serializers. * If there is already a serializer, this function will return the existing one. * @param clazz * @return */ private static ISerializer getSerializer(final Class clazz) { // The serializer ISerializer serializer = serializers.get(clazz); // was the serializer already in list? if (serializer == null) { // Get all annotations Field[] fields = clazz.getFields(); // All serializer fields with their order as key HashMap<Integer, Field> serializerFields = new HashMap<Integer, Field>(); ArrayList<Integer> annotationOrders = new ArrayList<Integer>(); // Iterate through all fields for (Field f : fields) { if (f.isAnnotationPresent(SerializerField.class)) { f.setAccessible(true); SerializerField annotation = f.getAnnotation(SerializerField.class); if (annotationOrders.contains(annotation.order())) { ConsoleHelper.writeLog("ERROR", "Duplicate serializer field entry: " + f.getName(), "Serializer"); } serializerFields.put(annotation.order(), f); annotationOrders.add(annotation.order()); } } // Create annotation order list and sort it int[] orders = new int[annotationOrders.size()]; int counter = 0; for (Integer i : annotationOrders) { orders[counter]=i.intValue(); counter++; } Arrays.sort(orders); // Create array for saving serialization info final SerializerFieldInfo[] serializerFieldInfo = new SerializerFieldInfo[orders.length]; // Now iterate through all serializer fields for (int i : orders) { Field field = serializerFields.get(i); SerializerField annotation = field.getAnnotation(SerializerField.class); serializerFieldInfo[i] = new SerializerFieldInfo(field, annotation.type()); } // Create anonymous serializer serializer = new ISerializer() { @Override public void serialize(BitWriter writer, Object object) { // Iterate through every serializer field info for (SerializerFieldInfo fieldInfo : serializerFieldInfo) { try { writer.writeField(fieldInfo.type, fieldInfo.field.get(object)); } catch (IllegalArgumentException | IllegalAccessException e) { ConsoleHelper.writeLog("ERROR", "Error while serializing object of type " + clazz.getName(), "Serializer"); } } } @Override public Object deserialize(BitReader reader) { // Instantiate new object Object newObject = null; try { newObject = clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { ConsoleHelper.writeLog("ERROR", "Error while instantiating object of type " + clazz.getName() + ". No constuctor without parameters?", "Serializer"); return null; } // Iterate through every serializer field info for (SerializerFieldInfo fieldInfo : serializerFieldInfo) { try { fieldInfo.field.set(newObject, reader.readField(fieldInfo.type)); } catch (IllegalArgumentException | IllegalAccessException e) { ConsoleHelper.writeLog("ERROR", "Error while deserializing object of type " + clazz.getName(), "Serializer"); } } return newObject; } }; serializers.put(clazz, serializer); } return serializer; } /** * Serializes the given object. * @param object * @return */ public static <T> void serialize(BitWriter writer, T object) { ISerializer serializer = getSerializer(object.getClass()); serializer.serialize(writer, object); } /** * Serializes the given object. * @param object * @return */ public static <T> T deserialize(BitReader reader, Class<T> typeClass) { ISerializer serializer = getSerializer(typeClass); return (T) serializer.deserialize(reader); } }