|
|
|
@ -1,7 +1,12 @@ |
|
|
|
|
package mightypork.utils.ion; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.*; |
|
|
|
|
import java.io.File; |
|
|
|
|
import java.io.FileInputStream; |
|
|
|
|
import java.io.FileOutputStream; |
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.io.InputStream; |
|
|
|
|
import java.io.OutputStream; |
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
import java.util.HashMap; |
|
|
|
|
import java.util.List; |
|
|
|
@ -13,17 +18,17 @@ import mightypork.utils.Support; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Universal data storage system (main API class) |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @author Ondřej Hruška (MightyPork) |
|
|
|
|
*/ |
|
|
|
|
public class Ion { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final int RESERVED_LOW = 0; |
|
|
|
|
private static final int RESERVED_HIGH = 49; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final int RANGE_LOW = 0; |
|
|
|
|
private static final int RANGE_HIGH = 255; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// marks for object saving
|
|
|
|
|
/** Null mark */ |
|
|
|
|
static final int NULL = 0; |
|
|
|
@ -79,31 +84,31 @@ public class Ion { |
|
|
|
|
public static final int MAP = 25; |
|
|
|
|
/** Array of arbitrary objects */ |
|
|
|
|
public static final int OBJECT_ARRAY = 26; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Ionizables<Mark, Class> */ |
|
|
|
|
private static Map<Integer, Class<?>> markToClass = new HashMap<>(); |
|
|
|
|
private static Map<Class<?>, Integer> classToMark = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static Map<Class<?>, IonizerBinary<?>> ionizersBinary = new HashMap<>(); |
|
|
|
|
private static Map<Class<?>, IonizerBundled<?>> ionizersBundled = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static boolean reservedMarkChecking; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static { |
|
|
|
|
reservedMarkChecking = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// register built-ins
|
|
|
|
|
register(ION_BUNDLE, IonDataBundle.class); |
|
|
|
|
register(SEQUENCE_WRAPPER, IonSequenceWrapper.class); |
|
|
|
|
register(MAP_WRAPPER, IonMapWrapper.class); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
reservedMarkChecking = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Register a type for writing/loading. |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @param mark binary ION mark |
|
|
|
|
* @param objClass class of the registered object |
|
|
|
|
*/ |
|
|
|
@ -114,16 +119,16 @@ public class Ion { |
|
|
|
|
throw new IllegalArgumentException("Cannot register directly: " + Support.str(objClass)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assertHasImplicitConstructor(objClass); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
registerUsingMark(mark, objClass); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Try to register a type using a static final ION_MARK int field. |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @param objClass type class
|
|
|
|
|
*/ |
|
|
|
|
public static void register(Class<?> objClass) |
|
|
|
@ -133,66 +138,66 @@ public class Ion { |
|
|
|
|
throw new IllegalArgumentException("Cannot register directly: " + Support.str(objClass)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assertHasImplicitConstructor(objClass); |
|
|
|
|
registerUsingConstant(objClass); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void registerUsingMark(int mark, Class<?> objClass) |
|
|
|
|
{ |
|
|
|
|
assertMarkAvailable(mark, objClass); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
markToClass.put(mark, objClass); |
|
|
|
|
classToMark.put(objClass, mark); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static void registerUsingConstant(Class<?> objClass) |
|
|
|
|
{ |
|
|
|
|
try { |
|
|
|
|
final int mark = ((Number) Reflect.getConstantFieldValue(objClass, "ION_MARK")).intValue(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
registerUsingMark(mark, objClass); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (final Exception e) { |
|
|
|
|
throw new RuntimeException("Could not register " + Support.str(objClass) + " using an ION_MARK field.", e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Register new binary ionizer. |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @param mark binary ION mark |
|
|
|
|
* @param ionizer ionizer |
|
|
|
|
*/ |
|
|
|
|
public static void registerIndirect(int mark, IonizerBinary<?> ionizer) |
|
|
|
|
{ |
|
|
|
|
final Class<?> objClass = Reflect.getGenericParameters(ionizer.getClass())[0]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
registerUsingMark(mark, objClass); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ionizersBinary.put(objClass, ionizer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Register new bundled ionizer. |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @param mark binary ION mark |
|
|
|
|
* @param ionizer ionizer |
|
|
|
|
*/ |
|
|
|
|
public static void registerIndirect(int mark, IonizerBundled<?> ionizer) |
|
|
|
|
{ |
|
|
|
|
final Class<?> objClass = Reflect.getGenericParameters(ionizer.getClass())[0]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
registerUsingMark(mark, objClass); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ionizersBundled.put(objClass, ionizer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void assertHasImplicitConstructor(Class<?> objClass) |
|
|
|
|
{ |
|
|
|
|
try { |
|
|
|
@ -201,28 +206,28 @@ public class Ion { |
|
|
|
|
throw new RuntimeException("Class " + objClass + " doesn't have an implicit constructor."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void assertMarkAvailable(int mark, Class<?> objClass) |
|
|
|
|
{ |
|
|
|
|
// negative marks are allowed.
|
|
|
|
|
if (mark > RANGE_HIGH) throw new IllegalArgumentException("Mark must be < 256."); |
|
|
|
|
if (mark < RANGE_LOW) throw new IllegalArgumentException("Mark must be positive."); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (reservedMarkChecking && isMarkReserved(mark)) { |
|
|
|
|
throw new IllegalArgumentException("Marks " + RESERVED_LOW + ".." + RESERVED_HIGH + " are reserved."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (markToClass.containsKey(mark)) { |
|
|
|
|
throw new IllegalArgumentException("Mark " + mark + " is already in use."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (classToMark.containsKey(objClass)) { |
|
|
|
|
throw new IllegalArgumentException(Support.str(objClass) + " is already registered."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Load binary from file and cast. |
|
|
|
|
*/ |
|
|
|
@ -230,19 +235,19 @@ public class Ion { |
|
|
|
|
{ |
|
|
|
|
return fromFile(new File(path)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Load binary from file and cast. |
|
|
|
|
*/ |
|
|
|
|
public static <T> T fromFile(File file) throws IOException |
|
|
|
|
{ |
|
|
|
|
try (InputStream in = new FileInputStream(file)) { |
|
|
|
|
try(InputStream in = new FileInputStream(file)) { |
|
|
|
|
return fromStream(in); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Write binary to file with mark. |
|
|
|
|
*/ |
|
|
|
@ -250,49 +255,49 @@ public class Ion { |
|
|
|
|
{ |
|
|
|
|
toFile(new File(path), obj); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Write binary to file with mark. |
|
|
|
|
*/ |
|
|
|
|
public static void toFile(File file, Object obj) throws IOException |
|
|
|
|
{ |
|
|
|
|
try (OutputStream out = new FileOutputStream(file)) { |
|
|
|
|
|
|
|
|
|
try(OutputStream out = new FileOutputStream(file)) { |
|
|
|
|
|
|
|
|
|
toStream(out, obj); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
out.flush(); |
|
|
|
|
} catch (final Exception e) { |
|
|
|
|
throw new IOException("Error writing to ION file.", e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Load object from stream based on mark, try to cast. |
|
|
|
|
*/ |
|
|
|
|
public static <T> T fromStream(InputStream in) throws IOException |
|
|
|
|
{ |
|
|
|
|
try (final IonInput inp = new IonInput(in)) { |
|
|
|
|
try(final IonInput inp = new IonInput(in)) { |
|
|
|
|
return (T) inp.readObject(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Write object to output with a mark. |
|
|
|
|
*/ |
|
|
|
|
public static void toStream(OutputStream out, Object obj) throws IOException |
|
|
|
|
{ |
|
|
|
|
try (IonOutput iout = new IonOutput(out)) { |
|
|
|
|
try(IonOutput iout = new IonOutput(out)) { |
|
|
|
|
iout.writeObject(obj); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Get ion input |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @param path file path to read |
|
|
|
|
* @return input |
|
|
|
|
* @throws IOException |
|
|
|
@ -301,11 +306,11 @@ public class Ion { |
|
|
|
|
{ |
|
|
|
|
return getInput(new File(path)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Get ion input |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @param file file to read |
|
|
|
|
* @return input |
|
|
|
|
* @throws IOException |
|
|
|
@ -315,11 +320,11 @@ public class Ion { |
|
|
|
|
{ |
|
|
|
|
return new IonInput(new FileInputStream(file)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Get ion output |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @param path file path to write |
|
|
|
|
* @return output |
|
|
|
|
* @throws IOException |
|
|
|
@ -328,11 +333,11 @@ public class Ion { |
|
|
|
|
{ |
|
|
|
|
return getOutput(new File(path)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Get ion output |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @param file file to write |
|
|
|
|
* @return output |
|
|
|
|
* @throws IOException |
|
|
|
@ -342,8 +347,8 @@ public class Ion { |
|
|
|
|
{ |
|
|
|
|
return new IonOutput(new FileOutputStream(file)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Create new bundle and write the object to it. |
|
|
|
|
*/ |
|
|
|
@ -353,12 +358,12 @@ public class Ion { |
|
|
|
|
content.save(ib); |
|
|
|
|
return ib; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Try to unwrap an object from bundle. The object class must have implicit |
|
|
|
|
* accessible constructor. |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @param bundle unwrapped bundle |
|
|
|
|
* @param objClass class of desired object |
|
|
|
|
* @return the object unwrapped |
|
|
|
@ -374,56 +379,56 @@ public class Ion { |
|
|
|
|
throw new IOException("Could not instantiate " + Support.str(objClass) + "."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static Class<?> getClassForMark(int mark) |
|
|
|
|
{ |
|
|
|
|
return markToClass.get(mark); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static IonizerBinary<?> getIonizerBinaryForClass(Class<?> clz) |
|
|
|
|
{ |
|
|
|
|
return ionizersBinary.get(clz); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static IonizerBundled<?> getIonizerBundledForClass(Class<?> clz) |
|
|
|
|
{ |
|
|
|
|
return ionizersBundled.get(clz); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static int getMark(Object object) |
|
|
|
|
{ |
|
|
|
|
assertRegistered(object); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return classToMark.get(object.getClass()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return true if the mark is for a registered {@link IonBinary} object |
|
|
|
|
*/ |
|
|
|
|
static boolean isMarkForBinary(int mark) |
|
|
|
|
{ |
|
|
|
|
if (!markToClass.containsKey(mark)) return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return IonBinary.class.isAssignableFrom(markToClass.get(mark)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return true if the mark is for a registered {@link IonBinary} object |
|
|
|
|
*/ |
|
|
|
|
static boolean isMarkForBundled(int mark) |
|
|
|
|
{ |
|
|
|
|
if (!markToClass.containsKey(mark)) return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return IonBundled.class.isAssignableFrom(markToClass.get(mark)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return true if the mark is for a registered indirectly saved binary |
|
|
|
|
* object |
|
|
|
@ -431,11 +436,11 @@ public class Ion { |
|
|
|
|
static boolean isMarkForIndirectBinary(int mark) |
|
|
|
|
{ |
|
|
|
|
if (!markToClass.containsKey(mark)) return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ionizersBinary.containsKey(markToClass.get(mark)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return true if the mark is for a registered indirectly saved bundled |
|
|
|
|
* object |
|
|
|
@ -443,11 +448,11 @@ public class Ion { |
|
|
|
|
static boolean isMarkForIndirectBundled(int mark) |
|
|
|
|
{ |
|
|
|
|
if (!markToClass.containsKey(mark)) return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ionizersBundled.containsKey(markToClass.get(mark)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return true if the mark is reserved for internal use |
|
|
|
|
*/ |
|
|
|
@ -455,8 +460,8 @@ public class Ion { |
|
|
|
|
{ |
|
|
|
|
return mark >= RESERVED_LOW && mark <= RESERVED_HIGH; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return true if the mark is for a registered {@link IonBinary} object |
|
|
|
|
*/ |
|
|
|
@ -464,14 +469,14 @@ public class Ion { |
|
|
|
|
{ |
|
|
|
|
final Class<?> clazz = object.getClass(); |
|
|
|
|
if (classToMark.containsKey(clazz)) return true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Make sure object is registered in the table. |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* @throws IOException if not registered or class mismatch |
|
|
|
|
*/ |
|
|
|
|
static void assertRegistered(Object obj) |
|
|
|
@ -480,8 +485,8 @@ public class Ion { |
|
|
|
|
throw new RuntimeException("Type not registered: " + Support.str(obj.getClass())); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* For get all external registered types - just like if the class was |
|
|
|
|
* freshly loaded. Can be used for unit tests. |
|
|
|
@ -489,31 +494,31 @@ public class Ion { |
|
|
|
|
public static void reset() |
|
|
|
|
{ |
|
|
|
|
final List<Integer> toRemove = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
// remove direct
|
|
|
|
|
|
|
|
|
|
// remove direct
|
|
|
|
|
for (final Integer mark : markToClass.keySet()) { |
|
|
|
|
if (!isMarkReserved(mark)) { |
|
|
|
|
toRemove.add(mark); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (final int i : toRemove) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final Class<?> clz = markToClass.remove(i); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classToMark.remove(clz); |
|
|
|
|
ionizersBinary.remove(clz); |
|
|
|
|
ionizersBundled.remove(clz); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static boolean isObjectIndirectBundled(Object obj) |
|
|
|
|
{ |
|
|
|
|
return ionizersBundled.containsKey(obj.getClass()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static boolean isObjectIndirectBinary(Object obj) |
|
|
|
|
{ |
|
|
|
|
return ionizersBinary.containsKey(obj.getClass()); |
|
|
|
|