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

io.github.cubedtear.jcubit.bds.BDSUtil Maven / Gradle / Ivy

package io.github.cubedtear.jcubit.bds;

import com.google.common.base.Strings;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import io.github.cubedtear.jcubit.util.API;
import io.github.cubedtear.jcubit.util.Nullable;
import io.github.cubedtear.jcubit.util.ReflectionUtil;

import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * Collection of utility methods using BDS.
 *
 * @author Aritz Lopez
 * @see BDS
 */
public class BDSUtil {

    private static final String CLASS_NAME_TAG = "0__className";
    private static final String ARRAY_TYPE_TAG = "0__arrayType";
    private static final String ARRAY_LENGTH_TAG = "0__arrayLength";
    private static final String PRIMITIVE_VALUE_TAG = "0__value";
    private static final String ROOT_OBJ_TAG = "0__root";
    private static final String BACKREF_TAG = "0__back_";
    private static final String IDX_TAG = "0__ID";
    private static final String NULL_TAG = "0__null__";
    private static final String PRIMITIVE_ARRAY_VALUE_TAG = "0__primVal";
    private static final String DATE_VALUE_TAG = "0__date__";

    private static final Map serializers = Maps.newHashMap();

    /**
     * Registers a serializer to be used when (de)serializing the specified type.
     *
     * @param type       The type that has to be serialized by the given serializer.
     * @param serializer The serializer.
     */
    @API
    public static void registerSerializer(Class type, Serializer serializer) {
        serializers.put(type.getName(), serializer);
    }

    /**
     * Serializes the given object into a BDS.
     * 

* The following types have a special treatment: *

    *
  • Primitive types
  • *
  • Wrapper types
  • *
  • {@link Date java.util.Date}
  • *
  • {@link File java.io.File}
  • *
  • All subclasses of {@link Collection java.util.Collection<E>} (See below)
  • *
*

* All other types will be serialized by writing each field (private fields included), provided they are not * annotated with {@link Transient @Transient}. *

* In order for subclasses of collections to serialize correctly with the default serialization, the class implementing the * collection must have a public no-argument constructor. Moreover, if the order is important, the collection must implement * the {@link List List<E>} interface. In case this is not possible or it does not work, a custom serialization must be * provided. *

* When a type has to be specially serialized, both to minimize the resulting BDS size (because maybe some fields are redundant) * or because the default serialization does not work, a {@link Serializer} must be written for the type, and registered through * {@link BDSUtil#registerSerializer(Class, Serializer)}. *

* Note: If this method is run from a {@link Serializer}, the usage of the method {@link BDSUtil#serialize(Object, BackrefFixer)} is highly recommended * to reduce the size of the result, if there are any cycles in the references. * * @param instance The object to serialize. * @return The BDS version of the given object. * @throws SerializationException If an exception occurs when serializing (e.g. There is a cycle that cannot be solved, or a field cannot be accessed). * @see BDSUtil#serialize(Object, BackrefFixer) * @see BDSUtil#registerSerializer(Class, Serializer) */ @API public static BDS serialize(Object instance) throws SerializationException { return serialize(instance, null); } /** * Serializes the given object into a BDS. This method may only be run from a {@link Serializer}. * Uses the given {@link BackrefFixer} to fix future cycles. Useful when deserializing a custom object requires deserializing * another child object. *

* For a full explanation of how serialization works, see {@link BDSUtil#serialize(Object)} * * @param instance The object to serialize. * @param fixer Reference used internally to fix past references. * @return The BDS version of the given object. * @throws SerializationException If an exception occurs when serializing (e.g. there is a cycle that cannot be solved, or a field cannot be accessed). * @see BDSUtil#serialize(Object) */ @API public static BDS serialize(Object instance, @Nullable BackrefFixer fixer) throws SerializationException { if (instance == null) { BDS result = new BDS(NULL_TAG); result.addString(CLASS_NAME_TAG, NULL_TAG); return result; } else return serializeInternal2(ROOT_OBJ_TAG, instance, fixer != null ? fixer.alreadyWritten : HashMultimap., Map.Entry>create(), fixer != null ? fixer.biggest : new IntRef(-1)); } /** * Deserializes an object serialized with {@link BDSUtil#serialize(Object)}. * * @param data The serialized object. * @return The deserialized object. * @throws SerializationException If an exception occurs when deserializing (e.g. there is a cycle that cannot be solved, or a field cannot be accessed). */ @Nullable @API public static Object deserialize(BDS data) throws SerializationException { try { String oldName = data.getName(); data.setName(ROOT_OBJ_TAG); Object o = deserializeInternal(data, Maps.newHashMap(), Sets.newHashSet()); data.setName(oldName); return o; } catch (CannotDeserializeYet cannotDeserializeYet) { throw new AssertionError("Should never happen"); } } /** * Checks whether the given BDS corresponds to what this class uses to represent null values. * * @param bds The BDS to check if is the null BDS. * @return {@code true} if it is the null BDS. */ public static boolean isNull(BDS bds) { return bds == null || NULL_TAG.equals(bds.getString(CLASS_NAME_TAG)); } private static BDS serializeInternal2(String name, Object instance, Multimap, Map.Entry> alreadyWritten, IntRef biggest) throws SerializationException { BDS bds = new BDS(name); if (instance == null) { bds.addString(CLASS_NAME_TAG, NULL_TAG); return bds; } Class type = instance.getClass(); bds.addInt(IDX_TAG, ++biggest.value); if (serializers.containsKey(type.getName())) { int idx = findCycle(instance, type, alreadyWritten); if (idx != -1) bds.addInt(BACKREF_TAG, idx); else { alreadyWritten.put(Maps.immutableEntry(type.getName(), instance.hashCode()), Maps.immutableEntry(biggest.value, instance)); Serializer serializer = serializers.get(type.getName()); serializer.serialize(instance, bds, new BackrefFixer(alreadyWritten, biggest)); if (!bds.addString(CLASS_NAME_TAG, type.getName())) { throw new IllegalStateException("BDS String \"" + CLASS_NAME_TAG + "\" cannot be used when serializing!"); } } } else if (Boolean.class.equals(type) || boolean.class.equals(type)) { bds.addString(CLASS_NAME_TAG, "Boolean"); bds.addInt(PRIMITIVE_VALUE_TAG, (Boolean) instance ? 1 : 0); } else if (Byte.class.equals(type) || byte.class.equals(type)) { bds.addString(CLASS_NAME_TAG, "Byte"); bds.addByte(PRIMITIVE_VALUE_TAG, (Byte) instance); } else if (Character.class.equals(type) || byte.class.equals(type)) { bds.addString(CLASS_NAME_TAG, "Char"); bds.addChar(PRIMITIVE_VALUE_TAG, (Character) instance); } else if (Short.class.equals(type) || short.class.equals(type)) { bds.addString(CLASS_NAME_TAG, "Short"); bds.addShort(PRIMITIVE_VALUE_TAG, (Short) instance); } else if (Integer.class.equals(type) || int.class.equals(type)) { bds.addString(CLASS_NAME_TAG, "Int"); bds.addInt(PRIMITIVE_VALUE_TAG, (Integer) instance); } else if (Long.class.equals(type) || long.class.equals(type)) { bds.addString(CLASS_NAME_TAG, "Long"); bds.addLong(PRIMITIVE_VALUE_TAG, (Long) instance); } else if (Float.class.equals(type) || float.class.equals(type)) { bds.addString(CLASS_NAME_TAG, "Float"); bds.addFloat(PRIMITIVE_VALUE_TAG, (Float) instance); } else if (Double.class.equals(type) || double.class.equals(type)) { bds.addString(CLASS_NAME_TAG, "Double"); bds.addDouble(PRIMITIVE_VALUE_TAG, (Double) instance); } else if (String.class.equals(type)) { bds.addString(CLASS_NAME_TAG, "String"); bds.addString(PRIMITIVE_VALUE_TAG, (String) instance); } else if (File.class.equals(type)) { bds.addString(CLASS_NAME_TAG, "File"); bds.addString(PRIMITIVE_VALUE_TAG, ((File) instance).getAbsolutePath()); } else if (instance instanceof Date) { long time = ((Date) instance).getTime(); bds.addString(CLASS_NAME_TAG, "Date"); bds.addLong(DATE_VALUE_TAG, time); } else { int idx = findCycle(instance, type, alreadyWritten); if (idx != -1) bds.addInt(BACKREF_TAG, idx); else { alreadyWritten.put(Maps.immutableEntry(type.getName(), instance.hashCode()), Maps.immutableEntry(biggest.value, instance)); if (type.isArray()) { bds.addString(CLASS_NAME_TAG, "Array"); String componentType = type.getComponentType().getName(); bds.addString(ARRAY_TYPE_TAG, componentType); int length = Array.getLength(instance); bds.addInt(ARRAY_LENGTH_TAG, length); if (type.getComponentType().isPrimitive()) { if ("boolean".equals(componentType)) { boolean[] bArray = (boolean[]) instance; int[] b2i = new int[bArray.length]; for (int i = 0; i < bArray.length; i++) { b2i[i] = bArray[i] ? 1 : 0; } bds.addInts(PRIMITIVE_ARRAY_VALUE_TAG, b2i); } else if ("byte".equals(componentType)) { bds.addBytes(PRIMITIVE_ARRAY_VALUE_TAG, (byte[]) instance); } else if ("short".equals(componentType)) { bds.addShorts(PRIMITIVE_ARRAY_VALUE_TAG, (short[]) instance); } else if ("char".equals(componentType)) { bds.addChars(PRIMITIVE_ARRAY_VALUE_TAG, (char[]) instance); } else if ("int".equals(componentType)) { bds.addInts(PRIMITIVE_ARRAY_VALUE_TAG, (int[]) instance); } else if ("long".equals(componentType)) { bds.addLongs(PRIMITIVE_ARRAY_VALUE_TAG, (long[]) instance); } else if ("float".equals(componentType)) { bds.addFloats(PRIMITIVE_ARRAY_VALUE_TAG, (float[]) instance); } else if ("double".equals(componentType)) { bds.addDoubles(PRIMITIVE_ARRAY_VALUE_TAG, (double[]) instance); } else { throw new AssertionError("Unknown primitive type: " + componentType); } } else { for (int i = 0; i < length; i++) { Object o = Array.get(instance, i); BDS element = serializeInternal2("Item" + i, o, alreadyWritten, biggest); bds.addBDS(element); } } } else if (Collection.class.isAssignableFrom(type)) { Collection c = (Collection) instance; bds.addString(CLASS_NAME_TAG, "Collection"); bds.addString(ARRAY_TYPE_TAG, type.getName()); bds.addInt(ARRAY_LENGTH_TAG, c.size()); Iterator iter = c.iterator(); int i = 0; while (iter.hasNext()) { Object next = iter.next(); bds.addBDS(serializeInternal2("Item" + i++, next, alreadyWritten, biggest)); } } else { bds.addString(CLASS_NAME_TAG, type.getName()); for (Field f : ReflectionUtil.getAllFields(type)) { if (f.isAnnotationPresent(Transient.class) || Modifier.isStatic(f.getModifiers())) continue; if (!f.isAccessible()) { try { f.setAccessible(true); } catch (SecurityException e) { throw new SerializationException("Error serializing: Cannot access field " + f.getName() + " of class " + type, e); } } try { try { bds.addBDS(serializeInternal2(f.getName(), f.get(instance), alreadyWritten, biggest)); } catch (IllegalAccessException e) { throw new AssertionError("This should never happen"); } } catch (StackOverflowError e) { throw new SerializationException("Most probably a cycle exists in the object structure, and it could not be fixed. Provide a good hashCode and equals to your classes to fix it", e); } } } } } return bds; } private static int findCycle(Object instance, Class type, Multimap, Map.Entry> alreadyWritten) { Integer idx = null; Collection> entries = alreadyWritten.get(Maps.immutableEntry(type.getName(), instance.hashCode())); for (Map.Entry entry : entries) { if (entry.getValue().equals(instance)) { idx = entry.getKey(); break; } } return idx != null ? idx : -1; } @Nullable @SuppressWarnings({"unchecked", "ConstantConditions"}) private static Object deserializeInternal(BDS bds, Map pastInstances, Set unresolvedReferences) throws SerializationException, CannotDeserializeYet { if (isNull(bds)) return null; Object result; String type = bds.getString(CLASS_NAME_TAG); Integer backRef = bds.getInt(BACKREF_TAG); if (type == null) { if (backRef == null) { throw new SerializationException("Error deserializing: BDS was not in the correct format."); } else { throw new CannotDeserializeYet(backRef); } } int refId = bds.getInt(IDX_TAG); if (serializers.containsKey(type)) { result = serializers.get(type).deserialize(bds); pastInstances.put(refId, result); } else if ("Boolean".equals(type)) { result = bds.getInt(PRIMITIVE_VALUE_TAG) == 1; pastInstances.put(refId, result); } else if ("Byte".equals(type)) { result = bds.getByte(PRIMITIVE_VALUE_TAG); pastInstances.put(refId, result); } else if ("Char".equals(type)) { result = bds.getChar(PRIMITIVE_VALUE_TAG); pastInstances.put(refId, result); } else if ("Short".equals(type)) { result = bds.getShort(PRIMITIVE_VALUE_TAG); pastInstances.put(refId, result); } else if ("Int".equals(type)) { result = bds.getInt(PRIMITIVE_VALUE_TAG); pastInstances.put(refId, result); } else if ("Long".equals(type)) { result = bds.getLong(PRIMITIVE_VALUE_TAG); pastInstances.put(refId, result); } else if ("Float".equals(type)) { result = bds.getFloat(PRIMITIVE_VALUE_TAG); pastInstances.put(refId, result); } else if ("Double".equals(type)) { result = bds.getDouble(PRIMITIVE_VALUE_TAG); pastInstances.put(refId, result); } else if ("String".equals(type)) { result = bds.getString(PRIMITIVE_VALUE_TAG); pastInstances.put(refId, result); } else if ("File".equals(type)) { result = new File(bds.getString(PRIMITIVE_VALUE_TAG)); pastInstances.put(refId, result); } else if ("Date".equals(type)) { result = new Date(); ((Date) result).setTime(bds.getLong(DATE_VALUE_TAG)); pastInstances.put(refId, result); } else if ("Array".equals(type)) { String componentType = bds.getString(ARRAY_TYPE_TAG); if (componentType != null) { int length = bds.getInt(ARRAY_LENGTH_TAG); if ("boolean".equals(componentType)) { result = new boolean[length]; int[] ints = bds.getIntArray(PRIMITIVE_ARRAY_VALUE_TAG); for (int i = 0; i < ints.length; i++) { ((boolean[]) result)[i] = ints[i] == 1; } } else if ("byte".equals(componentType)) { result = bds.getByteArray(PRIMITIVE_ARRAY_VALUE_TAG); } else if ("short".equals(componentType)) { result = bds.getShortArray(PRIMITIVE_ARRAY_VALUE_TAG); } else if ("char".equals(componentType)) { result = bds.getCharArray(PRIMITIVE_ARRAY_VALUE_TAG); } else if ("int".equals(componentType)) { result = bds.getIntArray(PRIMITIVE_ARRAY_VALUE_TAG); } else if ("long".equals(componentType)) { result = bds.getLongArray(PRIMITIVE_ARRAY_VALUE_TAG); } else if ("float".equals(componentType)) { result = bds.getFloatArray(PRIMITIVE_ARRAY_VALUE_TAG); } else if ("double".equals(componentType)) { result = bds.getDoubleArray(PRIMITIVE_ARRAY_VALUE_TAG); } else { try { Class cType = Class.forName(componentType); result = Array.newInstance(cType, length); pastInstances.put(refId, result); for (int i = 0; i < length; i++) { try { Object element = deserializeInternal(bds.getBDS("Item" + i), pastInstances, unresolvedReferences); Array.set(result, i, element); } catch (CannotDeserializeYet e) { unresolvedReferences.add(new UnresolvedArray(result, i, e.refId)); } } } catch (ClassNotFoundException e) { throw new SerializationException("Error deserializing: Class " + componentType + " could not be found.", e); } } } else { int backref = backRef; if (pastInstances.containsKey(backref)) { result = pastInstances.get(backref); } else throw new CannotDeserializeYet(backref); } } else if ("Collection".equals(type)) { String collectionType = bds.getString(ARRAY_TYPE_TAG); if (collectionType != null) { int length = bds.getInt(ARRAY_LENGTH_TAG); try { Class cType = Class.forName(collectionType); try { Collection collection = (Collection) cType.newInstance(); pastInstances.put(refId, collection); for (int i = 0; i < length; i++) { try { Object element = deserializeInternal(bds.getBDS("Item" + i), pastInstances, unresolvedReferences); collection.add(element); } catch (CannotDeserializeYet e) { unresolvedReferences.add(new UnresolvedCol(collection, i, e.refId)); } } result = collection; } catch (InstantiationException e) { throw new SerializationException("Error deserializing: Class " + type + " cannot be instantiated. Add a default constructor.", e); } catch (IllegalAccessException e) { throw new SerializationException("Error deserializing: Empty constructor for class " + type + " is not public.", e); } } catch (ClassNotFoundException e) { throw new SerializationException("Error deserializing: Class " + type + " could not be found.", e); } } else { int backref = backRef; if (pastInstances.containsKey(backref)) { result = pastInstances.get(backref); } else throw new CannotDeserializeYet(backref); } } else { try { Class clazz = Class.forName(type); try { result = clazz.newInstance(); pastInstances.put(refId, result); for (Field f : ReflectionUtil.getAllFields(clazz)) { if (f.isAnnotationPresent(Transient.class) || Modifier.isStatic(f.getModifiers())) continue; if (!f.isAccessible()) { try { f.setAccessible(true); } catch (SecurityException e) { throw new SerializationException("Field " + f.getName() + " of class " + type + " could not be made accessible!", e); } } BDS fieldBDS = bds.getBDS(f.getName()); try { Object value = deserializeInternal(fieldBDS, pastInstances, unresolvedReferences); f.set(result, value); } catch (CannotDeserializeYet e) { unresolvedReferences.add(new UnresolvedField(result, f, e.refId)); } } } catch (InstantiationException e) { throw new SerializationException("Error deserializing: Class " + type + " cannot be instantiated. Add a default constructor.", e); } catch (IllegalAccessException e) { throw new SerializationException("Error deserializing: Empty constructor for class " + type + " is not public.", e); } } catch (ClassNotFoundException e) { throw new SerializationException("Error deserializing: Class " + type + " could not be found.", e); } } if (ROOT_OBJ_TAG.equals(bds.getName())) { Set toReSolve = new HashSet<>(unresolvedReferences); while (!toReSolve.isEmpty()) { unresolvedReferences = toReSolve; toReSolve = Sets.newHashSet(); for (UnresolvedReference ur : unresolvedReferences) { if (pastInstances.containsKey(ur.refId) || ur.refId == 0) { Object value = pastInstances.get(ur.refId); if (ur instanceof UnresolvedField) { UnresolvedField urf = (UnresolvedField) ur; try { urf.f.set(urf.instance, value); } catch (IllegalAccessException e) { throw new AssertionError("How did this even happen?"); } } else if (ur instanceof UnresolvedArray) { UnresolvedArray ura = (UnresolvedArray) ur; Array.set(ura.array, ura.idx, value); } else if (ur instanceof UnresolvedCol) { UnresolvedCol urc = ((UnresolvedCol) ur); if (urc.collection instanceof List) { ((List) urc.collection).add(urc.idx, value); } else { urc.collection.add(value); } } } else toReSolve.add(ur); } } } return result; } /** * Pretty-prints the given BDS into the provided PrintStream. * * @param bds The BDS to pretty-print. * @param out The stream to which the BDS should be printed. */ @API public static void debug(BDS bds, PrintStream out) { debug(bds, out, 0); } @SuppressWarnings("ConstantConditions") private static void debug(BDS bds, PrintStream out, int level) { out.println(Strings.repeat("\t", level) + "BDS: " + bds.getName()); if (bds.getAllBytes().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Bytes:"); for (String s : bds.getAllBytes()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + bds.getByte(s)); } } if (bds.getAllChars().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Chars:"); for (String s : bds.getAllChars()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + (int) bds.getChar(s)); } } if (bds.getAllShorts().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Shorts:"); for (String s : bds.getAllShorts()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + bds.getShort(s)); } } if (bds.getAllInts().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Ints:"); for (String s : bds.getAllInts()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + bds.getInt(s)); } } if (bds.getAllLongs().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Longs:"); for (String s : bds.getAllLongs()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + bds.getLong(s)); } } if (bds.getAllFloats().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Floats:"); for (String s : bds.getAllFloats()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + bds.getFloat(s)); } } if (bds.getAllDoubles().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Doubles:"); for (String s : bds.getAllDoubles()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + bds.getDouble(s)); } } if (bds.getAllStrings().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Strings:"); for (String s : bds.getAllStrings()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + bds.getString(s)); } } if (bds.getAllByteArrays().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Byte Arrays:"); for (String s : bds.getAllByteArrays()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + Arrays.toString(bds.getByteArray(s))); } } if (bds.getAllCharArrays().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Char Arrays:"); for (String s : bds.getAllCharArrays()) { out.print(Strings.repeat("\t", level + 2) + s + ": ["); char[] charArray = bds.getCharArray(s); for (int i = 0; i < charArray.length; i++) { char c = charArray[i]; out.print((int) c); if (i != charArray.length - 1) out.print(", "); } out.println("]"); } } if (bds.getAllShortArrays().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Short Arrays:"); for (String s : bds.getAllShortArrays()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + Arrays.toString(bds.getShortArray(s))); } } if (bds.getAllIntArrays().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Int Arrays:"); for (String s : bds.getAllIntArrays()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + Arrays.toString(bds.getIntArray(s))); } } if (bds.getAllLongArrays().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Long Arrays:"); for (String s : bds.getAllLongArrays()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + Arrays.toString(bds.getLongArray(s))); } } if (bds.getAllFloatArrays().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Float Arrays:"); for (String s : bds.getAllFloatArrays()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + Arrays.toString(bds.getFloatArray(s))); } } if (bds.getAllDoubleArrays().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "Double Arrays:"); for (String s : bds.getAllDoubleArrays()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + Arrays.toString(bds.getDoubleArray(s))); } } if (bds.getAllStringArrays().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "String:"); for (String s : bds.getAllStringArrays()) { out.println(Strings.repeat("\t", level + 2) + s + ": " + Arrays.toString(bds.getStringArray(s))); } } if (bds.getAllBDSs().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "BDSs:"); for (String s : bds.getAllBDSs()) { debug(bds.getBDS(s), out, level + 2); } } if (bds.getAllBDSArrays().size() != 0) { out.println(Strings.repeat("\t", level + 1) + "BDS Arrays:"); for (String s : bds.getAllBDSArrays()) { out.println(Strings.repeat("\t", level + 2) + s + " = ["); for (BDS nBds : bds.getBDSArray(s)) { debug(nBds, out, level + 3); } out.print(Strings.repeat("\t", level + 1) + "]"); } } out.println(); } /** * This class is used in {@link Serializer Serializers} in order to fix cycles in compound objects (made of several others). * The only way to get an instance of this class is through the {@link Serializer#serialize(Object, BDS, BackrefFixer)} method. * The instance given to the Serializer should be used to call {@link BDSUtil#serialize(Object, BackrefFixer)}, and not the method without * the last parameter. * * @author Aritz Lopez */ public static class BackrefFixer { private final Multimap, Map.Entry> alreadyWritten; private final IntRef biggest; private BackrefFixer(Multimap, Map.Entry> alreadyWritten, IntRef biggest) { this.alreadyWritten = alreadyWritten; this.biggest = biggest; } } private static class IntRef { int value; public IntRef(int value) { this.value = value; } } protected abstract static class UnresolvedReference { protected int refId; } private static class UnresolvedField extends UnresolvedReference { final Object instance; final Field f; public UnresolvedField(Object instance, Field f, int refId) { this.instance = instance; this.f = f; this.refId = refId; } } private static class UnresolvedCol extends UnresolvedReference { final Collection collection; final int idx; private UnresolvedCol(Collection collection, int idx, int refId) { this.collection = collection; this.idx = idx; this.refId = refId; } } private static class UnresolvedArray extends UnresolvedReference { final Object array; final int idx; private UnresolvedArray(Object array, int idx, int refId) { this.array = array; this.idx = idx; this.refId = refId; } } private static class CannotDeserializeYet extends Exception { final int refId; public CannotDeserializeYet(int refId) { this.refId = refId; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy