From 035aa58847f9ef986faa0a56cf933d8b973d0995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 19 Apr 2014 00:49:18 +0200 Subject: [PATCH] Reworked ION --- .../util/files/ion/AbstractIonList.java | 81 --- .../util/files/ion/AbstractIonMap.java | 100 ---- .../util/files/ion/BinaryUtils.java | 235 -------- src/mightypork/util/files/ion/Ion.java | 541 ++++++++++++++---- .../util/files/ion/IonDataBundle.java | 157 +++++ .../util/files/ion/IonDataList.java | 169 ++++++ .../util/files/ion/IonException.java | 25 - src/mightypork/util/files/ion/IonList.java | 181 ++---- src/mightypork/util/files/ion/IonMap.java | 184 +++--- src/mightypork/util/files/ion/IonMarks.java | 57 -- src/mightypork/util/files/ion/Ionizable.java | 13 +- .../util/files/ion/IonizableOptional.java | 17 - 12 files changed, 871 insertions(+), 889 deletions(-) delete mode 100644 src/mightypork/util/files/ion/AbstractIonList.java delete mode 100644 src/mightypork/util/files/ion/AbstractIonMap.java delete mode 100644 src/mightypork/util/files/ion/BinaryUtils.java create mode 100644 src/mightypork/util/files/ion/IonDataBundle.java create mode 100644 src/mightypork/util/files/ion/IonDataList.java delete mode 100644 src/mightypork/util/files/ion/IonException.java delete mode 100644 src/mightypork/util/files/ion/IonMarks.java delete mode 100644 src/mightypork/util/files/ion/IonizableOptional.java diff --git a/src/mightypork/util/files/ion/AbstractIonList.java b/src/mightypork/util/files/ion/AbstractIonList.java deleted file mode 100644 index 8883758..0000000 --- a/src/mightypork/util/files/ion/AbstractIonList.java +++ /dev/null @@ -1,81 +0,0 @@ -package mightypork.util.files.ion; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; - - -/** - * Ionizable Arraylist - * - * @author MightyPork - * @param - */ -public abstract class AbstractIonList extends ArrayList implements Ionizable { - - @Override - public void ionRead(InputStream in) throws IonException - { - try { - while (true) { - final byte b = BinaryUtils.readByte(in); - - if (b == IonMarks.ENTRY) { - final T value = (T) Ion.readObject(in); - add(value); - } else if (b == IonMarks.END) { - break; - } else { - throw new IonException("Unexpected mark in IonList: " + Integer.toHexString(b)); - } - } - ionReadCustomData(in); - } catch (final IOException e) { - throw new IonException("Error reading ion list", e); - } - } - - - @Override - public void ionWrite(OutputStream out) throws IonException - { - try { - for (final T entry : this) { - if (entry instanceof IonizableOptional && !((IonizableOptional) entry).ionShouldSave()) continue; - BinaryUtils.writeByte(out, IonMarks.ENTRY); - Ion.writeObject(out, entry); - } - BinaryUtils.writeByte(out, IonMarks.END); - ionWriteCustomData(out); - } catch (final IOException e) { - throw new IonException("Error reading ion map", e); - } - } - - - /** - * Read custom data of this AbstractIonList implementation - * - * @param in input stream - */ - public void ionReadCustomData(InputStream in) - { - } - - - /** - * Write custom data of this AbstractIonList implementation - * - * @param out output stream - */ - public void ionWriteCustomData(OutputStream out) - { - } - - - @Override - public abstract byte ionMark(); - -} diff --git a/src/mightypork/util/files/ion/AbstractIonMap.java b/src/mightypork/util/files/ion/AbstractIonMap.java deleted file mode 100644 index 3cd4b8e..0000000 --- a/src/mightypork/util/files/ion/AbstractIonMap.java +++ /dev/null @@ -1,100 +0,0 @@ -package mightypork.util.files.ion; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.LinkedHashMap; - - -/** - * Ionizable HashMap - * - * @author MightyPork - * @param - */ -public abstract class AbstractIonMap extends LinkedHashMap implements Ionizable { - - @Override - public V get(Object key) - { - return super.get(key); - } - - - @Override - public V put(String key, V value) - { - return super.put(key, value); - } - - - @Override - public void ionRead(InputStream in) throws IonException - { - try { - while (true) { - final byte b = BinaryUtils.readByte(in); - if (b == IonMarks.ENTRY) { - final String key = BinaryUtils.readString(in); - - final V value = (V) Ion.readObject(in); - put(key, value); - - } else if (b == IonMarks.END) { - break; - } else { - throw new RuntimeException("Unexpected mark in IonMap: " + Integer.toHexString(b)); - } - } - ionReadCustomData(in); - } catch (final IOException e) { - throw new IonException("Error reading ion map", e); - } - } - - - @Override - public void ionWrite(OutputStream out) throws IonException - { - try { - for (final java.util.Map.Entry entry : entrySet()) { - BinaryUtils.writeByte(out, IonMarks.ENTRY); - BinaryUtils.writeString(out, entry.getKey()); - Ion.writeObject(out, entry.getValue()); - } - BinaryUtils.writeByte(out, IonMarks.END); - ionWriteCustomData(out); - } catch (final IOException e) { - throw new IonException("Error reading ion map", e); - } - } - - - /** - * Read custom data of this AbstractIonMap implementation - * - * @param in input stream - */ - public void ionReadCustomData(InputStream in) - { - } - - - /** - * Write custom data of this AbstractIonMap implementation - * - * @param out output stream - */ - public void ionWriteCustomData(OutputStream out) - { - } - - - @Override - public byte ionMark() - { - return IonMarks.MAP; - } - -} diff --git a/src/mightypork/util/files/ion/BinaryUtils.java b/src/mightypork/util/files/ion/BinaryUtils.java deleted file mode 100644 index e421a08..0000000 --- a/src/mightypork/util/files/ion/BinaryUtils.java +++ /dev/null @@ -1,235 +0,0 @@ -package mightypork.util.files.ion; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; - - -/** - * Utilities to store and load objects to streams. - * - * @author MightyPork - */ -public class BinaryUtils { - - private static ByteBuffer bi = ByteBuffer.allocate(Integer.SIZE / 8); - private static ByteBuffer bd = ByteBuffer.allocate(Double.SIZE / 8); - private static ByteBuffer bf = ByteBuffer.allocate(Float.SIZE / 8); - private static ByteBuffer bc = ByteBuffer.allocate(Character.SIZE / 8); - private static ByteBuffer bl = ByteBuffer.allocate(Long.SIZE / 8); - private static ByteBuffer bs = ByteBuffer.allocate(Short.SIZE / 8); - - private static byte[] ai = new byte[Integer.SIZE / 8]; - private static byte[] ad = new byte[Double.SIZE / 8]; - private static byte[] af = new byte[Float.SIZE / 8]; - private static byte[] ac = new byte[Character.SIZE / 8]; - private static byte[] al = new byte[Long.SIZE / 8]; - private static byte[] as = new byte[Short.SIZE / 8]; - - - // CONVERSIONS - - public static byte[] getBytesBool(boolean bool) - { - return new byte[] { (byte) (bool ? 1 : 0) }; - } - - - public static byte[] getBytesByte(byte num) - { - return new byte[] { num }; - } - - - public static byte[] getBytesChar(char num) - { - bc.clear(); - bc.putChar(num); - return bc.array(); - } - - - public static byte[] getBytesShort(short num) - { - bs.clear(); - bs.putShort(num); - return bs.array(); - } - - - public static byte[] getBytesInt(int num) - { - bi.clear(); - bi.putInt(num); - return bi.array(); - } - - - public static byte[] getBytesLong(long num) - { - bl.clear(); - bl.putLong(num); - return bl.array(); - } - - - public static byte[] getBytesFloat(float num) - { - bf.clear(); - bf.putFloat(num); - return bf.array(); - } - - - public static byte[] getBytesDouble(double num) - { - bd.clear(); - bd.putDouble(num); - return bd.array(); - } - - - public static byte[] getBytesString(String str) - { - final char[] chars = str.toCharArray(); - - final ByteBuffer bstr = ByteBuffer.allocate((Character.SIZE / 8) * chars.length + (Character.SIZE / 8)); - for (final char c : chars) { - bstr.putChar(c); - } - - bstr.putChar((char) 0); - - return bstr.array(); - } - - - public static void writeBoolean(OutputStream out, boolean num) throws IOException - { - out.write(getBytesBool(num)); - } - - - public static void writeByte(OutputStream out, byte num) throws IOException - { - out.write(getBytesByte(num)); - } - - - public static void writeChar(OutputStream out, char num) throws IOException - { - out.write(getBytesChar(num)); - } - - - public static void writeShort(OutputStream out, short num) throws IOException - { - out.write(getBytesShort(num)); - } - - - public static void writeInt(OutputStream out, int num) throws IOException - { - out.write(getBytesInt(num)); - } - - - public static void writeLong(OutputStream out, long num) throws IOException - { - out.write(getBytesLong(num)); - } - - - public static void writeFloat(OutputStream out, float num) throws IOException - { - out.write(getBytesFloat(num)); - } - - - public static void writeDouble(OutputStream out, double num) throws IOException - { - out.write(getBytesDouble(num)); - } - - - public static void writeString(OutputStream out, String str) throws IOException - { - out.write(getBytesString(str)); - } - - - // READING - - public static boolean readBoolean(InputStream in) throws IOException - { - return in.read() > 0; - } - - - public static byte readByte(InputStream in) throws IOException - { - return (byte) in.read(); - } - - - public static char readChar(InputStream in) throws IOException - { - if (-1 == in.read(ac, 0, ac.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(ac); - return buf.getChar(); - } - - - public static short readShort(InputStream in) throws IOException - { - if (-1 == in.read(as, 0, as.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(as); - return buf.getShort(); - } - - - public static long readLong(InputStream in) throws IOException - { - if (-1 == in.read(al, 0, al.length)) throw new IOException("End of stream."); - - final ByteBuffer buf = ByteBuffer.wrap(al); - return buf.getLong(); - } - - - public static int readInt(InputStream in) throws IOException - { - if (-1 == in.read(ai, 0, ai.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(ai); - return buf.getInt(); - } - - - public static float readFloat(InputStream in) throws IOException - { - if (-1 == in.read(af, 0, af.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(af); - return buf.getFloat(); - } - - - public static double readDouble(InputStream in) throws IOException - { - if (-1 == in.read(ad, 0, ad.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(ad); - return buf.getDouble(); - } - - - public static String readString(InputStream in) throws IOException - { - String s = ""; - char c; - while ((c = readChar(in)) > 0) { - s += c; - } - return s; - } -} diff --git a/src/mightypork/util/files/ion/Ion.java b/src/mightypork/util/files/ion/Ion.java index 475e005..fd955dd 100644 --- a/src/mightypork/util/files/ion/Ion.java +++ b/src/mightypork/util/files/ion/Ion.java @@ -2,10 +2,11 @@ package mightypork.util.files.ion; import java.io.*; +import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; -import mightypork.util.math.Calc; +import mightypork.util.logging.Log; /** @@ -15,57 +16,128 @@ import mightypork.util.math.Calc; */ public class Ion { + /* + * 0-29 ... primitive and Java built-in types + * 30-59 ... technical marks + * 60-99 ... built-in ION types + */ + + // primitives + /** Null mark (for internal use) */ + private static final short NULL = 0; + /** Boolean mark (for internal use) */ + private static final short BOOLEAN = 1; + /** Byte mark (for internal use) */ + private static final short BYTE = 2; + /** Character mark (for internal use) */ + private static final short CHAR = 3; + /** Short mark (for internal use) */ + private static final short SHORT = 4; + /** Integer mark (for internal use) */ + private static final short INT = 5; + /** Long mark (for internal use) */ + private static final short LONG = 6; + /** Float mark (for internal use) */ + private static final short FLOAT = 7; + /** Double mark (for internal use) */ + private static final short DOUBLE = 8; + /** String mark (for internal use) */ + private static final short STRING = 9; + + // technical + + /** + * Entry mark - general purpose, marks an entry in sequence of objects. Used + * to indicate that the sequence continues wityh another element. + */ + public static final short ENTRY = 30; + + /** + * Start mark - general purpose, marks start of a sequence of stored + * objects. + */ + public static final short START = 31; + + /** + * End mark - general purpose, marks end of sequence of stored objects. + */ + public static final short END = 32; + + /** + * Length mark, indicating length of something (such as array) - general + * purpose + */ + public static final short LENGTH = 33; + + // built in + /** Map mark (built-in data structure) */ + static final short DATA_BUNDLE = 60; + + /** List mark (built-in data structure) */ + static final short DATA_LIST = 61; + /** Ionizables */ - private static Map> customIonizables = new HashMap<>(); + private static Map> customIonizables = new HashMap<>(); + + // buffers and helper arrays for storing to streams. + private static ByteBuffer bi = ByteBuffer.allocate(Integer.SIZE / 8); + private static ByteBuffer bd = ByteBuffer.allocate(Double.SIZE / 8); + private static ByteBuffer bf = ByteBuffer.allocate(Float.SIZE / 8); + private static ByteBuffer bc = ByteBuffer.allocate(Character.SIZE / 8); + private static ByteBuffer bl = ByteBuffer.allocate(Long.SIZE / 8); + private static ByteBuffer bs = ByteBuffer.allocate(Short.SIZE / 8); + private static byte[] ai = new byte[Integer.SIZE / 8]; + private static byte[] ad = new byte[Double.SIZE / 8]; + private static byte[] af = new byte[Float.SIZE / 8]; + private static byte[] ac = new byte[Character.SIZE / 8]; + private static byte[] al = new byte[Long.SIZE / 8]; + private static byte[] as = new byte[Short.SIZE / 8]; + + /** + * Indicates whether range checking should be performed when registering + * marks. + */ + private static boolean safety; - // register default ionizables static { - try { - registerIonizable(IonMarks.MAP, IonMap.class); - registerIonizable(IonMarks.LIST, IonList.class); - } catch (final IonException e) { - e.printStackTrace(); - } + // register default ionizables + safety = false; + registerIonizable(Ion.DATA_BUNDLE, IonDataBundle.class); + registerIonizable(Ion.DATA_LIST, IonDataList.class); + safety = true; } /** * Register new Ionizable for direct reconstructing. * - * @param mark byte mark to be used, see {@link IonMarks} for reference. + * @param mark mark to be used. Numbers 0..99 are reserved. * @param objClass class of the registered Ionizable - * @throws IonException */ - public static void registerIonizable(byte mark, Class objClass) throws IonException + public static void registerIonizable(short mark, Class objClass) { + // negative marks are allowed. + + if (safety && mark >= 0 && mark < 100) { + throw new RuntimeException("Marks 0..99 are reserved."); + } + if (customIonizables.containsKey(mark)) { - throw new IonException("IonMark " + mark + " is already used."); + throw new RuntimeException("Mark " + mark + " is already in use."); } + customIonizables.put(mark, objClass); } - /** - * Load Ion object from file. - * - * @param file file path - * @return the loaded object - * @throws IonException - */ - public static Object fromFile(String file) throws IonException - { - return fromFile(new File(file)); - } - - /** * Load Ion object from file. * * @param file file * @return the loaded object - * @throws IonException on failure + * @throws IOException on failure */ - public static Object fromFile(File file) throws IonException + public static Object fromFile(File file) throws IOException { try(InputStream in = new FileInputStream(file)) { @@ -73,7 +145,7 @@ public class Ion { return obj; } catch (final IOException e) { - throw new IonException("Error loading ION file.", e); + throw new IOException("Error loading ION file.", e); } } @@ -83,9 +155,9 @@ public class Ion { * * @param in input stream * @return the loaded object - * @throws IonException + * @throws IOException */ - public static Object fromStream(InputStream in) throws IonException + public static Object fromStream(InputStream in) throws IOException { return readObject(in); } @@ -96,22 +168,9 @@ public class Ion { * * @param path file path * @param obj object to store - * @throws IonException + * @throws IOException */ - public static void toFile(String path, Object obj) throws IonException - { - toFile(new File(path), obj); - } - - - /** - * Store Ion object to file. - * - * @param path file path - * @param obj object to store - * @throws IonException - */ - public static void toFile(File path, Object obj) throws IonException + public static void toFile(File path, Object obj) throws IOException { try(OutputStream out = new FileOutputStream(path)) { final String f = path.toString(); @@ -119,159 +178,399 @@ public class Ion { if (!dir.mkdirs()) throw new IOException("Could not create file."); - toStream(out, obj); + writeObject(out, obj); out.flush(); out.close(); } catch (final Exception e) { - throw new IonException("Error writing to ION file.", e); + throw new IOException("Error writing to ION file.", e); } } /** - * Store Ion object to output stream. - * - * @param out output stream * - * @param obj object to store - * @throws IonException - */ - public static void toStream(OutputStream out, Object obj) throws IonException - { - writeObject(out, obj); - } - - - /** - * Read single ionizable or primitive object from input stream + * Read single object from input stream * * @param in input stream * @return the loaded object - * @throws IonException + * @throws IOException */ - public static Object readObject(InputStream in) throws IonException + public static Object readObject(InputStream in) throws IOException { try { - final int bi = in.read(); - if (bi == -1) throw new IonException("Unexpected end of stream."); - final byte b = (byte) bi; - if (customIonizables.containsKey(b)) { - Ionizable ion; + final short mark = readMark(in); + if (customIonizables.containsKey(mark)) { + Ionizable ionObj; + try { - ion = ((Ionizable) customIonizables.get(b).newInstance()); - } catch (final InstantiationException e) { - throw new IonException("Cound not instantiate " + customIonizables.get(b).getSimpleName(), e); - } catch (final IllegalAccessException e) { - throw new IonException("Cound not instantiate " + customIonizables.get(b).getSimpleName(), e); + ionObj = ((Ionizable) customIonizables.get(mark).newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + throw new IOException("Cound not instantiate: " + Log.str(customIonizables.get(mark)), e); } - ion.ionRead(in); - return ion; + + ionObj.loadFrom(in); + return ionObj; } - switch (b) { - case IonMarks.BOOLEAN: - return BinaryUtils.readBoolean(in); - case IonMarks.BYTE: - return BinaryUtils.readByte(in); - case IonMarks.CHAR: - return BinaryUtils.readChar(in); - case IonMarks.SHORT: - return BinaryUtils.readShort(in); - case IonMarks.INT: - return BinaryUtils.readInt(in); - case IonMarks.LONG: - return BinaryUtils.readLong(in); - case IonMarks.FLOAT: - return BinaryUtils.readFloat(in); - case IonMarks.DOUBLE: - return BinaryUtils.readDouble(in); - case IonMarks.STRING: - final String s = BinaryUtils.readString(in); + switch (mark) { + case Ion.NULL: + return null; + + case Ion.BOOLEAN: + return readBoolean(in); + + case Ion.BYTE: + return readByte(in); + + case Ion.CHAR: + return readChar(in); + + case Ion.SHORT: + return readShort(in); + + case Ion.INT: + return readInt(in); + + case Ion.LONG: + return readLong(in); + + case Ion.FLOAT: + return readFloat(in); + + case Ion.DOUBLE: + return readDouble(in); + + case Ion.STRING: + final String s = readString(in); return s; default: - throw new IonException("Invalid Ion mark " + Integer.toHexString(bi)); + throw new IOException("Invalid Ion mark: " + mark); } } catch (final IOException e) { - throw new IonException("Error loading ION file: ", e); + throw new IOException("Error loading ION file: ", e); } } + public static void expect(InputStream in, short mark) throws IOException + { + if (readMark(in) != mark) throw new IOException("Unexpected mark in ION stream."); + } + + + public static short readMark(InputStream in) throws IOException + { + return readShort(in); + } + + + public static void writeMark(OutputStream out, short mark) throws IOException + { + writeShort(out, mark); + } + + /** * Write single ionizable or primitive object to output stream * * @param out output stream * @param obj stored object - * @throws IonException + * @throws IOException */ - public static void writeObject(OutputStream out, Object obj) throws IonException + public static void writeObject(OutputStream out, Object obj) throws IOException { try { if (obj instanceof Ionizable) { - out.write(((Ionizable) obj).ionMark()); - ((Ionizable) obj).ionWrite(out); + writeMark(out, ((Ionizable) obj).getIonMark()); + ((Ionizable) obj).saveTo(out); return; } if (obj instanceof Boolean) { - out.write(IonMarks.BOOLEAN); - BinaryUtils.writeBoolean(out, (Boolean) obj); + writeMark(out, Ion.BOOLEAN); + writeBoolean(out, (Boolean) obj); return; } if (obj instanceof Byte) { - out.write(IonMarks.BYTE); - BinaryUtils.writeByte(out, (Byte) obj); + writeMark(out, Ion.BYTE); + writeByte(out, (Byte) obj); return; } if (obj instanceof Character) { - out.write(IonMarks.CHAR); - BinaryUtils.writeChar(out, (Character) obj); + writeMark(out, Ion.CHAR); + writeChar(out, (Character) obj); return; } if (obj instanceof Short) { - out.write(IonMarks.SHORT); - BinaryUtils.writeShort(out, (Short) obj); + writeMark(out, Ion.SHORT); + writeShort(out, (Short) obj); return; } if (obj instanceof Integer) { - out.write(IonMarks.INT); - BinaryUtils.writeInt(out, (Integer) obj); + writeMark(out, Ion.INT); + writeInt(out, (Integer) obj); return; } if (obj instanceof Long) { - out.write(IonMarks.LONG); - BinaryUtils.writeLong(out, (Long) obj); + writeMark(out, Ion.LONG); + writeLong(out, (Long) obj); return; } if (obj instanceof Float) { - out.write(IonMarks.FLOAT); - BinaryUtils.writeFloat(out, (Float) obj); + writeMark(out, Ion.FLOAT); + writeFloat(out, (Float) obj); return; } if (obj instanceof Double) { - out.write(IonMarks.DOUBLE); - BinaryUtils.writeDouble(out, (Double) obj); + writeMark(out, Ion.DOUBLE); + writeDouble(out, (Double) obj); return; } if (obj instanceof String) { - out.write(IonMarks.STRING); - BinaryUtils.writeString(out, (String) obj); + writeMark(out, Ion.STRING); + writeString(out, (String) obj); return; } - throw new IonException(Calc.cname(obj) + " can't be stored in Ion storage."); + throw new IOException("Object " + Log.str(obj) + " could not be be ionized."); } catch (final IOException e) { - throw new IonException("Could not store " + obj, e); + throw new IOException("Could not store: " + obj, e); + } + } + + + private static byte[] getBytesBool(boolean bool) + { + return new byte[] { (byte) (bool ? 1 : 0) }; + } + + + private static byte[] getBytesByte(byte num) + { + return new byte[] { num }; + } + + + private static byte[] getBytesChar(char num) + { + synchronized (bc) { + bc.clear(); + bc.putChar(num); + return bc.array(); + } + } + + + private static byte[] getBytesShort(short num) + { + synchronized (bs) { + bs.clear(); + bs.putShort(num); + return bs.array(); + } + } + + + private static byte[] getBytesInt(int num) + { + synchronized (bi) { + bi.clear(); + bi.putInt(num); + return bi.array(); + } + } + + + private static byte[] getBytesLong(long num) + { + synchronized (bl) { + bl.clear(); + bl.putLong(num); + return bl.array(); + } + } + + + private static byte[] getBytesFloat(float num) + { + synchronized (bf) { + bf.clear(); + bf.putFloat(num); + return bf.array(); + } + } + + + private static byte[] getBytesDouble(double num) + { + synchronized (bd) { + bd.clear(); + bd.putDouble(num); + return bd.array(); + } + } + + + private static byte[] getBytesString(String str) + { + final char[] chars = str.toCharArray(); + + final ByteBuffer bstr = ByteBuffer.allocate((Character.SIZE / 8) * chars.length + (Character.SIZE / 8)); + for (final char c : chars) { + bstr.putChar(c); + } + + bstr.putChar((char) 0); + + return bstr.array(); + } + + + public static void writeBoolean(OutputStream out, boolean bool) throws IOException + { + out.write(getBytesBool(bool)); + } + + + public static void writeByte(OutputStream out, byte b) throws IOException + { + out.write(getBytesByte(b)); + } + + + public static void writeChar(OutputStream out, char c) throws IOException + { + out.write(getBytesChar(c)); + } + + + public static void writeShort(OutputStream out, short s) throws IOException + { + out.write(getBytesShort(s)); + } + + + public static void writeInt(OutputStream out, int i) throws IOException + { + out.write(getBytesInt(i)); + } + + + public static void writeLong(OutputStream out, long l) throws IOException + { + out.write(getBytesLong(l)); + } + + + public static void writeFloat(OutputStream out, float f) throws IOException + { + out.write(getBytesFloat(f)); + } + + + public static void writeDouble(OutputStream out, double d) throws IOException + { + out.write(getBytesDouble(d)); + } + + + public static void writeString(OutputStream out, String str) throws IOException + { + out.write(getBytesString(str)); + } + + + public static boolean readBoolean(InputStream in) throws IOException + { + return readByte(in) > 0; + } + + + public static byte readByte(InputStream in) throws IOException + { + int b = in.read(); + if (-1 == b) throw new IOException("End of stream."); + return (byte) b; + } + + + public static char readChar(InputStream in) throws IOException + { + synchronized (ac) { + if (-1 == in.read(ac, 0, ac.length)) throw new IOException("End of stream."); + final ByteBuffer buf = ByteBuffer.wrap(ac); + return buf.getChar(); + } + } + + + public static short readShort(InputStream in) throws IOException + { + synchronized (as) { + if (-1 == in.read(as, 0, as.length)) throw new IOException("End of stream."); + final ByteBuffer buf = ByteBuffer.wrap(as); + return buf.getShort(); + } + } + + + public static long readLong(InputStream in) throws IOException + { + synchronized (al) { + if (-1 == in.read(al, 0, al.length)) throw new IOException("End of stream."); + final ByteBuffer buf = ByteBuffer.wrap(al); + return buf.getLong(); + } + } + + + public static int readInt(InputStream in) throws IOException + { + synchronized (ai) { + if (-1 == in.read(ai, 0, ai.length)) throw new IOException("End of stream."); + final ByteBuffer buf = ByteBuffer.wrap(ai); + return buf.getInt(); + } + } + + + public static float readFloat(InputStream in) throws IOException + { + synchronized (af) { + if (-1 == in.read(af, 0, af.length)) throw new IOException("End of stream."); + final ByteBuffer buf = ByteBuffer.wrap(af); + return buf.getFloat(); + } + } + + + public static double readDouble(InputStream in) throws IOException + { + synchronized (ad) { + if (-1 == in.read(ad, 0, ad.length)) throw new IOException("End of stream."); + final ByteBuffer buf = ByteBuffer.wrap(ad); + return buf.getDouble(); + } + } + + + public static String readString(InputStream in) throws IOException + { + String s = ""; + char c; + while ((c = readChar(in)) > 0) { + s += c; } + return s; } } diff --git a/src/mightypork/util/files/ion/IonDataBundle.java b/src/mightypork/util/files/ion/IonDataBundle.java new file mode 100644 index 0000000..4e262ca --- /dev/null +++ b/src/mightypork/util/files/ion/IonDataBundle.java @@ -0,0 +1,157 @@ +package mightypork.util.files.ion; + + + +/** + * Ionizable HashMap + * + * @author MightyPork + */ +public class IonDataBundle extends IonMap { + + public boolean getBoolean(String key) + { + return (Boolean) get(key); + } + + + public boolean getBool(String key) + { + return (Boolean) get(key); + } + + + public byte getByte(String key) + { + return (Byte) get(key); + } + + + public char getChar(String key) + { + return (Character) get(key); + } + + + public short getShort(String key) + { + return (Short) get(key); + } + + + public int getInt(String key) + { + return (Integer) get(key); + } + + + public long getLong(String key) + { + return (Long) get(key); + } + + + public float getFloat(String key) + { + return (Float) get(key); + } + + + public double getDouble(String key) + { + return (Double) get(key); + } + + + public String getString(String key) + { + return (String) get(key); + } + + + @Override + public Object get(Object arg0) + { + return super.get(arg0); + } + + + public void putBoolean(String key, boolean num) + { + put(key, num); + } + + + public void putBool(String key, boolean num) + { + put(key, num); + } + + + public void putByte(String key, int num) + { + put(key, (byte) num); + } + + + public void putChar(String key, char num) + { + put(key, num); + } + + + public void putCharacter(String key, char num) + { + put(key, num); + } + + + public void putShort(String key, int num) + { + put(key, num); + } + + + public void putInt(String key, int num) + { + put(key, num); + } + + + public void putInteger(String key, int num) + { + put(key, num); + } + + + public void putLong(String key, long num) + { + put(key, num); + } + + + public void putFloat(String key, double num) + { + put(key, (float) num); + } + + + public void putDouble(String key, double num) + { + put(key, num); + } + + + public void putString(String key, String num) + { + put(key, num); + } + + + @Override + public short getIonMark() + { + return Ion.DATA_BUNDLE; + } + +} diff --git a/src/mightypork/util/files/ion/IonDataList.java b/src/mightypork/util/files/ion/IonDataList.java new file mode 100644 index 0000000..d15ce55 --- /dev/null +++ b/src/mightypork/util/files/ion/IonDataList.java @@ -0,0 +1,169 @@ +package mightypork.util.files.ion; + +import java.io.IOException; + + +/** + * Ionizable Arraylist + * + * @author MightyPork + */ +public class IonDataList extends IonList { + + public Ionizable getIonizable(int index) throws IOException + { + return (Ionizable) getOfType(index, Ionizable.class); + } + + + public boolean getBoolean(int index) throws IOException + { + return (Boolean) getOfType(index, Boolean.class); + } + + + public byte getByte(int index) throws IOException + { + return (Byte) getOfType(index, Byte.class); + } + + + public char getChar(int index) throws IOException + { + return (Character) getOfType(index, Character.class); + } + + + public short getShort(int index) throws IOException + { + return (Short) getOfType(index, Short.class); + } + + + public int getInt(int index) throws IOException + { + return (Integer) getOfType(index, Integer.class); + } + + + public long getLong(int index) throws IOException + { + return (Long) getOfType(index, Long.class); + } + + + public float getFloat(int index) throws IOException + { + return (Float) getOfType(index, Float.class); + } + + + public double getDouble(int index) throws IOException + { + return (Double) getOfType(index, Double.class); + } + + + public String getString(int index) throws IOException + { + return (String) getOfType(index, String.class); + } + + + public void addIonizable(Ionizable o) throws IOException + { + assertNotNull(o); + add(o); + } + + + public void addBoolean(boolean o) throws IOException + { + assertNotNull(o); + add(o); + } + + + public void addByte(byte o) throws IOException + { + assertNotNull(o); + add(o); + } + + + public void addChar(char o) throws IOException + { + assertNotNull(o); + add(o); + } + + + public void addShort(short o) throws IOException + { + assertNotNull(o); + add(o); + } + + + public void addInt(int o) throws IOException + { + assertNotNull(o); + add(o); + } + + + public void addLong(long o) throws IOException + { + assertNotNull(o); + add(o); + } + + + public void addFloat(float o) throws IOException + { + assertNotNull(o); + add(o); + } + + + public void addDouble(double o) throws IOException + { + assertNotNull(o); + add(o); + } + + + public void addString(String o) throws IOException + { + assertNotNull(o); + add(o); + } + + + public Object getOfType(int index, Class type) throws IOException + { + try { + final Object o = super.get(index); + if (o == null || !o.getClass().isAssignableFrom(type)) { + throw new IOException("Incorrect object type"); + } + return o; + } catch (final IndexOutOfBoundsException e) { + throw new IOException("Out of bounds"); + } + } + + + private static void assertNotNull(Object o) throws IOException + { + if (o == null) throw new IOException("Cannot store null"); + } + + + @Override + public short getIonMark() + { + return Ion.DATA_LIST; + } + +} diff --git a/src/mightypork/util/files/ion/IonException.java b/src/mightypork/util/files/ion/IonException.java deleted file mode 100644 index cc14ea2..0000000 --- a/src/mightypork/util/files/ion/IonException.java +++ /dev/null @@ -1,25 +0,0 @@ -package mightypork.util.files.ion; - - -public class IonException extends Exception { - - public IonException() { - super(); - } - - - public IonException(String message, Throwable cause) { - super(message, cause); - } - - - public IonException(String message) { - super(message); - } - - - public IonException(Throwable cause) { - super(cause); - } - -} diff --git a/src/mightypork/util/files/ion/IonList.java b/src/mightypork/util/files/ion/IonList.java index 2517407..e8dca66 100644 --- a/src/mightypork/util/files/ion/IonList.java +++ b/src/mightypork/util/files/ion/IonList.java @@ -1,157 +1,80 @@ package mightypork.util.files.ion; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; + + /** * Ionizable Arraylist * * @author MightyPork + * @param */ -public class IonList extends AbstractIonList { - - public Ionizable getIonizable(int index) throws IonException - { - return (Ionizable) getCheckType(index, Ionizable.class); - } - - - public boolean getBoolean(int index) throws IonException - { - return (Boolean) getCheckType(index, Boolean.class); - } - - - public byte getByte(int index) throws IonException - { - return (Byte) getCheckType(index, Byte.class); - } - - - public char getChar(int index) throws IonException - { - return (Character) getCheckType(index, Character.class); - } - - - public short getShort(int index) throws IonException - { - return (Short) getCheckType(index, Short.class); - } - +public abstract class IonList extends ArrayList implements Ionizable { - public int getInt(int index) throws IonException - { - return (Integer) getCheckType(index, Integer.class); - } - - - public long getLong(int index) throws IonException - { - return (Long) getCheckType(index, Long.class); - } - - - public float getFloat(int index) throws IonException - { - return (Float) getCheckType(index, Float.class); - } - - - public double getDouble(int index) throws IonException - { - return (Double) getCheckType(index, Double.class); - } - - - public String getString(int index) throws IonException - { - return (String) getCheckType(index, String.class); - } - - - public void addIonizable(Ionizable o) throws IonException - { - assertNotNull(o); - } - - - public void addBoolean(boolean o) throws IonException - { - assertNotNull(o); - } - - - public void addByte(byte o) throws IonException - { - assertNotNull(o); - } - - - public void addChar(char o) throws IonException - { - assertNotNull(o); - } - - - public void addShort(short o) throws IonException - { - assertNotNull(o); - } - - - public void addInt(int o) throws IonException - { - assertNotNull(o); - } - - - public void addLong(long o) throws IonException - { - assertNotNull(o); - } - - - public void addFloat(float o) throws IonException - { - assertNotNull(o); - } - - - public void addDouble(double o) throws IonException - { - assertNotNull(o); - } - - - public void addString(String o) throws IonException + @Override + public void loadFrom(InputStream in) throws IOException { - assertNotNull(o); + try { + while (true) { + final short mark = Ion.readMark(in); + + if (mark == Ion.ENTRY) { + final T value = (T) Ion.readObject(in); + add(value); + } else if (mark == Ion.END) { + break; + } else { + throw new IOException("Unexpected mark in IonList: " + mark); + } + } + readCustomData(in); + } catch (final IOException e) { + throw new IOException("Error reading ion list", e); + } } - public Object getCheckType(int index, Class type) throws IonException + @Override + public void saveTo(OutputStream out) throws IOException { try { - final Object o = super.get(index); - if (o == null || !o.getClass().isAssignableFrom(type)) { - throw new IonException("Incorrect object type"); + for (final T entry : this) { + Ion.writeMark(out, Ion.ENTRY); + Ion.writeObject(out, entry); } - return o; - } catch (final IndexOutOfBoundsException e) { - throw new IonException("Out of bounds"); + Ion.writeMark(out, Ion.END); + writeCustomData(out); + } catch (final IOException e) { + throw new IOException("Error reading ion map", e); } } - private static void assertNotNull(Object o) throws IonException + /** + * Read custom data of this AbstractIonList implementation + * + * @param in input stream + */ + public void readCustomData(InputStream in) { - if (o == null) throw new IonException("Cannot store null"); } - @Override - public byte ionMark() + /** + * Write custom data of this AbstractIonList implementation + * + * @param out output stream + */ + public void writeCustomData(OutputStream out) { - return IonMarks.LIST; } + + @Override + public abstract short getIonMark(); + } diff --git a/src/mightypork/util/files/ion/IonMap.java b/src/mightypork/util/files/ion/IonMap.java index 8769655..9a33c46 100644 --- a/src/mightypork/util/files/ion/IonMap.java +++ b/src/mightypork/util/files/ion/IonMap.java @@ -1,156 +1,104 @@ package mightypork.util.files.ion; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.LinkedHashMap; + +import mightypork.util.annotations.DefaultImpl; + + /** * Ionizable HashMap * * @author MightyPork + * @param key + * @param value */ -public class IonMap extends AbstractIonMap { - - public boolean getBoolean(String key) - { - return (Boolean) get(key); - } - - - public boolean getBool(String key) - { - return (Boolean) get(key); - } - - - public byte getByte(String key) - { - return (Byte) get(key); - } - - - public char getChar(String key) - { - return (Character) get(key); - } - - - public short getShort(String key) - { - return (Short) get(key); - } - - - public int getInt(String key) - { - return (Integer) get(key); - } - - - public long getLong(String key) - { - return (Long) get(key); - } - - - public float getFloat(String key) - { - return (Float) get(key); - } - - - public double getDouble(String key) - { - return (Double) get(key); - } - - - public String getString(String key) - { - return (String) get(key); - } - +public abstract class IonMap extends LinkedHashMap implements Ionizable { @Override - public Object get(Object arg0) + public V get(Object key) { - return super.get(arg0); + return super.get(key); } - public void putBoolean(String key, boolean num) - { - put(key, num); - } - - - public void putBool(String key, boolean num) - { - put(key, num); - } - - - public void putByte(String key, int num) - { - put(key, (byte) num); - } - - - public void putChar(String key, char num) - { - put(key, num); - } - - - public void putCharacter(String key, char num) - { - put(key, num); - } - - - public void putShort(String key, int num) - { - put(key, num); - } - - - public void putInt(String key, int num) - { - put(key, num); - } - - - public void putInteger(String key, int num) + @Override + public V put(K key, V value) { - put(key, num); + return super.put(key, value); } - public void putLong(String key, long num) - { - put(key, num); + @Override + public void loadFrom(InputStream in) throws IOException + { + try { + while (true) { + final short mark = Ion.readMark(in); + if (mark == Ion.ENTRY) { + final K key = (K) Ion.readObject(in); + final V value = (V) Ion.readObject(in); + put(key, value); + + } else if (mark == Ion.END) { + break; + } else { + throw new RuntimeException("Unexpected mark in IonMap: " + mark); + } + } + readCustomData(in); + } catch (final IOException | ClassCastException e) { + throw new IOException("Error reading ion map", e); + } } - public void putFloat(String key, double num) + @Override + public void saveTo(OutputStream out) throws IOException { - put(key, (float) num); + try { + for (final java.util.Map.Entry entry : entrySet()) { + Ion.writeMark(out, Ion.ENTRY); + Ion.writeObject(out, entry.getKey()); + Ion.writeObject(out, entry.getValue()); + } + Ion.writeMark(out, Ion.END); + writeCustomData(out); + } catch (final IOException e) { + throw new IOException("Error writing ion map", e); + } } - public void putDouble(String key, double num) + /** + * Read custom data of this AbstractIonMap implementation + * + * @param in input stream + */ + @DefaultImpl + public void readCustomData(InputStream in) { - put(key, num); } - public void putString(String key, String num) + /** + * Write custom data of this AbstractIonMap implementation + * + * @param out output stream + */ + @DefaultImpl + public void writeCustomData(OutputStream out) { - put(key, num); } @Override - public byte ionMark() + public short getIonMark() { - return IonMarks.MAP; + return Ion.DATA_BUNDLE; } } diff --git a/src/mightypork/util/files/ion/IonMarks.java b/src/mightypork/util/files/ion/IonMarks.java deleted file mode 100644 index 9195f96..0000000 --- a/src/mightypork/util/files/ion/IonMarks.java +++ /dev/null @@ -1,57 +0,0 @@ -package mightypork.util.files.ion; - - -/** - * Byte marks used to structure data in Ion files. - * - * @author MightyPork - */ -public class IonMarks { - - /** Null value */ - public static final byte NULL = 0; - - /** Boolean value */ - public static final byte BOOLEAN = 1; - - /** Byte value */ - public static final byte BYTE = 2; - - /** Character value */ - public static final byte CHAR = 3; - - /** Short value */ - public static final byte SHORT = 4; - - /** Integer value */ - public static final byte INT = 5; - - /** Long value */ - public static final byte LONG = 6; - - /** Float value */ - public static final byte FLOAT = 7; - - /** Double value */ - public static final byte DOUBLE = 8; - - /** String value */ - public static final byte STRING = 9; - - /** List value (begin) - contains entries, ends with END */ - public static final byte LIST = 10; - - /** Map value (begin) - contains entries, ends with END */ - public static final byte MAP = 11; - - /** - * List / Map entry
- * In list directly followed by entry value. In map followed by (string) key - * and the entry value. - */ - public static final byte ENTRY = 12; - - /** End of List / Map */ - public static final byte END = 13; - -} diff --git a/src/mightypork/util/files/ion/Ionizable.java b/src/mightypork/util/files/ion/Ionizable.java index 89f5298..e7bee44 100644 --- a/src/mightypork/util/files/ion/Ionizable.java +++ b/src/mightypork/util/files/ion/Ionizable.java @@ -1,6 +1,7 @@ package mightypork.util.files.ion; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -19,19 +20,19 @@ public interface Ionizable { * reading right after it. * * @param in input stream - * @throws IonException + * @throws IOException */ - public void ionRead(InputStream in) throws IonException; + void loadFrom(InputStream in) throws IOException; /** - * Store data to output stream. mark has already been written, begin right + * Store data to output stream. Mark has already been written, begin right * after it. * * @param out Output stream - * @throws IonException + * @throws IOException */ - public void ionWrite(OutputStream out) throws IonException; + void saveTo(OutputStream out) throws IOException; /** @@ -39,5 +40,5 @@ public interface Ionizable { * * @return Ion mark byte. */ - public byte ionMark(); + public short getIonMark(); } diff --git a/src/mightypork/util/files/ion/IonizableOptional.java b/src/mightypork/util/files/ion/IonizableOptional.java deleted file mode 100644 index cd47ecd..0000000 --- a/src/mightypork/util/files/ion/IonizableOptional.java +++ /dev/null @@ -1,17 +0,0 @@ -package mightypork.util.files.ion; - - -/** - * Optional ionizable - * - * @author MightyPork - */ -public interface IonizableOptional extends Ionizable { - - /** - * Get if this ionizable should be saved to a list - * - * @return should save - */ - public boolean ionShouldSave(); -}