package mightypork.utils.ion; import java.io.Closeable; import java.io.DataInput; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import mightypork.utils.exceptions.CorruptDataException; /** * Ion input stream * * @author Ondřej Hruška (MightyPork) */ public class IonInput implements Closeable { private final DataInput in; private final InputStream stream; @SuppressWarnings("resource") public IonInput(File inFile) throws FileNotFoundException { this(new FileInputStream(inFile)); } public IonInput(InputStream in) { this.stream = in; this.in = new DataInputStream(in); } /** * Read int 0-255. Suitable when the int was written using * writeIntByte() method. * * @return int * @throws IOException */ public int readIntByte() throws IOException { return in.readUnsignedByte(); } /** * Read an int 0-65535. Suitable when the int was written using * writeIntShort() method. * * @return int * @throws IOException */ public int readIntShort() throws IOException { return in.readUnsignedShort(); } public boolean readBoolean() throws IOException { return in.readBoolean(); } public byte readByte() throws IOException { return in.readByte(); } public short readShort() throws IOException { return in.readShort(); } public char readChar() throws IOException { return in.readChar(); } public int readInt() throws IOException { return in.readInt(); } public long readLong() throws IOException { return in.readLong(); } public float readFloat() throws IOException { return in.readFloat(); } public double readDouble() throws IOException { return in.readDouble(); } public String readString() throws IOException { return in.readUTF(); } public boolean[] readBooleans() throws IOException { final int length = readLength(); final boolean[] arr = new boolean[length]; for (int i = 0; i < length; i++) { arr[i] = in.readBoolean(); } return arr; } public byte[] readBytes() throws IOException { final int length = readLength(); final byte[] arr = new byte[length]; for (int i = 0; i < length; i++) { arr[i] = in.readByte(); } return arr; } public char[] readChars() throws IOException { final int length = readLength(); final char[] arr = new char[length]; for (int i = 0; i < length; i++) { arr[i] = in.readChar(); } return arr; } public short[] readShorts() throws IOException { final int length = readLength(); final short[] arr = new short[length]; for (int i = 0; i < length; i++) { arr[i] = in.readShort(); } return arr; } public int[] readInts() throws IOException { final int length = readLength(); final int[] arr = new int[length]; for (int i = 0; i < length; i++) { arr[i] = in.readInt(); } return arr; } public long[] readLongs() throws IOException { final int length = readLength(); final long[] arr = new long[length]; for (int i = 0; i < length; i++) { arr[i] = in.readLong(); } return arr; } public float[] readFloats() throws IOException { final int length = readLength(); final float[] arr = new float[length]; for (int i = 0; i < length; i++) { arr[i] = in.readFloat(); } return arr; } public double[] readDoubles() throws IOException { final int length = readLength(); final double[] arr = new double[length]; for (int i = 0; i < length; i++) { arr[i] = in.readDouble(); } return arr; } public String[] readStrings() throws IOException { final int length = readLength(); final String[] arr = new String[length]; for (int i = 0; i < length; i++) { arr[i] = in.readUTF(); } return arr; } public Object[] readObjects() throws IOException { final int length = readLength(); final Object[] arr = new Object[length]; for (int i = 0; i < length; i++) { arr[i] = readObject(); } return arr; } /** * Read bundle without a mark */ public IonDataBundle readBundle() throws IOException { final IonDataBundle ib = new IonDataBundle(); ib.load(this); return ib; } /** * Read bundle without a mark, load into a provided one */ public void readBundle(IonDataBundle filled) throws IOException { filled.clear(); filled.load(this); } private int readMark() throws IOException { return readIntByte(); } private int readLength() throws IOException { return readInt(); } /** *

* Read object based on mark; if null mark is found, returns default value. *

*

* If, however, an object of invalid or different type is found, an * exception will be thrown. *

* * @param def default value. * @return the loaded object * @throws CorruptDataException */ @SuppressWarnings("unchecked") public T readObject(T def) throws CorruptDataException { try { final Object o = readObject(); return (T) (o == null ? def : o); } catch (final Exception e) { throw new CorruptDataException("Could not load object.", e); } } /** * Read single object, preceded by a mark. * * @return the loaded object * @throws IOException */ public Object readObject() throws IOException { final int mark = readMark(); try { if (Ion.isMarkForBinary(mark)) { IonBinary loaded; loaded = (IonBinary) Ion.getClassForMark(mark).newInstance(); loaded.load(this); return loaded; } if (Ion.isMarkForBundled(mark)) { IonBundled loaded; loaded = (IonBundled) Ion.getClassForMark(mark).newInstance(); final IonDataBundle ib = readBundle(); loaded.load(ib); return loaded; } if (Ion.isMarkForIndirectBundled(mark)) { final IonizerBundled ionizer = Ion.getIonizerBundledForClass(Ion.getClassForMark(mark)); return ionizer.load(readBundle()); } if (Ion.isMarkForIndirectBinary(mark)) { final IonizerBinary ionizer = Ion.getIonizerBinaryForClass(Ion.getClassForMark(mark)); return ionizer.load(this); } } catch (final Exception e) { throw new RuntimeException("Could not load object for mark: " + mark, e); } switch (mark) { case Ion.NULL: return null; case Ion.BOOLEAN: return readBoolean(); case Ion.BYTE: return readByte(); case Ion.CHAR: return readChar(); case Ion.SHORT: return readShort(); case Ion.INT: return readInt(); case Ion.LONG: return readLong(); case Ion.FLOAT: return readFloat(); case Ion.DOUBLE: return readDouble(); case Ion.STRING: return readString(); case Ion.BOOLEAN_ARRAY: return readBooleans(); case Ion.BYTE_ARRAY: return readBytes(); case Ion.CHAR_ARRAY: return readChars(); case Ion.SHORT_ARRAY: return readShorts(); case Ion.INT_ARRAY: return readInts(); case Ion.LONG_ARRAY: return readLongs(); case Ion.FLOAT_ARRAY: return readFloats(); case Ion.DOUBLE_ARRAY: return readDoubles(); case Ion.STRING_ARRAY: return readStrings(); case Ion.OBJECT_ARRAY: return readObjects(); case Ion.MAP: return readMap(); case Ion.SEQUENCE: return readSequence(); default: throw new CorruptDataException("Invalid mark: " + mark); } } /** * Reads mark and returns true if the mark is ENTRY, false if the mark is * END. Throws an exception otherwise. * * @return mark was ENTRY * @throws IOException when the mark is neither ENTRY or END. */ public boolean hasNextEntry() throws IOException { final int mark = readMark(); if (mark == Ion.ENTRY) return true; if (mark == Ion.END) return false; throw new CorruptDataException("Unexpected mark in sequence: " + mark); } /** * Read a sequence of elements into an ArrayList * * @return the collection * @throws IOException */ public Collection readSequence() throws IOException { return readSequence(new ArrayList()); } /** * Load entries into a collection. The collection is cleaned first. * * @param filled collection to populate * @return the collection * @throws IOException */ @SuppressWarnings("unchecked") public Collection readSequence(Collection filled) throws IOException { try { filled.clear(); while (hasNextEntry()) { filled.add((T) readObject()); } return filled; } catch (final ClassCastException e) { throw new CorruptDataException("Unexpected element type in sequence.", e); } } /** * Read element pairs into a HashMap * * @return the map * @throws IOException */ public Map readMap() throws IOException { return readMap(new HashMap()); } /** * Load data into a map. The map is cleaned first. * * @param filled filled map * @return the map * @throws IOException */ @SuppressWarnings("unchecked") public Map readMap(Map filled) throws IOException { try { filled.clear(); while (hasNextEntry()) { final K key = (K) readObject(); final V value = (V) readObject(); filled.put(key, value); } return filled; } catch (final ClassCastException e) { throw new CorruptDataException("Unexpected element type in map.", e); } } @Override public void close() throws IOException { stream.close(); } }