From 86e47c87d560409b9185523a5b6d7f7445570008 Mon Sep 17 00:00:00 2001 From: kiip1 <25848425+kiip1@users.noreply.github.com> Date: Sat, 28 Jan 2023 21:44:26 +0100 Subject: [PATCH 1/3] Cleanup --- .../java/ch/njol/yggdrasil/ClassResolver.java | 11 +- .../DefaultYggdrasilInputStream.java | 125 +++---- .../DefaultYggdrasilOutputStream.java | 154 ++++---- .../java/ch/njol/yggdrasil/FieldHandler.java | 22 +- src/main/java/ch/njol/yggdrasil/Fields.java | 296 ++++++++------- .../java/ch/njol/yggdrasil/JRESerializer.java | 104 +++--- .../java/ch/njol/yggdrasil/PseudoEnum.java | 88 +++-- .../njol/yggdrasil/SimpleClassResolver.java | 23 +- src/main/java/ch/njol/yggdrasil/Tag.java | 71 ++-- .../java/ch/njol/yggdrasil/Yggdrasil.java | 309 +++++++--------- .../ch/njol/yggdrasil/YggdrasilException.java | 14 +- .../java/ch/njol/yggdrasil/YggdrasilID.java | 4 +- .../njol/yggdrasil/YggdrasilInputStream.java | 201 ++++------- .../njol/yggdrasil/YggdrasilOutputStream.java | 205 +++++------ .../njol/yggdrasil/YggdrasilSerializable.java | 64 ++-- .../njol/yggdrasil/YggdrasilSerializer.java | 49 ++- .../njol/yggdrasil/util/JREFieldHandler.java | 99 +++-- .../njol/yggdrasil/xml/YggXMLInputStream.java | 322 ----------------- .../yggdrasil/xml/YggXMLOutputStream.java | 339 ------------------ .../ch/njol/yggdrasil/xml/package-info.java | 27 -- 20 files changed, 815 insertions(+), 1712 deletions(-) delete mode 100644 src/main/java/ch/njol/yggdrasil/xml/YggXMLInputStream.java delete mode 100644 src/main/java/ch/njol/yggdrasil/xml/YggXMLOutputStream.java delete mode 100644 src/main/java/ch/njol/yggdrasil/xml/package-info.java diff --git a/src/main/java/ch/njol/yggdrasil/ClassResolver.java b/src/main/java/ch/njol/yggdrasil/ClassResolver.java index bf0c22e29ed..e8cea60120e 100644 --- a/src/main/java/ch/njol/yggdrasil/ClassResolver.java +++ b/src/main/java/ch/njol/yggdrasil/ClassResolver.java @@ -29,17 +29,16 @@ public interface ClassResolver { * @return The Class object that represents data with the given ID, or null if the ID does not belong to the implementor */ @Nullable - public Class getClass(String id); + Class getClass(String id); /** - * Gets an ID for a Class. The ID is used to identify the type of a saved object. + * Gets an ID for a Class. The ID is used to identify the type of saved object. *

- * // TODO make sure that it's unique - * - * @param c The class to get the ID of + * @param clazz The class to get the ID of * @return The ID of the given class, or null if this is not a class of the implementor */ + // TODO make sure that it's unique @Nullable - public String getID(Class c); + String getID(Class clazz); } diff --git a/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java b/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java index 2bf434bf364..c2043c3e310 100644 --- a/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java +++ b/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java @@ -18,102 +18,89 @@ */ package ch.njol.yggdrasil; -import static ch.njol.yggdrasil.Tag.T_ARRAY; -import static ch.njol.yggdrasil.Tag.T_REFERENCE; - import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.StreamCorruptedException; import java.lang.reflect.Array; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import org.eclipse.jdt.annotation.NonNull; +import static ch.njol.yggdrasil.Tag.T_ARRAY; +import static ch.njol.yggdrasil.Tag.T_REFERENCE; //Naming conventions: // x(): read info & data (e.g. content type, contents) [i.e. no tag] // _x(): read data only (e.g. contents) - public final class DefaultYggdrasilInputStream extends YggdrasilInputStream { - @SuppressWarnings("null") - private final static Charset UTF_8 = Charset.forName("UTF-8"); - + private final InputStream in; private final short version; - final InputStream in; - - public DefaultYggdrasilInputStream(final Yggdrasil y, final InputStream in) throws IOException { - super(y); + public DefaultYggdrasilInputStream(Yggdrasil yggdrasil, InputStream in) throws IOException { + super(yggdrasil); this.in = in; - final int m = readInt(); - if (m != Yggdrasil.MAGIC_NUMBER) + if (readInt() != Yggdrasil.MAGIC_NUMBER) throw new StreamCorruptedException("Not an Yggdrasil stream"); version = readShort(); if (version <= 0 || version > Yggdrasil.LATEST_VERSION) throw new StreamCorruptedException("Input was saved using a later version of Yggdrasil"); } - // private - /** * @throws EOFException If the end of the stream is reached */ private int read() throws IOException { - final int b = in.read(); + int b = in.read(); if (b < 0) throw new EOFException(); return b; } - private void readFully(final byte[] buf) throws IOException { - readFully(buf, 0, buf.length); + private void readFully(byte[] buf) throws IOException { + readFully(buf, buf.length); } - private void readFully(final byte[] buf, int off, final int len) throws IOException { - int l = len; - while (l > 0) { - final int n = in.read(buf, off, l); + private void readFully(byte[] buf, int startLength) throws IOException { + int offset = 0; + int length = startLength; + while (length > 0) { + int n = in.read(buf, offset, length); if (n < 0) - throw new EOFException("Expected " + len + " bytes, but could only read " + (len - l)); - off += n; - l -= n; + throw new EOFException("Expected " + startLength + " bytes, but could only read " + (startLength - length)); + offset += n; + length -= n; } } private final List readShortStrings = new ArrayList<>(); private String readShortString() throws IOException { - final int length = read(); + int length = read(); if (length == (T_REFERENCE.tag & 0xFF)) { - final int i = version <= 1 ? readInt() : readUnsignedInt(); + int i = version <= 1 ? readInt() : readUnsignedInt(); if (i < 0 || i > readShortStrings.size()) throw new StreamCorruptedException("Invalid short string reference " + i); return "" + readShortStrings.get(i); } - final byte[] d = new byte[length]; + byte[] d = new byte[length]; readFully(d); - final String s = new String(d, UTF_8); + String s = new String(d, StandardCharsets.UTF_8); if (length > 4) readShortStrings.add(s); return s; } - // Tag - @Override protected Tag readTag() throws IOException { - final int t = read(); - final Tag tag = Tag.byID(t); + int t = read(); + Tag tag = Tag.byID(t); if (tag == null) throw new StreamCorruptedException("Invalid tag 0x" + Integer.toHexString(t)); return tag; } - // Primitives - private byte readByte() throws IOException { return (byte) read(); } @@ -123,7 +110,7 @@ private short readShort() throws IOException { } private short readUnsignedShort() throws IOException { - final int b = read(); + int b = read(); if ((b & 0x80) != 0) return (short) (b & ~0x80); return (short) (b << 8 | read()); @@ -137,21 +124,17 @@ private int readInt() throws IOException { } private int readUnsignedInt() throws IOException { - final int b = read(); + int b = read(); if ((b & 0x80) != 0) return (b & ~0x80) << 8 | read(); return b << 24 | read() << 16 | read() << 8 | read(); } private long readLong() throws IOException { - return (long) read() << 56 - | (long) read() << 48 - | (long) read() << 40 - | (long) read() << 32 - | (long) read() << 24 - | read() << 16 - | read() << 8 - | read(); + return (long) read() << 56 | (long) read() << 48 + | (long) read() << 40 | (long) read() << 32 + | (long) read() << 24 | (long) read() << 16 + | (long) read() << 8 | read(); } private float readFloat() throws IOException { @@ -167,7 +150,7 @@ private char readChar() throws IOException { } private boolean readBoolean() throws IOException { - final int r = read(); + int r = read(); if (r == 0) return false; else if (r == 1) @@ -176,7 +159,7 @@ else if (r == 1) } @Override - protected Object readPrimitive(final Tag type) throws IOException { + protected Object readPrimitive(Tag type) throws IOException { switch (type) { case T_BYTE: return readByte(); @@ -194,29 +177,24 @@ protected Object readPrimitive(final Tag type) throws IOException { return readChar(); case T_BOOLEAN: return readBoolean(); - //$CASES-OMITTED$ default: throw new YggdrasilException("Internal error; " + type); } } @Override - protected Object readPrimitive_(final Tag type) throws IOException { + protected Object readPrimitive_(Tag type) throws IOException { return readPrimitive(type); } - // String - @Override protected String readString() throws IOException { - final int length = readUnsignedInt(); - final byte[] d = new byte[length]; + int length = readUnsignedInt(); + byte[] d = new byte[length]; readFully(d); - return new String(d, UTF_8); + return new String(d, StandardCharsets.UTF_8); } - // Array - @Override protected Class readArrayComponentType() throws IOException { return readClass(); @@ -227,8 +205,6 @@ protected int readArrayLength() throws IOException { return readUnsignedInt(); } - // Enum - @Override protected Class readEnumType() throws IOException { return yggdrasil.getClass(readShortString()); @@ -239,21 +215,18 @@ protected String readEnumID() throws IOException { return readShortString(); } - // Class - @SuppressWarnings("null") @Override protected Class readClass() throws IOException { Tag type; - int dim = 0; + int dimensions = 0; while ((type = readTag()) == T_ARRAY) - dim++; - @NonNull - Class c; + dimensions++; + Class clazz; switch (type) { case T_OBJECT: case T_ENUM: - c = yggdrasil.getClass(readShortString()); + clazz = yggdrasil.getClass(readShortString()); break; case T_BOOLEAN: case T_BOOLEAN_OBJ: @@ -273,8 +246,8 @@ protected Class readClass() throws IOException { case T_SHORT_OBJ: case T_CLASS: case T_STRING: - c = type.c; - assert c != null; + assert type.type != null; + clazz = type.type; break; case T_NULL: case T_REFERENCE: @@ -283,20 +256,16 @@ protected Class readClass() throws IOException { default: throw new YggdrasilException("Internal error; " + type); } - while (dim-- > 0) - c = Array.newInstance(c, 0).getClass(); - return c; + while (dimensions-- > 0) + clazz = Array.newInstance(clazz, 0).getClass(); + return clazz; } - // Reference - @Override protected int readReference() throws IOException { return readUnsignedInt(); } - // generic Object - @Override protected Class readObjectType() throws IOException { return yggdrasil.getClass(readShortString()); @@ -312,14 +281,12 @@ protected String readFieldID() throws IOException { return readShortString(); } - // stream - @Override public void close() throws IOException { try { read(); throw new StreamCorruptedException("Stream still has data, at least " + (1 + in.available()) + " bytes remain"); - } catch (final EOFException e) {} finally { + } catch (EOFException ignored) {} finally { in.close(); } } diff --git a/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilOutputStream.java b/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilOutputStream.java index 139a50b6e00..f77b8c2405c 100644 --- a/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilOutputStream.java +++ b/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilOutputStream.java @@ -18,81 +18,75 @@ */ package ch.njol.yggdrasil; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + import static ch.njol.yggdrasil.Tag.T_ARRAY; import static ch.njol.yggdrasil.Tag.T_REFERENCE; import static ch.njol.yggdrasil.Tag.getPrimitiveFromWrapper; import static ch.njol.yggdrasil.Tag.getType; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.util.HashMap; - public final class DefaultYggdrasilOutputStream extends YggdrasilOutputStream { - private final static Charset UTF_8 = Charset.forName("UTF-8"); - private final OutputStream out; - private final short version; - public DefaultYggdrasilOutputStream(final Yggdrasil y, final OutputStream out) throws IOException { - super(y); + public DefaultYggdrasilOutputStream(Yggdrasil yggdrasil, OutputStream out) throws IOException { + super(yggdrasil); this.out = out; - version = y.version; + version = yggdrasil.version; writeInt(Yggdrasil.MAGIC_NUMBER); writeShort(version); } - // private - - private void write(final int b) throws IOException { + private void write(int b) throws IOException { out.write(b); } @Override - protected void writeTag(final Tag t) throws IOException { - out.write(t.tag); + protected void writeTag(Tag tag) throws IOException { + out.write(tag.tag); } - private final HashMap writtenShortStrings = new HashMap<>(); + private final Map writtenShortStrings = new HashMap<>(); int nextShortStringID = 0; /** * Writes a class ID or Field name */ - private void writeShortString(final String s) throws IOException { - if (writtenShortStrings.containsKey(s)) { + private void writeShortString(String string) throws IOException { + if (writtenShortStrings.containsKey(string)) { writeTag(T_REFERENCE); if (version <= 1) - writeInt(writtenShortStrings.get(s)); + writeInt(writtenShortStrings.get(string)); else - writeUnsignedInt(writtenShortStrings.get(s)); + writeUnsignedInt(writtenShortStrings.get(string)); } else { if (nextShortStringID < 0) throw new YggdrasilException("Too many field names/class IDs (max: " + Integer.MAX_VALUE + ")"); - final byte[] d = s.getBytes(UTF_8); + byte[] d = string.getBytes(StandardCharsets.UTF_8); if (d.length >= (T_REFERENCE.tag & 0xFF)) - throw new YggdrasilException("Field name or Class ID too long: " + s); + throw new YggdrasilException("Field name or Class ID too long: " + string); write(d.length); out.write(d); if (d.length > 4) - writtenShortStrings.put(s, nextShortStringID++); + writtenShortStrings.put(string, nextShortStringID++); } } - // Primitives - - private void writeByte(final byte b) throws IOException { + private void writeByte(byte b) throws IOException { write(b & 0xFF); } - private void writeShort(final short s) throws IOException { + private void writeShort(short s) throws IOException { write((s >>> 8) & 0xFF); write(s & 0xFF); } - private void writeUnsignedShort(final short s) throws IOException { + private void writeUnsignedShort(short s) throws IOException { assert s >= 0; if (s <= 0x7f) writeByte((byte) (0x80 | s)); @@ -100,14 +94,14 @@ private void writeUnsignedShort(final short s) throws IOException { writeShort(s); } - private void writeInt(final int i) throws IOException { + private void writeInt(int i) throws IOException { write((i >>> 24) & 0xFF); write((i >>> 16) & 0xFF); write((i >>> 8) & 0xFF); write(i & 0xFF); } - private void writeUnsignedInt(final int i) throws IOException { + private void writeUnsignedInt(int i) throws IOException { assert i >= 0; if (i <= 0x7FFF) writeShort((short) (0x8000 | i)); @@ -115,7 +109,7 @@ private void writeUnsignedInt(final int i) throws IOException { writeInt(i); } - private void writeLong(final long l) throws IOException { + private void writeLong(long l) throws IOException { write((int) ((l >>> 56) & 0xFF)); write((int) ((l >>> 48) & 0xFF)); write((int) ((l >>> 40) & 0xFF)); @@ -126,102 +120,96 @@ private void writeLong(final long l) throws IOException { write((int) (l & 0xFF)); } - private void writeFloat(final float f) throws IOException { + private void writeFloat(float f) throws IOException { writeInt(Float.floatToIntBits(f)); } - private void writeDouble(final double d) throws IOException { + private void writeDouble(double d) throws IOException { writeLong(Double.doubleToLongBits(d)); } - private void writeChar(final char c) throws IOException { + private void writeChar(char c) throws IOException { writeShort((short) c); } - private void writeBoolean(final boolean b) throws IOException { + private void writeBoolean(boolean b) throws IOException { write(b ? 1 : 0); } @Override - protected void writePrimitive_(final Object o) throws IOException { - switch (getPrimitiveFromWrapper(o.getClass())) { + protected void writePrimitive_(Object object) throws IOException { + switch (getPrimitiveFromWrapper(object.getClass())) { case T_BYTE: - writeByte((Byte) o); + writeByte((Byte) object); break; case T_SHORT: - writeShort((Short) o); + writeShort((Short) object); break; case T_INT: - writeInt((Integer) o); + writeInt((Integer) object); break; case T_LONG: - writeLong((Long) o); + writeLong((Long) object); break; case T_FLOAT: - writeFloat((Float) o); + writeFloat((Float) object); break; case T_DOUBLE: - writeDouble((Double) o); + writeDouble((Double) object); break; case T_CHAR: - writeChar((Character) o); + writeChar((Character) object); break; case T_BOOLEAN: - writeBoolean((Boolean) o); + writeBoolean((Boolean) object); break; //$CASES-OMITTED$ default: - throw new YggdrasilException("Invalid call to writePrimitive with argument " + o); + throw new YggdrasilException("Invalid call to writePrimitive with argument " + object); } } @Override - protected void writePrimitiveValue(final Object o) throws IOException { - writePrimitive_(o); + protected void writePrimitiveValue(Object object) throws IOException { + writePrimitive_(object); } - // String - @Override - protected void writeStringValue(final String s) throws IOException { - final byte[] d = s.getBytes(UTF_8); + protected void writeStringValue(String string) throws IOException { + byte[] d = string.getBytes(StandardCharsets.UTF_8); writeUnsignedInt(d.length); out.write(d); } - // Array - @Override - protected void writeArrayComponentType(final Class componentType) throws IOException { + protected void writeArrayComponentType(Class componentType) throws IOException { writeClass_(componentType); } @Override - protected void writeArrayLength(final int length) throws IOException { + protected void writeArrayLength(int length) throws IOException { writeUnsignedInt(length); } @Override - protected void writeArrayEnd() throws IOException {} - - // Class + protected void writeArrayEnd() {} @Override - protected void writeClassType(final Class c) throws IOException { - writeClass_(c); + protected void writeClassType(Class type) throws IOException { + writeClass_(type); } - private void writeClass_(Class c) throws IOException { - while (c.isArray()) { + private void writeClass_(Class type) throws IOException { + while (type.isArray()) { writeTag(T_ARRAY); - c = c.getComponentType(); + type = type.getComponentType(); } - final Tag t = getType(c); - switch (t) { + Tag tag = getType(type); + switch (tag) { case T_OBJECT: case T_ENUM: - writeTag(t); - writeShortString(yggdrasil.getID(c)); + writeTag(tag); + writeShortString(yggdrasil.getID(type)); break; case T_BOOLEAN: case T_BOOLEAN_OBJ: @@ -241,57 +229,49 @@ private void writeClass_(Class c) throws IOException { case T_SHORT_OBJ: case T_CLASS: case T_STRING: - writeTag(t); + writeTag(tag); break; case T_NULL: case T_REFERENCE: case T_ARRAY: default: - throw new YggdrasilException("" + c.getCanonicalName()); + throw new YggdrasilException("" + type.getCanonicalName()); } } - // Enum - @Override - protected void writeEnumType(final String type) throws IOException { + protected void writeEnumType(String type) throws IOException { writeShortString(type); } @Override - protected void writeEnumID(final String id) throws IOException { + protected void writeEnumID(String id) throws IOException { writeShortString(id); } - // generic Object - @Override - protected void writeObjectType(final String type) throws IOException { + protected void writeObjectType(String type) throws IOException { writeShortString(type); } @Override - protected void writeNumFields(final short numFields) throws IOException { + protected void writeNumFields(short numFields) throws IOException { writeUnsignedShort(numFields); } @Override - protected void writeFieldID(final String id) throws IOException { + protected void writeFieldID(String id) throws IOException { writeShortString(id); } @Override - protected void writeObjectEnd() throws IOException {} - - // Reference + protected void writeObjectEnd() {} @Override - protected void writeReferenceID(final int ref) throws IOException { - writeUnsignedInt(ref); + protected void writeReferenceID(int reference) throws IOException { + writeUnsignedInt(reference); } - // stream - @Override public void flush() throws IOException { out.flush(); diff --git a/src/main/java/ch/njol/yggdrasil/FieldHandler.java b/src/main/java/ch/njol/yggdrasil/FieldHandler.java index c912924ff9f..ea894c68088 100644 --- a/src/main/java/ch/njol/yggdrasil/FieldHandler.java +++ b/src/main/java/ch/njol/yggdrasil/FieldHandler.java @@ -18,39 +18,39 @@ */ package ch.njol.yggdrasil; +import ch.njol.yggdrasil.Fields.FieldContext; + import java.io.StreamCorruptedException; import java.lang.reflect.Field; -import ch.njol.yggdrasil.Fields.FieldContext; - public interface FieldHandler { /** * Called when a loaded field doesn't exist. * - * @param o The object whose filed is missing + * @param object The object whose filed is missing * @param field The field read from stream * @return Whether this Handler handled the request */ - public boolean excessiveField(Object o, FieldContext field) throws StreamCorruptedException; + boolean excessiveField(Object object, FieldContext field) throws StreamCorruptedException; /** * Called if a field was not found in the stream. * - * @param o The object whose filed is missing + * @param object The object whose filed is missing * @param field The field that didn't occur in the stream * @return Whether this Handler handled the request */ - public boolean missingField(Object o, Field field) throws StreamCorruptedException; + boolean missingField(Object object, Field field) throws StreamCorruptedException; /** - * Called when a loaded value is not compatible with the type of a field. + * Called when a loaded value is not compatible with the type of field. * - * @param o The object the field belongs to - * @param f The field to set - * @param field The field read from stream + * @param object The object the field belongs to + * @param field The field to set + * @param context The field read from stream * @return Whether this Handler handled the request */ - public boolean incompatibleField(Object o, Field f, FieldContext field) throws StreamCorruptedException; + boolean incompatibleField(Object object, Field field, FieldContext context) throws StreamCorruptedException; } diff --git a/src/main/java/ch/njol/yggdrasil/Fields.java b/src/main/java/ch/njol/yggdrasil/Fields.java index 11d8b18fadc..865f3cf2bf3 100644 --- a/src/main/java/ch/njol/yggdrasil/Fields.java +++ b/src/main/java/ch/njol/yggdrasil/Fields.java @@ -18,6 +18,11 @@ */ package ch.njol.yggdrasil; +import ch.njol.yggdrasil.Fields.FieldContext; +import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilRobustSerializable; +import org.eclipse.jdt.annotation.Nullable; + +import javax.annotation.concurrent.NotThreadSafe; import java.io.NotSerializableException; import java.io.StreamCorruptedException; import java.lang.reflect.Field; @@ -31,40 +36,31 @@ import java.util.Map; import java.util.Set; -import javax.annotation.concurrent.NotThreadSafe; - -import org.eclipse.jdt.annotation.Nullable; - -import ch.njol.yggdrasil.Fields.FieldContext; // required - wtf -import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilRobustSerializable; - @NotThreadSafe public final class Fields implements Iterable { /** - * Holds a field's name and value, and throws {@link StreamCorruptedException}s if primitives or objects are used incorrectly. - * - * @author Peter Güttinger + * Holds a field's name and value, and throws {@link StreamCorruptedException}s + * if primitives or objects are used incorrectly. */ @NotThreadSafe - public final static class FieldContext { + public static final class FieldContext { final String id; /** not null if this {@link #isPrimitiveValue is a primitive} */ @Nullable private Object value; - private boolean isPrimitiveValue; - FieldContext(final String id) { + FieldContext(String id) { this.id = id; } - FieldContext(final Field f, final Object o) throws IllegalArgumentException, IllegalAccessException { - id = Yggdrasil.getID(f); - value = f.get(o); - isPrimitiveValue = f.getType().isPrimitive(); + FieldContext(Field field, Object object) throws IllegalArgumentException, IllegalAccessException { + id = Yggdrasil.getID(field); + value = field.get(object); + isPrimitiveValue = field.getType().isPrimitive(); } public String getID() { @@ -77,12 +73,12 @@ public boolean isPrimitive() { @Nullable public Class getType() { - final Object value = this.value; + Object value = this.value; if (value == null) return null; - final Class c = value.getClass(); - assert c != null; - return isPrimitiveValue ? Tag.getPrimitiveFromWrapper(c).c : c; + Class type = value.getClass(); + assert type != null; + return isPrimitiveValue ? Tag.getPrimitiveFromWrapper(type).type : type; } @Nullable @@ -94,10 +90,10 @@ public Object getObject() throws StreamCorruptedException { @SuppressWarnings("unchecked") @Nullable - public T getObject(final Class expectedType) throws StreamCorruptedException { + public T getObject(Class expectedType) throws StreamCorruptedException { if (isPrimitiveValue) throw new StreamCorruptedException("field " + id + " is a primitive, but expected " + expectedType); - final Object value = this.value; + Object value = this.value; if (value != null && !expectedType.isInstance(value)) throw new StreamCorruptedException("Field " + id + " of " + value.getClass() + ", but expected " + expectedType); return (T) value; @@ -111,42 +107,42 @@ public Object getPrimitive() throws StreamCorruptedException { } @SuppressWarnings("unchecked") - public T getPrimitive(final Class expectedType) throws StreamCorruptedException { + public T getPrimitive(Class expectedType) throws StreamCorruptedException { if (!isPrimitiveValue) throw new StreamCorruptedException("field " + id + " is not a primitive, but expected " + expectedType); assert expectedType.isPrimitive() || Tag.isWrapper(expectedType); - final Object value = this.value; + Object value = this.value; assert value != null; if (!(expectedType.isPrimitive() ? Tag.getWrapperClass(expectedType).isInstance(value) : expectedType.isInstance(value))) throw new StreamCorruptedException("Field " + id + " of " + value.getClass() + ", but expected " + expectedType); return (T) value; } - public void setObject(final @Nullable Object value) { + public void setObject(@Nullable Object value) { this.value = value; isPrimitiveValue = false; } - public void setPrimitive(final Object value) { - assert value != null && Tag.isWrapper(value.getClass()); + public void setPrimitive(Object value) { + assert Tag.isWrapper(value.getClass()); this.value = value; isPrimitiveValue = true; } - public void setField(final Object o, final Field f, final Yggdrasil y) throws StreamCorruptedException { - if (Modifier.isStatic(f.getModifiers())) - throw new StreamCorruptedException("The field " + id + " of " + f.getDeclaringClass() + " is static"); - if (Modifier.isTransient(f.getModifiers())) - throw new StreamCorruptedException("The field " + id + " of " + f.getDeclaringClass() + " is transient"); - if (f.getType().isPrimitive() != isPrimitiveValue) - throw new StreamCorruptedException("The field " + id + " of " + f.getDeclaringClass() + " is " + (f.getType().isPrimitive() ? "" : "not ") + "primitive"); + public void setField(Object object, Field field, Yggdrasil yggdrasil) throws StreamCorruptedException { + if (Modifier.isStatic(field.getModifiers())) + throw new StreamCorruptedException("The field " + id + " of " + field.getDeclaringClass() + " is static"); + if (Modifier.isTransient(field.getModifiers())) + throw new StreamCorruptedException("The field " + id + " of " + field.getDeclaringClass() + " is transient"); + if (field.getType().isPrimitive() != isPrimitiveValue) + throw new StreamCorruptedException("The field " + id + " of " + field.getDeclaringClass() + " is " + (field.getType().isPrimitive() ? "" : "not ") + "primitive"); try { - f.setAccessible(true); - f.set(o, value); - } catch (final IllegalArgumentException e) { - if (!(o instanceof YggdrasilRobustSerializable) || !((YggdrasilRobustSerializable) o).incompatibleField(f, this)) - y.incompatibleField(o, f, this); - } catch (final IllegalAccessException e) { + field.setAccessible(true); + field.set(object, value); + } catch (IllegalArgumentException e) { + if (!(object instanceof YggdrasilRobustSerializable) || !((YggdrasilRobustSerializable) object).incompatibleField(field, this)) + yggdrasil.incompatibleField(object, field, this); + } catch (IllegalAccessException e) { assert false; } } @@ -157,14 +153,14 @@ public int hashCode() { } @Override - public boolean equals(final @Nullable Object obj) { - if (this == obj) + public boolean equals(@Nullable Object object) { + if (this == object) return true; - if (obj == null) + if (object == null) return false; - if (!(obj instanceof FieldContext)) + if (!(object instanceof FieldContext)) return false; - final FieldContext other = (FieldContext) obj; + FieldContext other = (FieldContext) object; return id.equals(other.id); } @@ -182,130 +178,132 @@ public Fields() { yggdrasil = null; } - public Fields(final Yggdrasil yggdrasil) { + public Fields(Yggdrasil yggdrasil) { this.yggdrasil = yggdrasil; } /** - * Creates a fields object and initialises it with all non-transient and non-static fields of the given class and its superclasses. + * Creates a fields object and initialises it with all non-transient and + * non-static fields of the given class and its superclasses. * - * @param c Some class - * @throws NotSerializableException If a field occurs more than once (i.e. if a class has a field with the same name as a field in one of its superclasses) + * @param type Some class + * @throws NotSerializableException If a field occurs more than once (i.e. if a class has a + * field with the same name as a field in one of its superclasses) */ - public Fields(final Class c, final Yggdrasil yggdrasil) throws NotSerializableException { + public Fields(Class type, Yggdrasil yggdrasil) throws NotSerializableException { this.yggdrasil = yggdrasil; - for (final Field f : getFields(c)) { - assert f != null; - final String id = Yggdrasil.getID(f); + for (Field field : getFields(type)) { + assert field != null; + String id = Yggdrasil.getID(field); fields.put(id, new FieldContext(id)); } } /** - * Creates a fields object and initialises it with all non-transient and non-static fields of the given object. + * Creates a fields object and initialises it with all non-transient + * and non-static fields of the given object. * - * @param o Some object - * @throws NotSerializableException If a field occurs more than once (i.e. if a class has a field with the same name as a field in one of its superclasses) + * @param object Some object + * @throws NotSerializableException If a field occurs more than once (i.e. if a class + * has a field with the same name as a field in one of its superclasses) */ - public Fields(final Object o) throws NotSerializableException { - this(o, null); + public Fields(Object object) throws NotSerializableException { + this(object, null); } /** - * Creates a fields object and initialises it with all non-transient and non-static fields of the given object. + * Creates a fields object and initialises it with all non-transient + * and non-static fields of the given object. * - * @param o Some object - * @throws NotSerializableException If a field occurs more than once (i.e. if a class has a field with the same name as a field in one of its superclasses) + * @param object Some object + * @throws NotSerializableException If a field occurs more than once (i.e. if a class + * has a field with the same name as a field in one of its superclasses) */ - public Fields(final Object o, @Nullable final Yggdrasil yggdrasil) throws NotSerializableException { + public Fields(Object object, @Nullable Yggdrasil yggdrasil) throws NotSerializableException { this.yggdrasil = yggdrasil; - final Class c = o.getClass(); - assert c != null; - for (final Field f : getFields(c)) { - assert f != null; + Class type = object.getClass(); + assert type != null; + for (Field field : getFields(type)) { + assert field != null; try { - fields.put(Yggdrasil.getID(f), new FieldContext(f, o)); - } catch (final IllegalArgumentException e) { - assert false; - } catch (final IllegalAccessException e) { + fields.put(Yggdrasil.getID(field), new FieldContext(field, object)); + } catch (IllegalArgumentException | IllegalAccessException e) { assert false; } } } - private final static Map, Collection> cache = new HashMap<>(); + private static final Map, Collection> cache = new HashMap<>(); /** - * Gets all serialisable fields of the provided class, including superclasses. + * Gets all serializable fields of the provided class, including superclasses. * - * @param c The class to get the fields of + * @param type The class to get the fields of * @return All non-static and non-transient fields of the given class and its superclasses - * @throws NotSerializableException If a field occurs more than once (i.e. if a class has a field with the same name as a field in one of its superclasses) + * @throws NotSerializableException If a field occurs more than once (i.e. if a class has a + * field with the same name as a field in one of its superclasses) */ - public static Collection getFields(final Class c) throws NotSerializableException { - Collection fields = cache.get(c); + public static Collection getFields(Class type) throws NotSerializableException { + Collection fields = cache.get(type); if (fields != null) return fields; fields = new ArrayList<>(); - final Set ids = new HashSet<>(); - for (Class sc = c; sc != null; sc = sc.getSuperclass()) { - final Field[] fs = sc.getDeclaredFields(); - for (final Field f : fs) { - final int m = f.getModifiers(); - if (Modifier.isStatic(m) || Modifier.isTransient(m)) + Set ids = new HashSet<>(); + for (Class superClass = type; superClass != null; superClass = superClass.getSuperclass()) { + Field[] declaredFields = superClass.getDeclaredFields(); + for (Field field : declaredFields) { + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; - final String id = Yggdrasil.getID(f); + String id = Yggdrasil.getID(field); if (ids.contains(id)) - throw new NotSerializableException(c + "/" + sc + ": duplicate field id '" + id + "'"); - f.setAccessible(true); - fields.add(f); + throw new NotSerializableException(type + "/" + superClass + ": duplicate field id '" + id + "'"); + field.setAccessible(true); + fields.add(field); ids.add(id); } } fields = Collections.unmodifiableCollection(fields); - assert fields != null; - cache.put(c, fields); + cache.put(type, fields); return fields; } /** * Sets all fields of the given Object to the values stored in this Fields object. * - * @param o The object whose fields should be set - * @throws StreamCorruptedException - * @throws NotSerializableException + * @param object The object whose fields should be set * @throws YggdrasilException If this was called on a Fields object not created by Yggdrasil itself */ - public void setFields(final Object o) throws StreamCorruptedException, NotSerializableException { - final Yggdrasil y = yggdrasil; - if (y == null) + public void setFields(Object object) throws StreamCorruptedException, NotSerializableException { + Yggdrasil yggdrasil = this.yggdrasil; + if (yggdrasil == null) throw new YggdrasilException(""); - final Set excessive = new HashSet<>(fields.values()); - final Class oc = o.getClass(); - assert oc != null; - for (final Field f : getFields(oc)) { - assert f != null; - final String id = Yggdrasil.getID(f); - final FieldContext c = fields.get(id); - if (c == null) { - if (!(o instanceof YggdrasilRobustSerializable) || !((YggdrasilRobustSerializable) o).missingField(f)) - y.missingField(o, f); + Set excessive = new HashSet<>(fields.values()); + Class type = object.getClass(); + assert type != null; + for (Field field : getFields(type)) { + assert field != null; + String id = Yggdrasil.getID(field); + FieldContext context = fields.get(id); + if (context == null) { + if (!(object instanceof YggdrasilRobustSerializable) || !((YggdrasilRobustSerializable) object).missingField(field)) + yggdrasil.missingField(object, field); } else { - c.setField(o, f, y); + context.setField(object, field, yggdrasil); } - excessive.remove(c); + excessive.remove(context); } - for (final FieldContext f : excessive) { - assert f != null; - if (!(o instanceof YggdrasilRobustSerializable) || !((YggdrasilRobustSerializable) o).excessiveField(f)) - y.excessiveField(o, f); + for (FieldContext context : excessive) { + assert context != null; + if (!(object instanceof YggdrasilRobustSerializable) || !((YggdrasilRobustSerializable) object).excessiveField(context)) + yggdrasil.excessiveField(object, context); } } @Deprecated - public void setFields(final Object o, final Yggdrasil y) throws StreamCorruptedException, NotSerializableException { - assert yggdrasil == y; - setFields(o); + public void setFields(Object object, Yggdrasil yggdrasil) throws StreamCorruptedException, NotSerializableException { + assert this.yggdrasil == yggdrasil; + setFields(object); } /** @@ -315,75 +313,75 @@ public int size() { return fields.size(); } - public void putObject(final String fieldID, final @Nullable Object value) { - FieldContext c = fields.get(fieldID); - if (c == null) - fields.put(fieldID, c = new FieldContext(fieldID)); - c.setObject(value); + public void putObject(String fieldID, @Nullable Object value) { + FieldContext context = fields.get(fieldID); + if (context == null) + fields.put(fieldID, context = new FieldContext(fieldID)); + context.setObject(value); } - public void putPrimitive(final String fieldID, final Object value) { - FieldContext c = fields.get(fieldID); - if (c == null) - fields.put(fieldID, c = new FieldContext(fieldID)); - c.setPrimitive(value); + public void putPrimitive(String fieldID, Object value) { + FieldContext context = fields.get(fieldID); + if (context == null) + fields.put(fieldID, context = new FieldContext(fieldID)); + context.setPrimitive(value); } /** * @param fieldID A field's id * @return Whether the field is defined */ - public boolean contains(final String fieldID) { + public boolean contains(String fieldID) { return fields.containsKey(fieldID); } public boolean hasField(String fieldID) { - return this.fields.containsKey(fieldID); + return fields.containsKey(fieldID); } @Nullable - public Object getObject(final String field) throws StreamCorruptedException { - final FieldContext c = fields.get(field); - if (c == null) + public Object getObject(String field) throws StreamCorruptedException { + FieldContext context = fields.get(field); + if (context == null) throw new StreamCorruptedException("Nonexistent field " + field); - return c.getObject(); + return context.getObject(); } @Nullable - public T getObject(final String fieldID, final Class expectedType) throws StreamCorruptedException { + public T getObject(String fieldID, Class expectedType) throws StreamCorruptedException { assert !expectedType.isPrimitive(); - final FieldContext c = fields.get(fieldID); - if (c == null) + FieldContext context = fields.get(fieldID); + if (context == null) throw new StreamCorruptedException("Nonexistent field " + fieldID); - return c.getObject(expectedType); + return context.getObject(expectedType); } - public Object getPrimitive(final String fieldID) throws StreamCorruptedException { - final FieldContext c = fields.get(fieldID); - if (c == null) + public Object getPrimitive(String fieldID) throws StreamCorruptedException { + FieldContext context = fields.get(fieldID); + if (context == null) throw new StreamCorruptedException("Nonexistent field " + fieldID); - return c.getPrimitive(); + return context.getPrimitive(); } - public T getPrimitive(final String fieldID, final Class expectedType) throws StreamCorruptedException { + public T getPrimitive(String fieldID, Class expectedType) throws StreamCorruptedException { assert expectedType.isPrimitive() || Tag.getPrimitiveFromWrapper(expectedType).isPrimitive(); - final FieldContext c = fields.get(fieldID); - if (c == null) + FieldContext context = fields.get(fieldID); + if (context == null) throw new StreamCorruptedException("Nonexistent field " + fieldID); - return c.getPrimitive(expectedType); + return context.getPrimitive(expectedType); } @Nullable - public T getAndRemoveObject(final String field, final Class expectedType) throws StreamCorruptedException { - final T t = getObject(field, expectedType); + public T getAndRemoveObject(String field, Class expectedType) throws StreamCorruptedException { + T object = getObject(field, expectedType); removeField(field); - return t; + return object; } - public T getAndRemovePrimitive(final String field, final Class expectedType) throws StreamCorruptedException { - final T t = getPrimitive(field, expectedType); + public T getAndRemovePrimitive(String field, Class expectedType) throws StreamCorruptedException { + T object = getPrimitive(field, expectedType); removeField(field); - return t; + return object; } /** @@ -392,7 +390,7 @@ public T getAndRemovePrimitive(final String field, final Class expectedTy * @param fieldID The id of the field to remove * @return Whether a field with the given name was actually defined */ - public boolean removeField(final String fieldID) { + public boolean removeField(String fieldID) { return fields.remove(fieldID) != null; } diff --git a/src/main/java/ch/njol/yggdrasil/JRESerializer.java b/src/main/java/ch/njol/yggdrasil/JRESerializer.java index f08c83831e1..fdd2bfafaaa 100644 --- a/src/main/java/ch/njol/yggdrasil/JRESerializer.java +++ b/src/main/java/ch/njol/yggdrasil/JRESerializer.java @@ -18,6 +18,9 @@ */ package ch.njol.yggdrasil; +import com.google.common.collect.ImmutableList; +import org.eclipse.jdt.annotation.Nullable; + import java.io.NotSerializableException; import java.io.StreamCorruptedException; import java.util.ArrayList; @@ -26,75 +29,68 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; +import java.util.List; import java.util.Map; -import java.util.Set; import java.util.UUID; -import org.eclipse.jdt.annotation.Nullable; - public class JRESerializer extends YggdrasilSerializer { - private final static Class[] supportedClasses = { - ArrayList.class, LinkedList.class, - HashSet.class, - HashMap.class, - UUID.class - }; - - private final static Set> set = new HashSet<>(Arrays.asList(supportedClasses)); + private static final List> SUPPORTED_CLASSES = ImmutableList.of( + ArrayList.class, LinkedList.class, HashSet.class, HashMap.class, UUID.class); @Override @Nullable - public Class getClass(final String id) { - for (final Class c : supportedClasses) - if (c.getSimpleName().equals(id)) - return c; + public Class getClass(String id) { + for (Class type : SUPPORTED_CLASSES) + if (type.getSimpleName().equals(id)) + return type; return null; } @Override @Nullable - public String getID(final Class c) { - if (set.contains(c)) - return c.getSimpleName(); + public String getID(Class type) { + if (SUPPORTED_CLASSES.contains(type)) + return type.getSimpleName(); return null; } @Override - public Fields serialize(final Object o) { - if (!set.contains(o.getClass())) + public Fields serialize(Object object) { + if (!SUPPORTED_CLASSES.contains(object.getClass())) throw new IllegalArgumentException(); - final Fields f = new Fields(); - if (o instanceof Collection) { - final Collection c = ((Collection) o); - f.putObject("values", c.toArray()); - } else if (o instanceof Map) { - final Map m = ((Map) o); - f.putObject("keys", m.keySet().toArray()); - f.putObject("values", m.values().toArray()); - } else if (o instanceof UUID) { - f.putPrimitive("mostSigBits", Long.valueOf(((UUID)o).getMostSignificantBits())); - f.putPrimitive("leastSigBits", Long.valueOf(((UUID)o).getLeastSignificantBits())); + Fields fields = new Fields(); + if (object instanceof Collection) { + Collection collection = ((Collection) object); + fields.putObject("values", collection.toArray()); + } else if (object instanceof Map) { + Map map = ((Map) object); + fields.putObject("keys", map.keySet().toArray()); + fields.putObject("values", map.values().toArray()); + } else if (object instanceof UUID) { + fields.putPrimitive("mostSigBits", ((UUID) object).getMostSignificantBits()); + fields.putPrimitive("leastSigBits", ((UUID) object).getLeastSignificantBits()); } - assert f.size() > 0 : o; - return f; + assert fields.size() > 0 : object; + return fields; } @Override - public boolean canBeInstantiated(Class c){ - return c != UUID.class; + public boolean canBeInstantiated(Class type) { + return type != UUID.class; } @Override @Nullable - public T newInstance(final Class c) { + public T newInstance(Class type) { try { - return c.newInstance(); - } catch (final InstantiationException e) { // all collections handled here have public nullary constructors + //noinspection deprecation + return type.newInstance(); + } catch (InstantiationException e) { // all collections handled here have public nullary constructors e.printStackTrace(); assert false; return null; - } catch (final IllegalAccessException e) { + } catch (IllegalAccessException e) { e.printStackTrace(); assert false; return null; @@ -103,25 +99,25 @@ public T newInstance(final Class c) { @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public void deserialize(final Object o, final Fields fields) throws StreamCorruptedException { + public void deserialize(Object object, Fields fields) throws StreamCorruptedException { try { - if (o instanceof Collection) { - final Collection c = ((Collection) o); - final Object[] values = fields.getObject("values", Object[].class); + if (object instanceof Collection) { + Collection collection = ((Collection) object); + Object[] values = fields.getObject("values", Object[].class); if (values == null) throw new StreamCorruptedException(); - c.addAll((Collection) Arrays.asList(values)); + collection.addAll((Collection) Arrays.asList(values)); return; - } else if (o instanceof Map) { - final Map m = ((Map) o); - final Object[] keys = fields.getObject("keys", Object[].class), values = fields.getObject("values", Object[].class); + } else if (object instanceof Map) { + Map map = ((Map) object); + Object[] keys = fields.getObject("keys", Object[].class), values = fields.getObject("values", Object[].class); if (keys == null || values == null || keys.length != values.length) throw new StreamCorruptedException(); for (int i = 0; i < keys.length; i++) - ((Map) m).put(keys[i], values[i]); + ((Map) map).put(keys[i], values[i]); return; } - } catch (final Exception e) { + } catch (Exception e) { throw new StreamCorruptedException(e.getClass().getSimpleName() + ": " + e.getMessage()); } throw new StreamCorruptedException(); @@ -129,10 +125,12 @@ public void deserialize(final Object o, final Fields fields) throws StreamCorrup @SuppressWarnings({"unchecked", "null"}) @Override - public E deserialize(Class c, Fields fields) throws StreamCorruptedException, NotSerializableException { - if (c == UUID.class) { - return (E) new UUID(fields.getPrimitive("mostSigBits", Long.TYPE).longValue(), fields.getPrimitive("leastSigBits", Long.TYPE).longValue()); - } + public E deserialize(Class type, Fields fields) throws StreamCorruptedException, NotSerializableException { + if (type == UUID.class) + return (E) new UUID( + fields.getPrimitive("mostSigBits", Long.TYPE), + fields.getPrimitive("leastSigBits", Long.TYPE)); + throw new StreamCorruptedException(); } diff --git a/src/main/java/ch/njol/yggdrasil/PseudoEnum.java b/src/main/java/ch/njol/yggdrasil/PseudoEnum.java index 259f58adc73..a3d2b5ed6a7 100644 --- a/src/main/java/ch/njol/yggdrasil/PseudoEnum.java +++ b/src/main/java/ch/njol/yggdrasil/PseudoEnum.java @@ -18,6 +18,9 @@ */ package ch.njol.yggdrasil; +import org.eclipse.jdt.annotation.Nullable; + +import javax.annotation.concurrent.ThreadSafe; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -26,27 +29,23 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import javax.annotation.concurrent.ThreadSafe; - -import org.eclipse.jdt.annotation.Nullable; - /** - * A class that acts as a "pseudo-enum", i.e. a class which only has immutable, (public,) final static instances, which can be identified by their unique name. The instances don't - * even have to be defined in their class, as they are registered in the constructor. + * A class that acts as a "pseudo-enum", i.e. a class which only has immutable, (public,) static final instances, + * which can be identified by their unique name. The instances don't even have to be defined in their class, + * as they are registered in the constructor. *

- * Please note that you cannot define a constant's id used for saving by annotating it with {@link YggdrasilID @YggdrasilID}, as the field(s) of the constant may not be known, and + * Please note that you cannot define a constant's id used for saving by annotating it with + * {@link YggdrasilID @YggdrasilID}, as the field(s) of the constant may not be known, and * furthermore a constant can be assigned to any number of fields. *

- * This class defines methods similar to those in {@link Enum} with minor differences, e.g. {@link #values()} returns a {@link List} instead of an array. - * - * @author Peter Güttinger + * This class defines methods similar to those in {@link Enum} with minor differences, e.g. {@link #values()} returns + * a {@link List} instead of an array. */ @ThreadSafe public abstract class PseudoEnum> { private final String name; private final int ordinal; - private final Info info; /** @@ -54,7 +53,7 @@ public abstract class PseudoEnum> { * @throws IllegalArgumentException If the given name is already in use. */ @SuppressWarnings("unchecked") - protected PseudoEnum(final String name) throws IllegalArgumentException { + protected PseudoEnum(String name) throws IllegalArgumentException { this.name = name; info = (Info) getInfo(getClass()); info.writeLock.lock(); @@ -113,8 +112,8 @@ public final int hashCode() { * Checks for reference equality (==). */ @Override - public final boolean equals(@Nullable final Object obj) { - return obj == this; + public final boolean equals(@Nullable Object object) { + return object == this; } /** @@ -129,8 +128,9 @@ protected final Object clone() throws CloneNotSupportedException { } /** - * Returns this constant's pseudo-enum class, i.e. the first non-anonymous superclass of this constant. This class is the same for all constants inheriting from a common class - * independently from whether they define an anonymous subclass. + * Returns this constant's pseudo-enum class, i.e. the first non-anonymous superclass of this constant. + * This class is the same for all constants inheriting from a common class + * independently of whether they define an anonymous subclass. * * @return This constant's pseudo-enum class. * @see Enum#getDeclaringClass() @@ -146,11 +146,11 @@ public final Class getDeclaringClass() { * @return The pseudo-enum class of the given class. * @see Enum#getDeclaringClass() */ - public static > Class getDeclaringClass(final Class type) { - Class c = type; - while (c.isAnonymousClass()) - c = c.getSuperclass(); - return c; + public static > Class getDeclaringClass(Class type) { + Class superClass = type; + while (superClass.isAnonymousClass()) + superClass = superClass.getSuperclass(); + return superClass; } /** @@ -164,26 +164,26 @@ public static > Class getDeclaringClass(final * @see Enum#valueOf(Class, String) */ public final List values() { - return values(getDeclaringClass(), info); + return values(getDeclaringClass()); } /** - * Returns all constants of the given class registered so far, ordered by their {@link #ordinal() id} (i.e. c.values()[c.ordinal()] == c is true for any constant - * c). + * Returns all constants of the given class registered so far, ordered by their {@link #ordinal() id} (i.e. type.values()[type.ordinal()] == type is true for any constant + * type). *

* The returned list is a copy of the internal list at the time this method was called. * * @return All constants registered so far. - * @throws IllegalArgumentException If {@link #getDeclaringClass(Class) getDeclaringClass}(c) != c (i.e. if the given class is anonymous). + * @throws IllegalArgumentException If {@link #getDeclaringClass(Class) getDeclaringClass}(type) != type (i.e. if the given class is anonymous). * @see Enum#valueOf(Class, String) */ - public static > List values(final Class c) throws IllegalArgumentException { - if (c != getDeclaringClass(c)) - throw new IllegalArgumentException(c + " != " + getDeclaringClass(c)); - return values(c, getInfo(c)); + public static > List values(Class type) throws IllegalArgumentException { + if (type != getDeclaringClass(type)) + throw new IllegalArgumentException(type + " != " + getDeclaringClass(type)); + return values(getInfo(type)); } - private static > List values(final Class c, final Info info) { + private static > List values(Info info) { info.readLock.lock(); try { return new ArrayList<>(info.values); @@ -200,8 +200,7 @@ private static > List values(final Class c, final * @throws IndexOutOfBoundsException if ID is < 0 or >= {@link #numConstants()} * @see #valueOf(String) */ - @SuppressWarnings("null") - public final T getConstant(final int id) throws IndexOutOfBoundsException { + public final T getConstant(int id) throws IndexOutOfBoundsException { info.readLock.lock(); try { return info.values.get(id); @@ -228,7 +227,7 @@ public final int numConstants() { * @see Enum#valueOf(Class, String) */ @Nullable - public final T valueOf(final String name) { + public final T valueOf(String name) { info.readLock.lock(); try { return info.map.get(name); @@ -238,14 +237,14 @@ public final T valueOf(final String name) { } /** - * @param c The class of the constant to find + * @param type The class of the constant to find * @param name The name of the constant to find * @return The constant with the given name, or null if no constant with that exact name was found in the given class. * @see Enum#valueOf(Class, String) */ @Nullable - public static > T valueOf(final Class c, final String name) { - final Info info = getInfo(c); + public static > T valueOf(Class type, String name) { + Info info = getInfo(type); info.readLock.lock(); try { return info.map.get(name); @@ -254,27 +253,24 @@ public static > T valueOf(final Class c, final String } } - @SuppressWarnings("null") - private final static class Info> { + private static final class Info> { + final List values = new ArrayList<>(); final Map map = new HashMap<>(); final ReadWriteLock lock = new ReentrantReadWriteLock(true); final Lock readLock = lock.readLock(), writeLock = lock.writeLock(); - public Info() {} } - /** - * Must be synchronised - */ - private final static Map>, Info> infos = new HashMap<>(); + private static final Map>, Info> infos = new HashMap<>(); - private static > Info getInfo(final Class c) { + @SuppressWarnings("unchecked") + private static > Info getInfo(Class type) { synchronized (infos) { - Info info = (Info) infos.get(getDeclaringClass(c)); + Info info = (Info) infos.get(getDeclaringClass(type)); if (info == null) - infos.put(c, info = new Info<>()); + infos.put(type, info = new Info<>()); return info; } } diff --git a/src/main/java/ch/njol/yggdrasil/SimpleClassResolver.java b/src/main/java/ch/njol/yggdrasil/SimpleClassResolver.java index 9d66d9dc858..05bf2ade436 100644 --- a/src/main/java/ch/njol/yggdrasil/SimpleClassResolver.java +++ b/src/main/java/ch/njol/yggdrasil/SimpleClassResolver.java @@ -18,34 +18,33 @@ */ package ch.njol.yggdrasil; -import javax.annotation.concurrent.NotThreadSafe; - +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import org.eclipse.jdt.annotation.Nullable; -import ch.njol.util.coll.BidiHashMap; -import ch.njol.util.coll.BidiMap; +import javax.annotation.concurrent.NotThreadSafe; @NotThreadSafe public class SimpleClassResolver implements ClassResolver { - private final BidiMap, String> classes = new BidiHashMap<>(); + private final BiMap, String> classes = HashBiMap.create(); - public void registerClass(final Class c, final String id) { - final String oldId = classes.put(c, id); + public void registerClass(Class type, String id) { + String oldId = classes.put(type, id); if (oldId != null && !oldId.equals(id)) - throw new YggdrasilException("Changed id of " + c + " from " + oldId + " to " + id); + throw new YggdrasilException("Changed id of " + type + " from " + oldId + " to " + id); } @Override @Nullable - public Class getClass(final String id) { - return classes.getKey(id); + public Class getClass(String id) { + return classes.inverse().get(id); } @Override @Nullable - public String getID(final Class c) { - return classes.getValue(c); + public String getID(Class type) { + return classes.get(type); } } diff --git a/src/main/java/ch/njol/yggdrasil/Tag.java b/src/main/java/ch/njol/yggdrasil/Tag.java index 6a25a82d030..068f3377be1 100644 --- a/src/main/java/ch/njol/yggdrasil/Tag.java +++ b/src/main/java/ch/njol/yggdrasil/Tag.java @@ -18,11 +18,11 @@ */ package ch.njol.yggdrasil; +import org.eclipse.jdt.annotation.Nullable; + import java.util.HashMap; import java.util.Map; -import org.eclipse.jdt.annotation.Nullable; - public enum Tag { /** the null reference */ T_NULL(0x0, null, "null"), @@ -53,20 +53,22 @@ public enum Tag { T_REFERENCE(0xFF, null, "reference"); /** primitive tags are between these value */ - public final static int MIN_PRIMITIVE = T_BYTE.tag, MAX_PRIMITIVE = T_BOOLEAN.tag; + public static final int MIN_PRIMITIVE = T_BYTE.tag; + public static final int MAX_PRIMITIVE = T_BOOLEAN.tag; /** primitive tags are between these value */ - public final static int MIN_WRAPPER = T_BYTE_OBJ.tag, MAX_WRAPPER = T_BOOLEAN_OBJ.tag; + public static final int MIN_WRAPPER = T_BYTE_OBJ.tag; + public static final int MAX_WRAPPER = T_BOOLEAN_OBJ.tag; public final byte tag; @Nullable - public final Class c; + public final Class type; public final String name; - private Tag(final int tag, final @Nullable Class c, final String name) { + Tag(int tag, @Nullable Class type, String name) { assert 0 <= tag && tag <= 0xFF : tag; this.tag = (byte) tag; - this.c = c; + this.type = type; this.name = name; } @@ -92,7 +94,6 @@ public boolean isWrapper() { return MIN_WRAPPER <= tag && tag <= MAX_WRAPPER; } - @SuppressWarnings("null") public Tag getWrapper() { if (!isPrimitive()) { assert false; @@ -101,44 +102,44 @@ public Tag getWrapper() { return byID[tag - MIN_PRIMITIVE + MIN_WRAPPER]; } - private final static Map, Tag> types = new HashMap<>(); - private final static Tag[] byID = new Tag[256]; - private final static Map byName = new HashMap<>(); + private static final Map, Tag> types = new HashMap<>(); + private static final Tag[] byID = new Tag[256]; + private static final Map byName = new HashMap<>(); static { - for (final Tag t : Tag.values()) { - types.put(t.c, t); - byID[t.tag & 0xFF] = t; - byName.put(t.name, t); + for (Tag tag : Tag.values()) { + types.put(tag.type, tag); + byID[tag.tag & 0xFF] = tag; + byName.put(tag.name, tag); } } - public static Tag getType(final @Nullable Class c) { - if (c == null) + public static Tag getType(@Nullable Class type) { + if (type == null) return T_NULL; - final Tag t = types.get(c); + Tag t = types.get(type); if (t != null) return t; - return c.isArray() ? T_ARRAY - : Enum.class.isAssignableFrom(c) || PseudoEnum.class.isAssignableFrom(c) ? T_ENUM // isEnum() doesn't work for subclasses + return type.isArray() ? T_ARRAY + : Enum.class.isAssignableFrom(type) || PseudoEnum.class.isAssignableFrom(type) ? T_ENUM // isEnum() doesn't work for subclasses : T_OBJECT; } @Nullable - public static Tag byID(final byte tag) { + public static Tag byID(byte tag) { return byID[tag & 0xFF]; } @Nullable - public static Tag byID(final int tag) { + public static Tag byID(int tag) { return byID[tag]; } @Nullable - public static Tag byName(final String name) { + public static Tag byName(String name) { return byName.get(name); } - private final static HashMap, Tag> wrapperTypes = new HashMap<>(); + private static final HashMap, Tag> wrapperTypes = new HashMap<>(); static { wrapperTypes.put(Byte.class, T_BYTE); wrapperTypes.put(Short.class, T_SHORT); @@ -150,28 +151,28 @@ public static Tag byName(final String name) { wrapperTypes.put(Boolean.class, T_BOOLEAN); } - public static boolean isWrapper(final Class c) { - return wrapperTypes.containsKey(c); + public static boolean isWrapper(Class type) { + return wrapperTypes.containsKey(type); } - public static Tag getPrimitiveFromWrapper(final Class wrapper) { - final Tag t = wrapperTypes.get(wrapper); - if (t == null) { + public static Tag getPrimitiveFromWrapper(Class wrapper) { + Tag tag = wrapperTypes.get(wrapper); + if (tag == null) { assert false : wrapper; return T_NULL; } - return t; + return tag; } - public static Class getWrapperClass(final Class primitive) { + public static Class getWrapperClass(Class primitive) { assert primitive.isPrimitive(); - final Tag t = types.get(primitive); - if (t == null) { + Tag tag = types.get(primitive); + if (tag == null) { assert false : primitive; return Object.class; } - final Class wrapper = t.getWrapper().c; - assert wrapper != null : t; + Class wrapper = tag.getWrapper().type; + assert wrapper != null : tag; return wrapper; } diff --git a/src/main/java/ch/njol/yggdrasil/Yggdrasil.java b/src/main/java/ch/njol/yggdrasil/Yggdrasil.java index e89482f606e..d97a42900af 100644 --- a/src/main/java/ch/njol/yggdrasil/Yggdrasil.java +++ b/src/main/java/ch/njol/yggdrasil/Yggdrasil.java @@ -18,6 +18,13 @@ */ package ch.njol.yggdrasil; +import ch.njol.yggdrasil.Fields.FieldContext; +import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilExtendedSerializable; +import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilRobustEnum; +import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilRobustSerializable; +import org.eclipse.jdt.annotation.Nullable; + +import javax.annotation.concurrent.NotThreadSafe; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -32,60 +39,47 @@ import java.util.ArrayList; import java.util.List; -import javax.annotation.concurrent.NotThreadSafe; - -import org.eclipse.jdt.annotation.Nullable; - -import ch.njol.yggdrasil.Fields.FieldContext; -import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilExtendedSerializable; -import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilRobustEnum; -import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilRobustSerializable; -import ch.njol.yggdrasil.xml.YggXMLInputStream; -import ch.njol.yggdrasil.xml.YggXMLOutputStream; - /** * Yggdrasil is a simple data format to store object graphs. *

- * Yggdrasil uses String IDs to identify classes, thus all classes to be (de)serialised have to be registered to Yggdrasil before doing anything (they can also be registered while - * Yggdrasil is working, but you must make sure that all classes are registered in time when deserialising). A {@link ClassResolver} or {@link YggdrasilSerializer} can also be used - * to find classes and IDs dynamically. + * Yggdrasil uses String IDs to identify classes, thus all classes to be (de)serialized have to be registered to + * Yggdrasil before doing anything (they can also be registered while Yggdrasil is working, but you must make sure + * that all classes are registered in time when deserializing). A {@link ClassResolver} or + * {@link YggdrasilSerializer} can also be used to find classes and IDs dynamically. *

* Default behaviour *

- * A Java object can be serialised and deserialised if it is a primitive, a primitive wrapper, a String, an enum or {@link PseudoEnum} (both require an ID), or its class meets all - * of the following requirements: + * A Java object can be serialized and deserialized if it is a primitive, a primitive wrapper, a String, an enum or + * {@link PseudoEnum} (both require an ID), or its class meets all the following requirements: *

    *
  • It implements {@link YggdrasilSerializable} *
  • It has an ID assigned to it (using the methods described above) - *
  • It provides a nullary constructor (any access modifier) (in particular anonymous and non-static inner classes can't be serialised) - *
  • All its non-transient and non-static fields are serialisable according to these requirements + *
  • It provides a nullary constructor (any access modifier) (in particular anonymous and non-static inner classes can't be serialized) + *
  • All its non-transient and non-static fields are serializable according to these requirements *
*

* Yggdrasil will generate errors if an object loaded either has too many fields and/or is missing some in the stream. *

* Customisation *

- * Any object that does not meet the above requirements for serialisation can still be (de)serialised using an {@link YggdrasilSerializer} (useful for objects of an external API), - * or by implementing {@link YggdrasilExtendedSerializable}. + * Any object that does not meet the above requirements for serialisation can still be (de)serialized using an + * {@link YggdrasilSerializer} (useful for objects of an external API), or by implementing {@link YggdrasilExtendedSerializable}. *

- * The behaviour in case of an invalid or outdated stream can be defined likewise, or one can implement {@link YggdrasilRobustSerializable} or {@link YggdrasilRobustEnum} - * respectively. - * - * @author Peter Güttinger + * The behaviour in case of an invalid or outdated stream can be defined likewise, or one can + * implement {@link YggdrasilRobustSerializable} or {@link YggdrasilRobustEnum} respectively. */ -@SuppressWarnings("deprecation") @NotThreadSafe public final class Yggdrasil { /** - * Magic Number: "Ygg\0" + * Magic Number: "Ygg\0" (('Y' << 24) + ('g' << 16) + ('g' << 8) + '\0') *

* hex: 0x59676700 */ - public final static int MAGIC_NUMBER = ('Y' << 24) + ('g' << 16) + ('g' << 8) + '\0'; + public static final int MAGIC_NUMBER = 0x59676700; /** latest protocol version */ - public final static short LATEST_VERSION = 1; // version 2 is only one minor change currently + public static final short LATEST_VERSION = 1; // version 2 is only one minor change currently public final short version; @@ -98,7 +92,7 @@ public Yggdrasil() { this(LATEST_VERSION); } - public Yggdrasil(final short version) { + public Yggdrasil(short version) { if (version <= 0 || version > LATEST_VERSION) throw new YggdrasilException("Unsupported version number"); this.version = version; @@ -106,77 +100,68 @@ public Yggdrasil(final short version) { classResolvers.add(simpleClassResolver); } - public YggdrasilOutputStream newOutputStream(final OutputStream out) throws IOException { + public YggdrasilOutputStream newOutputStream(OutputStream out) throws IOException { return new DefaultYggdrasilOutputStream(this, out); } - public YggdrasilInputStream newInputStream(final InputStream in) throws IOException { + public YggdrasilInputStream newInputStream(InputStream in) throws IOException { return new DefaultYggdrasilInputStream(this, in); } - @Deprecated - public YggXMLOutputStream newXMLOutputStream(final OutputStream out) throws IOException { - return new YggXMLOutputStream(this, out); - } - - @Deprecated - public YggdrasilInputStream newXMLInputStream(final InputStream in) throws IOException { - return new YggXMLInputStream(this, in); + public void registerClassResolver(ClassResolver resolver) { + if (!classResolvers.contains(resolver)) + classResolvers.add(resolver); } - public void registerClassResolver(final ClassResolver r) { - if (!classResolvers.contains(r)) - classResolvers.add(r); - } - - public void registerSingleClass(final Class c, final String id) { - simpleClassResolver.registerClass(c, id); + public void registerSingleClass(Class type, String id) { + simpleClassResolver.registerClass(type, id); } /** * Registers a class and uses its {@link YggdrasilID} as id. */ - public void registerSingleClass(final Class c) { - final YggdrasilID id = c.getAnnotation(YggdrasilID.class); + public void registerSingleClass(Class type) { + YggdrasilID id = type.getAnnotation(YggdrasilID.class); if (id == null) - throw new IllegalArgumentException(c.toString()); - simpleClassResolver.registerClass(c, id.value()); + throw new IllegalArgumentException(type.toString()); + simpleClassResolver.registerClass(type, id.value()); } - public void registerFieldHandler(final FieldHandler h) { - if (!fieldHandlers.contains(h)) - fieldHandlers.add(h); + public void registerFieldHandler(FieldHandler handler) { + if (!fieldHandlers.contains(handler)) + fieldHandlers.add(handler); } - public final boolean isSerializable(final Class c) { + public boolean isSerializable(Class type) { try { - return c.isPrimitive() || c == Object.class || (Enum.class.isAssignableFrom(c) || PseudoEnum.class.isAssignableFrom(c)) && getIDNoError(c) != null || - ((YggdrasilSerializable.class.isAssignableFrom(c) || getSerializer(c) != null) && newInstance(c) != c);// whatever, just make true out if it (null is a valid return value) - } catch (final StreamCorruptedException e) { // thrown by newInstance if the class does not provide a correct constructor or is abstract + return type.isPrimitive() || type == Object.class || (Enum.class.isAssignableFrom(type) || + PseudoEnum.class.isAssignableFrom(type)) && getIDNoError(type) != null || + ((YggdrasilSerializable.class.isAssignableFrom(type) || getSerializer(type) != null) && newInstance(type) != type); // whatever, just make true out if it (null is a valid return value) + } catch (StreamCorruptedException e) { // thrown by newInstance if the class does not provide a correct constructor or is abstract return false; - } catch (final NotSerializableException e) { + } catch (NotSerializableException e) { return false; } } @Nullable - YggdrasilSerializer getSerializer(final Class c) { - for (final ClassResolver r : classResolvers) { - if (r instanceof YggdrasilSerializer && r.getID(c) != null) - return (YggdrasilSerializer) r; + YggdrasilSerializer getSerializer(Class type) { + for (ClassResolver resolver : classResolvers) { + if (resolver instanceof YggdrasilSerializer && resolver.getID(type) != null) + return (YggdrasilSerializer) resolver; } return null; } - public Class getClass(final String id) throws StreamCorruptedException { + public Class getClass(String id) throws StreamCorruptedException { if ("Object".equals(id)) return Object.class; - for (final ClassResolver r : classResolvers) { - final Class c = r.getClass(id); - if (c != null) { // TODO error if not serialisable? - assert Tag.byName(id) == null && (Tag.getType(c) == Tag.T_OBJECT || Tag.getType(c) == Tag.T_ENUM) : "Tag IDs should not be matched: " + id + " (class resolver: " + r + ")"; - assert id.equals(r.getID(c)) : r + " returned " + c + " for id " + id + ", but returns id " + r.getID(c) + " for that class"; - return c; + for (ClassResolver resolver : classResolvers) { + Class type = resolver.getClass(id); + if (type != null) { // TODO error if not serializable? + assert Tag.byName(id) == null && (Tag.getType(type) == Tag.T_OBJECT || Tag.getType(type) == Tag.T_ENUM) : "Tag IDs should not be matched: " + id + " (class resolver: " + resolver + ")"; + assert id.equals(resolver.getID(type)) : resolver + " returned " + type + " for id " + id + ", but returns id " + resolver.getID(type) + " for that class"; + return type; } } throw new StreamCorruptedException("No class found for ID " + id); @@ -184,35 +169,35 @@ public Class getClass(final String id) throws StreamCorruptedException { @SuppressWarnings({"unchecked", "rawtypes"}) @Nullable - private String getIDNoError(Class c) { - if (c == Object.class) + private String getIDNoError(Class type) { + if (type == Object.class) return "Object"; - assert Tag.getType(c) == Tag.T_OBJECT || Tag.getType(c) == Tag.T_ENUM; - if (Enum.class.isAssignableFrom(c) && c.getSuperclass() != Enum.class) { - final Class s = c.getSuperclass(); - assert s != null; // c cannot be Object.class - c = s; + assert Tag.getType(type) == Tag.T_OBJECT || Tag.getType(type) == Tag.T_ENUM; + if (Enum.class.isAssignableFrom(type) && type.getSuperclass() != Enum.class) { + Class s = type.getSuperclass(); + assert s != null; // type cannot be Object.class + type = s; } - if (PseudoEnum.class.isAssignableFrom(c)) - c = PseudoEnum.getDeclaringClass((Class) c); - for (final ClassResolver r : classResolvers) { - final String id = r.getID(c); + if (PseudoEnum.class.isAssignableFrom(type)) + type = PseudoEnum.getDeclaringClass((Class) type); + for (ClassResolver resolver : classResolvers) { + String id = resolver.getID(type); if (id != null) { - assert Tag.byName(id) == null : "Class IDs should not match Tag IDs: " + id + " (class resolver: " + r + ")"; - final Class c2 = r.getClass(id); - assert c2 != null && (r instanceof YggdrasilSerializer ? id.equals(r.getID(c2)) : r.getClass(id) == c) : r + " returned id " + id + " for " + c + ", but returns " + c2 + " for that id"; + assert Tag.byName(id) == null : "Class IDs should not match Tag IDs: " + id + " (class resolver: " + resolver + ")"; + Class c2 = resolver.getClass(id); + assert c2 != null && (resolver instanceof YggdrasilSerializer ? id.equals(resolver.getID(c2)) : resolver.getClass(id) == type) : resolver + " returned id " + id + " for " + type + ", but returns " + c2 + " for that id"; return id; } } return null; } - public String getID(final Class c) throws NotSerializableException { - final String id = getIDNoError(c); + public String getID(Class type) throws NotSerializableException { + String id = getIDNoError(type); if (id == null) - throw new NotSerializableException("No ID found for " + c); - if (!isSerializable(c)) - throw new NotSerializableException(c.getCanonicalName()); + throw new NotSerializableException("No ID found for " + type); + if (!isSerializable(type)) + throw new NotSerializableException(type.getCanonicalName()); return id; } @@ -220,151 +205,125 @@ public String getID(final Class c) throws NotSerializableException { * Gets the ID of a field. *

* This method performs no checks on the given field. - * - * @param f + * * @return The field's id as given by its {@link YggdrasilID} annotation, or its name if it's not annotated. */ - public static String getID(final Field f) { - final YggdrasilID yid = f.getAnnotation(YggdrasilID.class); + public static String getID(Field field) { + YggdrasilID yid = field.getAnnotation(YggdrasilID.class); if (yid != null) { return yid.value(); } - return "" + f.getName(); + return "" + field.getName(); } - @SuppressWarnings("null") - public static String getID(final Enum e) { + public static String getID(Enum e) { try { return getID(e.getDeclaringClass().getDeclaredField(e.name())); - } catch (final NoSuchFieldException ex) { + } catch (NoSuchFieldException ex) { assert false : e; return "" + e.name(); } } - @SuppressWarnings({"unchecked", "null", "unused"}) - public static > Enum getEnumConstant(final Class c, final String id) throws StreamCorruptedException { - final Field[] fields = c.getDeclaredFields(); - for (final Field f : fields) { - assert f != null; - if (getID(f).equals(id)) - return Enum.valueOf(c, f.getName()); + @SuppressWarnings({"unchecked", "unused"}) + public static > Enum getEnumConstant(Class type, String id) throws StreamCorruptedException { + Field[] fields = type.getDeclaredFields(); + for (Field field : fields) { + assert field != null; + if (getID(field).equals(id)) + return Enum.valueOf(type, field.getName()); } - if (YggdrasilRobustEnum.class.isAssignableFrom(c)) { - final Object[] cs = c.getEnumConstants(); - if (cs.length == 0) - throw new StreamCorruptedException(c + " does not have any enum constants"); - final Enum e = ((YggdrasilRobustEnum) cs[0]).excessiveConstant(id); - if (e == null) - throw new YggdrasilException("YggdrasilRobustEnum " + c + " returned null from excessiveConstant(" + id + ")"); - if (!c.isInstance(e)) - throw new YggdrasilException(c + " returned a foreign enum constant: " + e.getClass() + "." + e); + if (YggdrasilRobustEnum.class.isAssignableFrom(type)) { + Object[] constants = type.getEnumConstants(); + if (constants.length == 0) + throw new StreamCorruptedException(type + " does not have any enum constants"); + Enum e = ((YggdrasilRobustEnum) constants[0]).excessiveConstant(id); + if (!type.isInstance(e)) + throw new YggdrasilException(type + " returned a foreign enum constant: " + e.getClass() + "." + e); return (Enum) e; } // TODO use field handlers/new enum handlers - throw new StreamCorruptedException("Enum constant " + id + " does not exist in " + c); + throw new StreamCorruptedException("Enum constant " + id + " does not exist in " + type); } - public void excessiveField(final Object o, final FieldContext field) throws StreamCorruptedException { - for (final FieldHandler h : fieldHandlers) { - if (h.excessiveField(o, field)) + public void excessiveField(Object object, FieldContext field) throws StreamCorruptedException { + for (FieldHandler handler : fieldHandlers) { + if (handler.excessiveField(object, field)) return; } - throw new StreamCorruptedException("Excessive field " + field.id + " in class " + o.getClass().getCanonicalName() + " was not handled"); + throw new StreamCorruptedException("Excessive field " + field.id + " in class " + object.getClass().getCanonicalName() + " was not handled"); } - public void missingField(final Object o, final Field f) throws StreamCorruptedException { - for (final FieldHandler h : fieldHandlers) { - if (h.missingField(o, f)) + public void missingField(Object object, Field field) throws StreamCorruptedException { + for (FieldHandler handler : fieldHandlers) { + if (handler.missingField(object, field)) return; } - throw new StreamCorruptedException("Missing field " + getID(f) + " in class " + o.getClass().getCanonicalName() + " was not handled"); + throw new StreamCorruptedException("Missing field " + getID(field) + " in class " + object.getClass().getCanonicalName() + " was not handled"); } - public void incompatibleField(final Object o, final Field f, final FieldContext field) throws StreamCorruptedException { - for (final FieldHandler h : fieldHandlers) { - if (h.incompatibleField(o, f, field)) + public void incompatibleField(Object object, Field field, FieldContext context) throws StreamCorruptedException { + for (FieldHandler handler : fieldHandlers) { + if (handler.incompatibleField(object, field, context)) return; } - throw new StreamCorruptedException("Incompatible field " + getID(f) + " in class " + o.getClass().getCanonicalName() + " of incompatible " + field.getType() + " was not handled"); + throw new StreamCorruptedException("Incompatible field " + getID(field) + " in class " + object.getClass().getCanonicalName() + " of incompatible " + context.getType() + " was not handled"); } - public void saveToFile(final Object o, final File f) throws IOException { - FileOutputStream fout = null; - YggdrasilOutputStream yout = null; - try { - fout = new FileOutputStream(f); - yout = newOutputStream(fout); - yout.writeObject(o); + public void saveToFile(Object object, File file) throws IOException { + try ( + FileOutputStream fout = new FileOutputStream(file); + YggdrasilOutputStream yout = newOutputStream(fout) + ) { + yout.writeObject(object); yout.flush(); - } finally { - if (yout != null) - yout.close(); - if (fout != null) - fout.close(); } } @Nullable - public T loadFromFile(final File f, final Class expectedType) throws IOException { - FileInputStream fin = null; - YggdrasilInputStream yin = null; - try { - fin = new FileInputStream(f); - yin = newInputStream(fin); + public T loadFromFile(File file, Class expectedType) throws IOException { + try ( + FileInputStream fin = new FileInputStream(file); + YggdrasilInputStream yin = newInputStream(fin) + ) { return yin.readObject(expectedType); - } finally { - if (yin != null) - yin.close(); - if (fin != null) - fin.close(); } } @SuppressWarnings({"rawtypes", "unchecked"}) @Nullable - final Object newInstance(final Class c) throws StreamCorruptedException, NotSerializableException { - final YggdrasilSerializer s = getSerializer(c); - if (s != null) { - if (!s.canBeInstantiated(c)) { // only used by isSerializable - return null if OK, throw an YggdrasilException if not + Object newInstance(Class type) throws StreamCorruptedException, NotSerializableException { + YggdrasilSerializer serializer = getSerializer(type); + if (serializer != null) { + if (!serializer.canBeInstantiated(type)) { // only used by isSerializable - return null if OK, throw an YggdrasilException if not try { - s.deserialize(c, new Fields(this)); - } catch (final StreamCorruptedException e) {} + serializer.deserialize(type, new Fields(this)); + } catch (StreamCorruptedException ignored) {} return null; } - final Object o = s.newInstance(c); + Object o = serializer.newInstance(type); if (o == null) - throw new YggdrasilException("YggdrasilSerializer " + s + " returned null from newInstance(" + c + ")"); + throw new YggdrasilException("YggdrasilSerializer " + serializer + " returned null from newInstance(" + type + ")"); return o; } // try whether a nullary constructor exists try { - final Constructor constr = c.getDeclaredConstructor(); + Constructor constr = type.getDeclaredConstructor(); constr.setAccessible(true); return constr.newInstance(); - } catch (final NoSuchMethodException e) { - throw new StreamCorruptedException("Cannot create an instance of " + c + " because it has no nullary constructor"); - } catch (final SecurityException e) { - throw new StreamCorruptedException("Cannot create an instance of " + c + " because the security manager didn't allow it"); - } catch (final InstantiationException e) { - throw new StreamCorruptedException("Cannot create an instance of " + c + " because it is abstract"); - } catch (final IllegalAccessException e) { - e.printStackTrace(); - assert false; - return null; - } catch (final IllegalArgumentException e) { + } catch (NoSuchMethodException e) { + throw new StreamCorruptedException("Cannot create an instance of " + type + " because it has no nullary constructor"); + } catch (SecurityException e) { + throw new StreamCorruptedException("Cannot create an instance of " + type + " because the security manager didn't allow it"); + } catch (InstantiationException e) { + throw new StreamCorruptedException("Cannot create an instance of " + type + " because it is abstract"); + } catch (IllegalAccessException | IllegalArgumentException e) { e.printStackTrace(); assert false; return null; - } catch (final InvocationTargetException e) { + } catch (InvocationTargetException e) { throw new RuntimeException(e); } } - // TODO command line, e.g. convert to XML - public static void main(final String[] args) { - System.err.println("Command line not supported yet"); - System.exit(1); - } - } diff --git a/src/main/java/ch/njol/yggdrasil/YggdrasilException.java b/src/main/java/ch/njol/yggdrasil/YggdrasilException.java index ee085237395..8ffb86dc39a 100644 --- a/src/main/java/ch/njol/yggdrasil/YggdrasilException.java +++ b/src/main/java/ch/njol/yggdrasil/YggdrasilException.java @@ -19,24 +19,24 @@ package ch.njol.yggdrasil; /** - * Thrown if the object(s) that should be saved/loaded with Yggdrasil do not comply with its requirements, or if Yggdrasil is used incorrectly. + * Thrown if the object(s) that should be saved/loaded with Yggdrasil do + * not comply with its requirements, or if Yggdrasil is used incorrectly. *

* A detail message will always be supplied, so fixing these errors should be trivial. - * - * @author Peter Güttinger */ -public class YggdrasilException extends RuntimeException { +public final class YggdrasilException extends RuntimeException { + private static final long serialVersionUID = -6130660396780458226L; - public YggdrasilException(final String message) { + public YggdrasilException(String message) { super(message); } - public YggdrasilException(final String message, final Throwable cause) { + public YggdrasilException(String message, Throwable cause) { super(message, cause); } - public YggdrasilException(final Throwable cause) { + public YggdrasilException(Throwable cause) { super(cause.getClass().getSimpleName() + (cause.getMessage() == null ? "" : ": " + cause.getMessage()), cause); } diff --git a/src/main/java/ch/njol/yggdrasil/YggdrasilID.java b/src/main/java/ch/njol/yggdrasil/YggdrasilID.java index ab03ff3f737..47b529a174a 100644 --- a/src/main/java/ch/njol/yggdrasil/YggdrasilID.java +++ b/src/main/java/ch/njol/yggdrasil/YggdrasilID.java @@ -26,12 +26,12 @@ /** * Can be used to set a class's or field's id used by Yggdrasil. - * - * @author Peter Güttinger */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD}) @Documented public @interface YggdrasilID { + String value(); + } diff --git a/src/main/java/ch/njol/yggdrasil/YggdrasilInputStream.java b/src/main/java/ch/njol/yggdrasil/YggdrasilInputStream.java index a9836a8d11b..3fd09daf89b 100644 --- a/src/main/java/ch/njol/yggdrasil/YggdrasilInputStream.java +++ b/src/main/java/ch/njol/yggdrasil/YggdrasilInputStream.java @@ -18,9 +18,8 @@ */ package ch.njol.yggdrasil; -import static ch.njol.yggdrasil.Tag.T_NULL; -import static ch.njol.yggdrasil.Tag.T_REFERENCE; -import static ch.njol.yggdrasil.Tag.getType; +import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilExtendedSerializable; +import org.eclipse.jdt.annotation.Nullable; import java.io.Closeable; import java.io.IOException; @@ -29,70 +28,58 @@ import java.util.ArrayList; import java.util.List; -import org.eclipse.jdt.annotation.Nullable; - -import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilExtendedSerializable; +import static ch.njol.yggdrasil.Tag.T_NULL; +import static ch.njol.yggdrasil.Tag.T_REFERENCE; +import static ch.njol.yggdrasil.Tag.getType; public abstract class YggdrasilInputStream implements Closeable { protected final Yggdrasil yggdrasil; - protected YggdrasilInputStream(final Yggdrasil yggdrasil) { + protected YggdrasilInputStream(Yggdrasil yggdrasil) { this.yggdrasil = yggdrasil; } - // Tag - protected abstract Tag readTag() throws IOException; - // Primitives - protected abstract Object readPrimitive(Tag type) throws IOException; protected abstract Object readPrimitive_(Tag type) throws IOException; - // String - protected abstract String readString() throws IOException; - // Array - protected abstract Class readArrayComponentType() throws IOException; protected abstract int readArrayLength() throws IOException; - private final void readArrayContents(final Object array) throws IOException { + private void readArrayContents(Object array) throws IOException { if (array.getClass().getComponentType().isPrimitive()) { - final int length = Array.getLength(array); - final Tag type = getType(array.getClass().getComponentType()); - for (int i = 0; i < length; i++) { + int length = Array.getLength(array); + Tag type = getType(array.getClass().getComponentType()); + for (int i = 0; i < length; i++) Array.set(array, i, readPrimitive_(type)); - } } else { - for (int i = 0; i < ((Object[]) array).length; i++) { + for (int i = 0; i < ((Object[]) array).length; i++) ((Object[]) array)[i] = readObject(); - } } } - // Enum - protected abstract Class readEnumType() throws IOException; protected abstract String readEnumID() throws IOException; @SuppressWarnings({"unchecked", "rawtypes"}) - private final Object readEnum() throws IOException { - final Class c = readEnumType(); - final String id = readEnumID(); + private Object readEnum() throws IOException { + Class c = readEnumType(); + String id = readEnumID(); if (Enum.class.isAssignableFrom(c)) { return Yggdrasil.getEnumConstant((Class) c, id); } else if (PseudoEnum.class.isAssignableFrom(c)) { - final Object o = PseudoEnum.valueOf((Class) c, id); + Object o = PseudoEnum.valueOf((Class) c, id); if (o != null) return o; // if (YggdrasilRobustPseudoEnum.class.isAssignableFrom(c)) { -// // TODO create this and a handler (for Enums as well) +// TODO create this and a handler (for Enums as well) // } throw new StreamCorruptedException("Enum constant " + id + " does not exist in " + c); } else { @@ -100,115 +87,106 @@ private final Object readEnum() throws IOException { } } - // Class - protected abstract Class readClass() throws IOException; - // Reference - protected abstract int readReference() throws IOException; - // generic Object - protected abstract Class readObjectType() throws IOException; protected abstract short readNumFields() throws IOException; protected abstract String readFieldID() throws IOException; - private final Fields readFields() throws IOException { - final Fields fields = new Fields(yggdrasil); - final short numFields = readNumFields(); + private Fields readFields() throws IOException { + Fields fields = new Fields(yggdrasil); + short numFields = readNumFields(); for (int i = 0; i < numFields; i++) { - final String id = readFieldID(); - final Tag t = readTag(); - if (t.isPrimitive()) - fields.putPrimitive(id, readPrimitive(t)); + String id = readFieldID(); + Tag tag = readTag(); + if (tag.isPrimitive()) + fields.putPrimitive(id, readPrimitive(tag)); else - fields.putObject(id, readObject(t)); + fields.putObject(id, readObject(tag)); } return fields; } - // any Objects - private final List readObjects = new ArrayList<>(); @Nullable public final Object readObject() throws IOException { - final Tag t = readTag(); - return readObject(t); + Tag tag = readTag(); + return readObject(tag); } @SuppressWarnings("unchecked") @Nullable - public final T readObject(final Class expectedType) throws IOException { - final Tag t = readTag(); - final Object o = readObject(t); - if (o != null && !expectedType.isInstance(o)) - throw new StreamCorruptedException("Object " + o + " is of " + o.getClass() + " but expected " + expectedType); - return (T) o; + public final T readObject(Class expectedType) throws IOException { + Tag tag = readTag(); + Object object = readObject(tag); + if (object != null && !expectedType.isInstance(object)) + throw new StreamCorruptedException("Object " + object + " is of " + object.getClass() + " but expected " + expectedType); + return (T) object; } - @SuppressWarnings({"rawtypes", "unchecked", "null", "unused"}) + @SuppressWarnings({"rawtypes", "unchecked"}) @Nullable - private final Object readObject(final Tag t) throws IOException { - if (t == T_NULL) + private Object readObject(Tag tag) throws IOException { + if (tag == T_NULL) return null; - if (t == T_REFERENCE) { - final int ref = readReference(); + if (tag == T_REFERENCE) { + int ref = readReference(); if (ref < 0 || ref >= readObjects.size()) throw new StreamCorruptedException("Invalid reference " + ref + ", " + readObjects.size() + " object(s) read so far"); - final Object o = readObjects.get(ref); - if (o == null) + Object object = readObjects.get(ref); + if (object == null) throw new StreamCorruptedException("Reference to uninstantiable object: " + ref); - return o; + return object; } - final Object o; - switch (t) { + Object object; + switch (tag) { case T_ARRAY: { - final Class c = readArrayComponentType(); - o = Array.newInstance(c, readArrayLength()); - assert o != null; - readObjects.add(o); - readArrayContents(o); - return o; + Class type = readArrayComponentType(); + object = Array.newInstance(type, readArrayLength()); + readObjects.add(object); + readArrayContents(object); + return object; } case T_CLASS: - o = readClass(); + object = readClass(); break; case T_ENUM: - o = readEnum(); + object = readEnum(); break; case T_STRING: - o = readString(); + object = readString(); break; case T_OBJECT: { - final Class c = readObjectType(); - final YggdrasilSerializer s = yggdrasil.getSerializer(c); + Class c = readObjectType(); + YggdrasilSerializer s = yggdrasil.getSerializer(c); if (s != null && !s.canBeInstantiated(c)) { - final int ref = readObjects.size(); + int ref = readObjects.size(); readObjects.add(null); - final Fields fields = readFields(); - o = s.deserialize(c, fields); - if (o == null) + Fields fields = readFields(); + object = s.deserialize(c, fields); + if (object == null) throw new YggdrasilException("YggdrasilSerializer " + s + " returned null from deserialize(" + c + "," + fields + ")"); - readObjects.set(ref, o); + readObjects.set(ref, object); } else { - o = yggdrasil.newInstance(c); - if (o == null) + object = yggdrasil.newInstance(c); + if (object == null) throw new StreamCorruptedException(); - readObjects.add(o); - final Fields fields = readFields(); + readObjects.add(object); + Fields fields = readFields(); if (s != null) { - s.deserialize(o, fields); - } else if (o instanceof YggdrasilExtendedSerializable) { - ((YggdrasilExtendedSerializable) o).deserialize(fields); + s.deserialize(object, fields); + } else if (object instanceof YggdrasilExtendedSerializable) { + ((YggdrasilExtendedSerializable) object).deserialize(fields); } else { - fields.setFields(o); + fields.setFields(object); } } - return o; + return object; } case T_BOOLEAN_OBJ: case T_BYTE_OBJ: @@ -218,9 +196,9 @@ private final Object readObject(final Tag t) throws IOException { case T_INT_OBJ: case T_LONG_OBJ: case T_SHORT_OBJ: - final Tag p = t.getPrimitive(); - assert p != null; - o = readPrimitive(p); + Tag primitive = tag.getPrimitive(); + assert primitive != null; + object = readPrimitive(primitive); break; case T_BYTE: case T_BOOLEAN: @@ -231,49 +209,12 @@ private final Object readObject(final Tag t) throws IOException { case T_LONG: case T_SHORT: throw new StreamCorruptedException(); - case T_REFERENCE: - case T_NULL: default: assert false; throw new StreamCorruptedException(); } - readObjects.add(o); - return o; + readObjects.add(object); + return object; } -// private final static class Validation implements Comparable { -// private final ObjectInputValidation v; -// private final int prio; -// -// public Validation(final ObjectInputValidation v, final int prio) { -// this.v = v; -// this.prio = prio; -// } -// -// private void validate() throws InvalidObjectException { -// v.validateObject(); -// } -// -// @Override -// public int compareTo(final Validation o) { -// return o.prio - prio; -// } -// } -// -// private final SortedSet validations = new TreeSet<>(); -// -// public void registerValidation(final ObjectInputValidation v, final int prio) throws NotActiveException, InvalidObjectException { -// if (depth == 0) -// throw new NotActiveException("stream inactive"); -// if (v == null) -// throw new InvalidObjectException("null callback"); -// validations.add(new Validation(v, prio)); -// } -// -// private void validate() throws InvalidObjectException { -// for (final Validation v : validations) -// v.validate(); -// validations.clear(); // if multiple objects are written to the stream this method will be called multiple times -// } - } diff --git a/src/main/java/ch/njol/yggdrasil/YggdrasilOutputStream.java b/src/main/java/ch/njol/yggdrasil/YggdrasilOutputStream.java index f296d2aac3b..7f0124f9573 100644 --- a/src/main/java/ch/njol/yggdrasil/YggdrasilOutputStream.java +++ b/src/main/java/ch/njol/yggdrasil/YggdrasilOutputStream.java @@ -18,14 +18,9 @@ */ package ch.njol.yggdrasil; -import static ch.njol.yggdrasil.Tag.T_ARRAY; -import static ch.njol.yggdrasil.Tag.T_CLASS; -import static ch.njol.yggdrasil.Tag.T_ENUM; -import static ch.njol.yggdrasil.Tag.T_NULL; -import static ch.njol.yggdrasil.Tag.T_OBJECT; -import static ch.njol.yggdrasil.Tag.T_REFERENCE; -import static ch.njol.yggdrasil.Tag.T_STRING; -import static ch.njol.yggdrasil.Tag.getType; +import ch.njol.yggdrasil.Fields.FieldContext; +import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilExtendedSerializable; +import org.eclipse.jdt.annotation.Nullable; import java.io.Closeable; import java.io.Flushable; @@ -34,130 +29,115 @@ import java.lang.reflect.Array; import java.util.IdentityHashMap; -import org.eclipse.jdt.annotation.Nullable; - -import ch.njol.yggdrasil.Fields.FieldContext; -import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilExtendedSerializable; +import static ch.njol.yggdrasil.Tag.T_ARRAY; +import static ch.njol.yggdrasil.Tag.T_CLASS; +import static ch.njol.yggdrasil.Tag.T_ENUM; +import static ch.njol.yggdrasil.Tag.T_NULL; +import static ch.njol.yggdrasil.Tag.T_OBJECT; +import static ch.njol.yggdrasil.Tag.T_REFERENCE; +import static ch.njol.yggdrasil.Tag.T_STRING; +import static ch.njol.yggdrasil.Tag.getType; public abstract class YggdrasilOutputStream implements Flushable, Closeable { protected final Yggdrasil yggdrasil; - protected YggdrasilOutputStream(final Yggdrasil yggdrasil) { + protected YggdrasilOutputStream(Yggdrasil yggdrasil) { this.yggdrasil = yggdrasil; } - // Tag - - protected abstract void writeTag(Tag t) throws IOException; - - // Null + protected abstract void writeTag(Tag tag) throws IOException; - private final void writeNull() throws IOException { + private void writeNull() throws IOException { writeTag(T_NULL); } - // Primitives + protected abstract void writePrimitiveValue(Object object) throws IOException; - protected abstract void writePrimitiveValue(Object o) throws IOException; + protected abstract void writePrimitive_(Object object) throws IOException; - protected abstract void writePrimitive_(Object o) throws IOException; - - private final void writePrimitive(final Object o) throws IOException { - final Tag t = Tag.getType(o.getClass()); - assert t.isWrapper(); - final Tag p = t.getPrimitive(); - assert p != null; - writeTag(p); - writePrimitiveValue(o); + private void writePrimitive(Object object) throws IOException { + Tag tag = Tag.getType(object.getClass()); + assert tag.isWrapper(); + Tag primitive = tag.getPrimitive(); + assert primitive != null; + writeTag(primitive); + writePrimitiveValue(object); } - private final void writeWrappedPrimitive(final Object o) throws IOException { - final Tag t = Tag.getType(o.getClass()); - assert t.isWrapper(); - writeTag(t); - writePrimitiveValue(o); + private void writeWrappedPrimitive(Object object) throws IOException { + Tag tag = Tag.getType(object.getClass()); + assert tag.isWrapper(); + writeTag(tag); + writePrimitiveValue(object); } - // String - - protected abstract void writeStringValue(String s) throws IOException; + protected abstract void writeStringValue(String string) throws IOException; - private final void writeString(final String s) throws IOException { + private void writeString(String string) throws IOException { writeTag(T_STRING); - writeStringValue(s); + writeStringValue(string); } - // Array - protected abstract void writeArrayComponentType(Class componentType) throws IOException; protected abstract void writeArrayLength(int length) throws IOException; protected abstract void writeArrayEnd() throws IOException; - private final void writeArray(final Object array) throws IOException { - final int length = Array.getLength(array); - final Class ct = array.getClass().getComponentType(); - assert ct != null; + private void writeArray(Object array) throws IOException { + int length = Array.getLength(array); + Class type = array.getClass().getComponentType(); + assert type != null; writeTag(T_ARRAY); - writeArrayComponentType(ct); + writeArrayComponentType(type); writeArrayLength(length); - if (ct.isPrimitive()) { + if (type.isPrimitive()) { for (int i = 0; i < length; i++) { - final Object p = Array.get(array, i); - assert p != null; - writePrimitive_(p); + Object object = Array.get(array, i); + assert object != null; + writePrimitive_(object); } writeArrayEnd(); } else { - for (final Object o : (Object[]) array) - writeObject(o); + for (Object object : (Object[]) array) + writeObject(object); writeArrayEnd(); } } - // Enum - protected abstract void writeEnumType(String type) throws IOException; protected abstract void writeEnumID(String id) throws IOException; - private final void writeEnum(final Enum o) throws IOException { + private void writeEnum(Enum object) throws IOException { writeTag(T_ENUM); - final Class c = o.getDeclaringClass(); - assert c != null; - writeEnumType(yggdrasil.getID(c)); - writeEnumID(Yggdrasil.getID(o)); + Class type = object.getDeclaringClass(); + writeEnumType(yggdrasil.getID(type)); + writeEnumID(Yggdrasil.getID(object)); } - private final void writeEnum(final PseudoEnum o) throws IOException { + private void writeEnum(PseudoEnum object) throws IOException { writeTag(T_ENUM); - writeEnumType(yggdrasil.getID(o.getDeclaringClass())); - writeEnumID(o.name()); + writeEnumType(yggdrasil.getID(object.getDeclaringClass())); + writeEnumID(object.name()); } - // Class + protected abstract void writeClassType(Class type) throws IOException; - protected abstract void writeClassType(Class c) throws IOException; - - private final void writeClass(final Class c) throws IOException { + private void writeClass(Class type) throws IOException { writeTag(T_CLASS); - writeClassType(c); + writeClassType(type); } - // Reference - protected abstract void writeReferenceID(int ref) throws IOException; - protected final void writeReference(final int ref) throws IOException { + protected final void writeReference(int ref) throws IOException { assert ref >= 0; writeTag(T_REFERENCE); writeReferenceID(ref); } - // generic Objects - protected abstract void writeObjectType(String type) throws IOException; protected abstract void writeNumFields(short numFields) throws IOException; @@ -167,88 +147,85 @@ protected final void writeReference(final int ref) throws IOException { protected abstract void writeObjectEnd() throws IOException; @SuppressWarnings({"rawtypes", "unchecked"}) - private final void writeGenericObject(final Object o, int ref) throws IOException { - final Class c = o.getClass(); - assert c != null; - if (!yggdrasil.isSerializable(c)) - throw new NotSerializableException(c.getName()); - final Fields fields; - final YggdrasilSerializer s = yggdrasil.getSerializer(c); - if (s != null) { - fields = s.serialize(o); - if (!s.canBeInstantiated(c)) { + private void writeGenericObject(Object object, int ref) throws IOException { + Class type = object.getClass(); + assert type != null; + if (!yggdrasil.isSerializable(type)) + throw new NotSerializableException(type.getName()); + Fields fields; + YggdrasilSerializer serializer = yggdrasil.getSerializer(type); + if (serializer != null) { + fields = serializer.serialize(object); + if (!serializer.canBeInstantiated(type)) { ref = ~ref; // ~ instead of - to also get a negative value if ref is 0 - writtenObjects.put(o, ref); + writtenObjects.put(object, ref); } - } else if (o instanceof YggdrasilExtendedSerializable) { - fields = ((YggdrasilExtendedSerializable) o).serialize(); + } else if (object instanceof YggdrasilExtendedSerializable) { + fields = ((YggdrasilExtendedSerializable) object).serialize(); } else { - fields = new Fields(o, yggdrasil); + fields = new Fields(object, yggdrasil); } if (fields.size() > Short.MAX_VALUE) - throw new YggdrasilException("Class " + c.getCanonicalName() + " has too many fields (" + fields.size() + ")"); + throw new YggdrasilException("Class " + type.getCanonicalName() + " has too many fields (" + fields.size() + ")"); writeTag(T_OBJECT); - writeObjectType(yggdrasil.getID(c)); + writeObjectType(yggdrasil.getID(type)); writeNumFields((short) fields.size()); - for (final FieldContext f : fields) { - writeFieldID(f.id); - if (f.isPrimitive()) - writePrimitive(f.getPrimitive()); + for (FieldContext field : fields) { + writeFieldID(field.id); + if (field.isPrimitive()) + writePrimitive(field.getPrimitive()); else - writeObject(f.getObject()); + writeObject(field.getObject()); } writeObjectEnd(); if (ref < 0) - writtenObjects.put(o, ~ref); + writtenObjects.put(object, ~ref); } - // any Objects - private int nextObjectID = 0; private final IdentityHashMap writtenObjects = new IdentityHashMap<>(); - public final void writeObject(final @Nullable Object o) throws IOException { - if (o == null) { + public final void writeObject(@Nullable Object object) throws IOException { + if (object == null) { writeNull(); return; } - if (writtenObjects.containsKey(o)) { - final int ref = writtenObjects.get(o); + if (writtenObjects.containsKey(object)) { + int ref = writtenObjects.get(object); if (ref < 0) - throw new YggdrasilException("Uninstantiable object " + o + " is referenced in its fields' graph"); + throw new YggdrasilException("Uninstantiable object " + object + " is referenced in its fields' graph"); writeReference(ref); return; } - final int ref = nextObjectID; + int ref = nextObjectID; nextObjectID++; - writtenObjects.put(o, ref); - final Tag type = getType(o.getClass()); + writtenObjects.put(object, ref); + Tag type = getType(object.getClass()); if (type.isWrapper()) { - writeWrappedPrimitive(o); + writeWrappedPrimitive(object); return; } switch (type) { case T_ARRAY: - writeArray(o); + writeArray(object); return; case T_STRING: - writeString((String) o); + writeString((String) object); return; case T_ENUM: - if (o instanceof Enum) - writeEnum((Enum) o); + if (object instanceof Enum) + writeEnum((Enum) object); else - writeEnum((PseudoEnum) o); + writeEnum((PseudoEnum) object); return; case T_CLASS: - writeClass((Class) o); + writeClass((Class) object); return; case T_OBJECT: - writeGenericObject(o, ref); + writeGenericObject(object, ref); return; - //$CASES-OMITTED$ default: throw new YggdrasilException("unhandled type " + type); } diff --git a/src/main/java/ch/njol/yggdrasil/YggdrasilSerializable.java b/src/main/java/ch/njol/yggdrasil/YggdrasilSerializable.java index 3908c434aa1..b9b4fac355b 100644 --- a/src/main/java/ch/njol/yggdrasil/YggdrasilSerializable.java +++ b/src/main/java/ch/njol/yggdrasil/YggdrasilSerializable.java @@ -27,20 +27,17 @@ import ch.njol.yggdrasil.Fields.FieldContext; /** - * Marks a class as serialisable by Yggdrasil. + * Marks a class as serializable by Yggdrasil. *

- * Enums don't have to implement this interface to be serialisable, but can implement {@link YggdrasilRobustEnum}. - * - * @author Peter Güttinger + * Enums don't have to implement this interface to be serializable, but can implement {@link YggdrasilRobustEnum}. */ public interface YggdrasilSerializable { /** - * A class that has had fields added, changed, or removed from it should implement this interface to handle the now invalid/missing fields that may still be read from stream. - * - * @author Peter Güttinger + * A class that has had fields added, changed, or removed from it should implement this + * interface to handle the now invalid/missing fields that may still be read from stream. */ - public static interface YggdrasilRobustSerializable extends YggdrasilSerializable { + interface YggdrasilRobustSerializable extends YggdrasilSerializable { /** * Called if a field that was read from stream is of an incompatible type to the existing field in this class. @@ -48,19 +45,20 @@ public static interface YggdrasilRobustSerializable extends YggdrasilSerializabl * @param field The Java field * @param value The field read from stream * @return Whether the field was handled. If false, - * yggdrasil.{@link Yggdrasil#incompatibleField(Object, Field, FieldContext) incompatibleField}(this, field, value) will be called. + * yggdrasil.{@link Yggdrasil#incompatibleField(Object, Field, FieldContext) incompatibleField}(this, field, value) + * will be called. */ - @SuppressWarnings("null") - public boolean incompatibleField(@NonNull Field field, @NonNull FieldContext value) throws StreamCorruptedException; + boolean incompatibleField(@NonNull Field field, @NonNull FieldContext value) throws StreamCorruptedException; /** * Called if a field was read from stream which does not exist in this class. * * @param field The field read from stream - * @return Whether the field was handled. If false, yggdrasil.{@link Yggdrasil#excessiveField(Object, FieldContext) excessiveField}(this, field) will be called. + * @return Whether the field was handled. If false, + * yggdrasil.{@link Yggdrasil#excessiveField(Object, FieldContext) excessiveField}(this, field) + * will be called. */ - @SuppressWarnings("null") - public boolean excessiveField(@NonNull FieldContext field) throws StreamCorruptedException; + boolean excessiveField(@NonNull FieldContext field) throws StreamCorruptedException; /** * Called if a field was not found in the stream. @@ -69,17 +67,14 @@ public static interface YggdrasilRobustSerializable extends YggdrasilSerializabl * @return Whether the field was handled (e.g. true if the default value is fine). If false, * yggdrasil.{@link Yggdrasil#missingField(Object, Field) missingField}(this, field) will be called. */ - @SuppressWarnings("null") - public boolean missingField(@NonNull Field field) throws StreamCorruptedException; + boolean missingField(@NonNull Field field) throws StreamCorruptedException; } /** * Provides a method to resolve missing enum constants. - * - * @author Peter Güttinger */ - public static interface YggdrasilRobustEnum { + interface YggdrasilRobustEnum { /** * Called when an enum constant is read from stream that does not exist in this enum. @@ -90,19 +85,19 @@ public static interface YggdrasilRobustEnum { * @param name The name read from stream * @return The renamed enum constant or null if the read string is invalid. If the returned Enum is not an instance of this enum type an exception will be thrown. */ - public Enum excessiveConstant(String name); + Enum excessiveConstant(String name); } /** - * A class that has transient fields or more generally wants to exactly define which fields to write to/read from stream should implement this interface. It provides two - * methods similar to Java's writeObject and readObject methods. + * A class that has transient fields or more generally wants to exactly + * define which fields to write to/read from stream should implement this interface. + * It provides two methods similar to Java's writeObject and readObject methods. *

- * If a class implements this interface implementing {@link YggdrasilRobustSerializable} as well is pointless since its methods won't get called. - * - * @author Peter Güttinger + * If a class implements this interface implementing {@link YggdrasilRobustSerializable} + * as well is pointless since its methods won't get called. */ - public static interface YggdrasilExtendedSerializable extends YggdrasilSerializable { + interface YggdrasilExtendedSerializable extends YggdrasilSerializable { /** * Serialises this object. Only fields contained in the returned Fields object will be written to stream. @@ -110,23 +105,24 @@ public static interface YggdrasilExtendedSerializable extends YggdrasilSerializa * You can use return new {@link Fields#Fields(Object) Fields}(this); to emulate the default behaviour. * * @return A Fields object containing all fields that should be written to stream - * @throws NotSerializableException If this object or one of its fields is not serialisable + * @throws NotSerializableException If this object or one of its fields is not serializable */ - public Fields serialize() throws NotSerializableException; + Fields serialize() throws NotSerializableException; /** - * Deserialises this object. No fields have been set when this method is called, use fields.{@link Fields#setFields setFields}(this, yggdrasil) to set all - * compatible non-transient and non-static fields (and call incompatible/missing field handlers if applicable – this implies that errors will be thrown if the fields + * Deserializes this object. No fields have been set when this method is called, use + * fields.{@link Fields#setFields setFields}(this, yggdrasil) to set all + * compatible non-transient and non-static fields (and call incompatible/missing field + * handlers if applicable – this implies that errors will be thrown if the fields * object is invalid). *

* You can use fields.{@link Fields#setFields(Object) setFields}(this); to emulate the default behaviour. * * @param fields A Fields object containing all fields read from stream - * @throws StreamCorruptedException If the Fields object is invalid, i.e. was not written by {@link #serialize()} or Yggrdasil's default serialisation. - * @throws NotSerializableException + * @throws StreamCorruptedException If the Fields object is invalid, i.e. was not written + * by {@link #serialize()} or Yggdrasil's default serialisation. */ - @SuppressWarnings("null") - public void deserialize(@NonNull Fields fields) throws StreamCorruptedException, NotSerializableException; + void deserialize(@NonNull Fields fields) throws StreamCorruptedException, NotSerializableException; } diff --git a/src/main/java/ch/njol/yggdrasil/YggdrasilSerializer.java b/src/main/java/ch/njol/yggdrasil/YggdrasilSerializer.java index 98b5ebda871..0424bb72695 100644 --- a/src/main/java/ch/njol/yggdrasil/YggdrasilSerializer.java +++ b/src/main/java/ch/njol/yggdrasil/YggdrasilSerializer.java @@ -18,15 +18,14 @@ */ package ch.njol.yggdrasil; +import org.eclipse.jdt.annotation.Nullable; + import java.io.NotSerializableException; import java.io.StreamCorruptedException; -import org.eclipse.jdt.annotation.Nullable; - /** - * Utility to be able to save and load classes with Yggdrasil that the user has no control of, e.g. classes of an external API. - * - * @author Peter Güttinger + * Utility to be able to save and load classes with Yggdrasil that + * the user has no control of, e.g. classes of an external API. */ public abstract class YggdrasilSerializer implements ClassResolver { @@ -39,23 +38,25 @@ public abstract class YggdrasilSerializer implements ClassResolver { *

* Use return new {@link Fields#Fields(Object) Fields}(this); to emulate the default behaviour. * - * @param o The object to serialise + * @param object The object to serialise * @return A Fields object representing the object's fields to serialise. Must not be null. - * @throws NotSerializableException If this object could not be serialised + * @throws NotSerializableException If this object could not be serialized */ - public abstract Fields serialize(T o) throws NotSerializableException; + public abstract Fields serialize(T object) throws NotSerializableException; /** - * Whether an instance of the given class can be dynamically created. If this method returns false, {@link #newInstance(Class)} and {@link #deserialize(Object, Fields)} will - * not be called for the given class, but {@link #deserialize(Class, Fields)} will be used instead, and having any reference to an object of the given class in its own fields' - * graph will cause Yggdrasil to throw an exception upon serialisation as no reference to the object will be available when deserialising the object. // TODO allow this + * Whether an instance of the given class can be dynamically created. If this method returns false, + * {@link #newInstance(Class)} and {@link #deserialize(Object, Fields)} will not be called for the given class, + * but {@link #deserialize(Class, Fields)} will be used instead, and having any reference to an object of the given + * class in its own fields' graph will cause Yggdrasil to throw an exception upon serialisation as no reference to + * the object will be available when deserializing the object. // TODO allow this *

- * Please note that you must not change the return value of this function ever - it is not saved in the stream. // TODO clarify + * Please note that you must not change the return value of this function ever - it is not saved in the stream. * - * @param c The class to check + * @param type The class to check * @return true by default */ - public boolean canBeInstantiated(final Class c) { + public boolean canBeInstantiated(Class type) { return true; } @@ -69,28 +70,26 @@ public boolean canBeInstantiated(final Class c) { public abstract E newInstance(Class c); /** - * Deserialises an object. + * Deserializes an object. *

* Use fields.{@link Fields#setFields(Object) setFields}(o); to emulate the default behaviour. * - * @param o The object to deserialise as returned by {@link #newInstance(Class)}. + * @param object The object to deserialize as returned by {@link #newInstance(Class)}. * @param fields The fields read from stream - * @throws StreamCorruptedException If deserialisation failed because the data read from stream is incomplete or invalid. - * @throws NotSerializableException + * @throws StreamCorruptedException If deserialization failed because the data read from stream is incomplete or invalid. */ - public abstract void deserialize(T o, Fields fields) throws StreamCorruptedException, NotSerializableException; + public abstract void deserialize(T object, Fields fields) throws StreamCorruptedException, NotSerializableException; /** - * Deserialises an object. + * Deserializes an object. * - * @param c The class to get an instance of + * @param type The class to get an instance of * @param fields The fields read from stream * @return An object representing the read fields. Must not be null (throw an exception instead). - * @throws StreamCorruptedException If deserialisation failed because the data read from stream is incomplete or invalid. - * @throws NotSerializableException If the class is not serialisable + * @throws StreamCorruptedException If deserialization failed because the data read from stream is incomplete or invalid. + * @throws NotSerializableException If the class is not serializable */ - @SuppressWarnings("unused") - public E deserialize(final Class c, final Fields fields) throws StreamCorruptedException, NotSerializableException { + public E deserialize(Class type, Fields fields) throws StreamCorruptedException, NotSerializableException { throw new YggdrasilException(getClass() + " does not override deserialize(Class, Fields)"); } diff --git a/src/main/java/ch/njol/yggdrasil/util/JREFieldHandler.java b/src/main/java/ch/njol/yggdrasil/util/JREFieldHandler.java index a5534cc855f..6bff027da85 100644 --- a/src/main/java/ch/njol/yggdrasil/util/JREFieldHandler.java +++ b/src/main/java/ch/njol/yggdrasil/util/JREFieldHandler.java @@ -18,117 +18,98 @@ */ package ch.njol.yggdrasil.util; +import ch.njol.yggdrasil.FieldHandler; +import ch.njol.yggdrasil.Fields.FieldContext; +import ch.njol.yggdrasil.YggdrasilException; + import java.io.StreamCorruptedException; import java.lang.reflect.Array; import java.lang.reflect.Field; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Map; -import ch.njol.yggdrasil.FieldHandler; -import ch.njol.yggdrasil.Fields.FieldContext; -import ch.njol.yggdrasil.YggdrasilException; - /** - * Handles common JRE-related incompatible field types. This handler is not added by default and is merely a utility. - * - * @author Peter Güttinger + * Handles common JRE-related incompatible field types. + * This handler is not added by default and is merely a utility. */ +@Deprecated public class JREFieldHandler implements FieldHandler { - /** - * Not used - */ @Override - public boolean excessiveField(final Object o, final FieldContext field) { + public boolean excessiveField(Object object, FieldContext field) { return false; } - /** - * Not used - */ @Override - public boolean missingField(final Object o, final Field field) throws StreamCorruptedException { + public boolean missingField(Object object, Field field) { return false; } /** * Converts collection types and non-primitive arrays - * - * @throws StreamCorruptedException */ @SuppressWarnings({"rawtypes", "unchecked"}) @Override - public boolean incompatibleField(final Object o, final Field f, final FieldContext field) throws StreamCorruptedException { - Object value = field.getObject(); + public boolean incompatibleField(Object object, Field field, FieldContext context) throws StreamCorruptedException { + Object value = context.getObject(); if (value instanceof Object[]) - value = Arrays.asList(value); + value = Collections.singletonList(value); if (value instanceof Collection) { - final Collection v = (Collection) value; + Collection collection = (Collection) value; try { - if (Collection.class.isAssignableFrom(f.getType())) { - final Collection c = (Collection) f.get(o); + if (Collection.class.isAssignableFrom(field.getType())) { + Collection c = (Collection) field.get(object); if (c != null) { c.clear(); - c.addAll(v); + c.addAll(collection); return true; } - } else if (Object[].class.isAssignableFrom(f.getType())) { - Object[] array = (Object[]) f.get(o); + } else if (Object[].class.isAssignableFrom(field.getType())) { + Object[] array = (Object[]) field.get(object); if (array != null) { - if (array.length < v.size()) + if (array.length < collection.size()) return false; - final Class ct = array.getClass().getComponentType(); - for (final Object x : v) { - if (!ct.isInstance(x)) + Class ct = array.getClass().getComponentType(); + for (Object iterated : collection) { + if (!ct.isInstance(iterated)) return false; } } else { - array = (Object[]) Array.newInstance(f.getType().getComponentType(), v.size()); - f.set(o, array); + array = (Object[]) Array.newInstance(field.getType().getComponentType(), collection.size()); + field.set(object, array); } - final int l = array.length; + int length = array.length; int i = 0; - for (final Object x : v) - array[i++] = x; - while (i < l) + for (Object iterated : collection) + array[i++] = iterated; + while (i < length) array[i++] = null; } - } catch (final IllegalArgumentException e) { - throw new YggdrasilException(e); - } catch (final IllegalAccessException e) { - throw new YggdrasilException(e); - } catch (final UnsupportedOperationException e) { - throw new YggdrasilException(e); - } catch (final ClassCastException e) { - throw new YggdrasilException(e); - } catch (final NullPointerException e) { - throw new YggdrasilException(e); - } catch (final IllegalStateException e) { + } catch ( + IllegalArgumentException | NullPointerException | IllegalStateException | + ClassCastException | UnsupportedOperationException | IllegalAccessException e + ) { throw new YggdrasilException(e); } } else if (value instanceof Map) { - if (!Map.class.isAssignableFrom(f.getType())) + if (!Map.class.isAssignableFrom(field.getType())) return false; try { - final Map m = (Map) f.get(o); + Map m = (Map) field.get(object); if (m != null) { m.clear(); m.putAll((Map) value); return true; } - } catch (final IllegalArgumentException e) { - throw new YggdrasilException(e); - } catch (final IllegalAccessException e) { - throw new YggdrasilException(e); - } catch (final UnsupportedOperationException e) { - throw new YggdrasilException(e); - } catch (final ClassCastException e) { - throw new YggdrasilException(e); - } catch (final NullPointerException e) { + } catch ( + IllegalArgumentException | IllegalAccessException | UnsupportedOperationException | + ClassCastException | NullPointerException e + ) { throw new YggdrasilException(e); } } + return false; } diff --git a/src/main/java/ch/njol/yggdrasil/xml/YggXMLInputStream.java b/src/main/java/ch/njol/yggdrasil/xml/YggXMLInputStream.java deleted file mode 100644 index b787be0e461..00000000000 --- a/src/main/java/ch/njol/yggdrasil/xml/YggXMLInputStream.java +++ /dev/null @@ -1,322 +0,0 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ -package ch.njol.yggdrasil.xml; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.StreamCorruptedException; -import java.lang.reflect.Array; -import java.util.NoSuchElementException; - -import javax.xml.stream.FactoryConfigurationError; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; - -import org.eclipse.jdt.annotation.Nullable; - -import ch.njol.yggdrasil.Tag; -import ch.njol.yggdrasil.Yggdrasil; -import ch.njol.yggdrasil.YggdrasilInputStream; - -/** - * @deprecated XML has so many quirks that storing arbitrary data cannot be guaranteed. - * @author Peter Güttinger - */ -@Deprecated -public final class YggXMLInputStream extends YggdrasilInputStream { - - private final XMLStreamReader in; - private final InputStream is; - - @SuppressWarnings("unused") - private final short version; - - @SuppressWarnings("null") - public YggXMLInputStream(final Yggdrasil y, final InputStream in) throws IOException { - super(y); - is = in; - try { - this.in = XMLInputFactory.newFactory().createXMLStreamReader(in); - while (this.in.next() != XMLStreamConstants.START_ELEMENT) {} - if (!this.in.getLocalName().equals("yggdrasil")) - throw new StreamCorruptedException("Not an Yggdrasil stream"); - final String v = getAttribute("version"); - short ver = 0; - try { - ver = Short.parseShort(v); - } catch (final NumberFormatException e) {} - if (ver <= 0 || ver > Yggdrasil.LATEST_VERSION) - throw new StreamCorruptedException("Input was saved using a later version of Yggdrasil"); - version = ver; - } catch (final XMLStreamException e) { - throw new IOException(e); - } catch (final FactoryConfigurationError e) { - throw new IOException(e); - } - } - - // private - - private Class getType(String s) throws StreamCorruptedException { - int dim = 0; - while (s.endsWith("[]")) { - s = "" + s.substring(0, s.length() - 2); - dim++; - } - Class c; - final Tag t = Tag.byName(s); - if (t != null) - c = t.c; - else - c = yggdrasil.getClass(s); - if (c == null) - throw new StreamCorruptedException("Invalid type " + s); - if (dim == 0) - return c; - while (dim-- > 0) - c = Array.newInstance(c, 0).getClass(); - return c; - } - - private String getAttribute(final String name) throws StreamCorruptedException { - final String s = in.getAttributeValue(null, name); - if (s == null) - throw new StreamCorruptedException("Missing attribute " + name + " for <" + in.getLocalName() + ">"); - return s; - } - - // Tag - - @Nullable - private Tag nextTag = null; - - @Override - protected Tag readTag() throws IOException { - if (nextTag != null) { - final Tag t = nextTag; - nextTag = null; - return t; - } - try { - while (in.next() != XMLStreamConstants.START_ELEMENT) {} - } catch (final XMLStreamException e) { - throw new StreamCorruptedException(e.getMessage()); - } catch (final NoSuchElementException e) { - throw new EOFException(); - } - @SuppressWarnings("null") - final Tag t = Tag.byName(in.getLocalName()); - if (t == null) - throw new StreamCorruptedException("Invalid tag " + in.getLocalName()); - return t; - } - - // Primitives - - @Override - protected Object readPrimitive(final Tag type) throws IOException { - try { - final String v = in.getElementText(); - switch (type) { - case T_BYTE: - return Byte.parseByte(v); - case T_SHORT: - return Short.parseShort(v); - case T_INT: - return Integer.parseInt(v); - case T_LONG: - return Long.parseLong(v); - case T_FLOAT: - return Float.parseFloat(v); - case T_DOUBLE: - return Double.parseDouble(v); - case T_BOOLEAN: - return Boolean.parseBoolean(v); - case T_CHAR: - if (v.length() > 1) - throw new StreamCorruptedException(); - return v.charAt(0); - //$CASES-OMITTED$ - default: - throw new StreamCorruptedException(); - } - } catch (final XMLStreamException e) { - throw new StreamCorruptedException(); - } catch (final NumberFormatException e) { - throw new StreamCorruptedException(); - } - } - - @Nullable - String primitiveData = null; - int primitiveDataIndex = 0; - - @Override - protected Object readPrimitive_(final Tag type) throws IOException { - try { - if (in.getEventType() == XMLStreamConstants.START_ELEMENT) { - primitiveData = in.getElementText(); // advances stream to END_ELEMENT - primitiveDataIndex = 0; - } - final String primitiveData = this.primitiveData; - if (primitiveData == null) - throw new StreamCorruptedException(); - assert in.getEventType() == XMLStreamConstants.END_ELEMENT; - switch (type) { - case T_BYTE: - return (byte) Short.parseShort(primitiveData.substring(primitiveDataIndex, primitiveDataIndex += 2), 16); - case T_SHORT: - return (short) Integer.parseInt(primitiveData.substring(primitiveDataIndex, primitiveDataIndex += 4), 16); - case T_INT: - return (int) Long.parseLong(primitiveData.substring(primitiveDataIndex, primitiveDataIndex += 8), 16); - case T_LONG: - return Long.parseLong(primitiveData.substring(primitiveDataIndex, primitiveDataIndex += 8), 16) << 32 | Long.parseLong(primitiveData.substring(primitiveDataIndex, primitiveDataIndex += 8), 16); - case T_FLOAT: - return Float.intBitsToFloat((int) Long.parseLong(primitiveData.substring(primitiveDataIndex, primitiveDataIndex += 8), 16)); - case T_DOUBLE: - return Double.longBitsToDouble(Long.parseLong(primitiveData.substring(primitiveDataIndex, primitiveDataIndex += 8), 16) << 32 | Long.parseLong(primitiveData.substring(primitiveDataIndex, primitiveDataIndex += 8), 16)); - case T_BOOLEAN: - final char c = primitiveData.charAt(primitiveDataIndex++); - if (c == '1') - return true; - else if (c == '0') - return false; - throw new StreamCorruptedException(); - case T_CHAR: - return primitiveData.charAt(primitiveDataIndex++); - //$CASES-OMITTED$ - default: - throw new StreamCorruptedException(); - } - } catch (final XMLStreamException e) { - throw new StreamCorruptedException(); - } catch (final StringIndexOutOfBoundsException e) { - throw new StreamCorruptedException(); - } catch (final NumberFormatException e) { - throw new StreamCorruptedException(); - } - } - - // String - - @SuppressWarnings("null") - @Override - protected String readString() throws IOException { - try { - return in.getElementText(); - } catch (final XMLStreamException e) { - throw new StreamCorruptedException(); - } - } - - // Array - - @Override - protected Class readArrayComponentType() throws IOException { - return getType(getAttribute("componentType")); - } - - @Override - protected int readArrayLength() throws IOException { - try { - return Integer.parseInt(getAttribute("length")); - } catch (final NumberFormatException e) { - throw new StreamCorruptedException(); - } - } - - // Enum - - @Override - protected Class readEnumType() throws IOException { - return getType(getAttribute("type")); - } - - @Override - protected String readEnumID() throws IOException { - try { - return "" + in.getElementText(); - } catch (final XMLStreamException e) { - throw new StreamCorruptedException(); - } - } - - // Class - - @Override - protected Class readClass() throws IOException { - try { - return getType("" + in.getElementText()); - } catch (final XMLStreamException e) { - throw new StreamCorruptedException(); - } - } - - // Reference - - @Override - protected int readReference() throws IOException { - try { - return Integer.parseInt(in.getElementText()); - } catch (final NumberFormatException e) { - throw new StreamCorruptedException(); - } catch (final XMLStreamException e) { - throw new StreamCorruptedException(); - } - } - - // generic Object - - @Override - protected Class readObjectType() throws IOException { - return getType(getAttribute("type")); - } - - @Override - protected short readNumFields() throws IOException { - try { - return Short.parseShort(getAttribute("numFields")); - } catch (final NumberFormatException e) { - throw new StreamCorruptedException(); - } - } - - @Override - protected String readFieldID() throws IOException { - nextTag = readTag(); - return getAttribute("id"); - } - - // stream - - @Override - public void close() throws IOException { - try { - // TODO error if not at EOF? - in.close(); - is.close(); - } catch (final XMLStreamException e) { - throw new IOException(e); - } - } - -} diff --git a/src/main/java/ch/njol/yggdrasil/xml/YggXMLOutputStream.java b/src/main/java/ch/njol/yggdrasil/xml/YggXMLOutputStream.java deleted file mode 100644 index 5d470cc7197..00000000000 --- a/src/main/java/ch/njol/yggdrasil/xml/YggXMLOutputStream.java +++ /dev/null @@ -1,339 +0,0 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ -package ch.njol.yggdrasil.xml; - -import static ch.njol.yggdrasil.Tag.T_NULL; -import static ch.njol.yggdrasil.Tag.getPrimitiveFromWrapper; -import static ch.njol.yggdrasil.Tag.getType; - -import java.io.IOException; -import java.io.NotSerializableException; -import java.io.OutputStream; -import java.util.Locale; -import java.util.regex.Pattern; - -import javax.xml.stream.FactoryConfigurationError; -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; - -import org.eclipse.jdt.annotation.Nullable; - -import ch.njol.util.StringUtils; -import ch.njol.yggdrasil.Tag; -import ch.njol.yggdrasil.Yggdrasil; -import ch.njol.yggdrasil.YggdrasilException; -import ch.njol.yggdrasil.YggdrasilOutputStream; - -/** - * @deprecated XML has so many quirks that storing arbitrary data cannot be guaranteed. - * @author Peter Güttinger - */ -@Deprecated -public final class YggXMLOutputStream extends YggdrasilOutputStream { - - private final OutputStream os; - private final XMLStreamWriter out; - - private final short version; - - @SuppressWarnings("null") - public YggXMLOutputStream(final Yggdrasil y, final OutputStream out) throws IOException, FactoryConfigurationError { - super(y); - version = y.version; - try { - os = out; - this.out = XMLOutputFactory.newFactory().createXMLStreamWriter(out, "UTF-8"); - this.out.writeStartDocument("utf-8", "1.0"); - this.out.writeStartElement("yggdrasil"); - writeAttribute("version", "" + version); - } catch (final XMLStreamException e) { - throw new IOException(e); - } - } - - // private - - @SuppressWarnings("null") - private String getTypeName(Class c) throws NotSerializableException { - String a = ""; - while (c.isArray()) { - a += "[]"; - c = c.getComponentType(); - } - final String s; - final Tag type = getType(c); - switch (type) { - case T_OBJECT: - case T_ENUM: - s = yggdrasil.getID(c); - break; - case T_BOOLEAN: - case T_BOOLEAN_OBJ: - case T_BYTE: - case T_BYTE_OBJ: - case T_CHAR: - case T_CHAR_OBJ: - case T_DOUBLE: - case T_DOUBLE_OBJ: - case T_FLOAT: - case T_FLOAT_OBJ: - case T_INT: - case T_INT_OBJ: - case T_LONG: - case T_LONG_OBJ: - case T_SHORT: - case T_SHORT_OBJ: - case T_CLASS: - case T_STRING: - default: - s = type.name; - break; - case T_NULL: - case T_REFERENCE: - case T_ARRAY: - throw new YggdrasilException("" + c.getCanonicalName()); - } - return s + a; - } - - @SuppressWarnings("null") - private final static Pattern valid = Pattern.compile("[\\u0009 \\u000A \\u000D \\u0020-\\u007E \\u0085 \\u00A0-\\uD7FF \\uE000-\\uFFFD \\x{10000}–\\x{10FFFF}]*", Pattern.COMMENTS); - - private static void validateString(final String s) throws IOException { - if (!valid.matcher(s).matches()) - throw new IOException("The string '" + s + "' contains characters illegal in XML 1.0: '" + toUnicodeEscapes("" + valid.matcher(s).replaceAll("")) + "'"); - } - - private static String toUnicodeEscapes(final String s) { - final StringBuilder b = new StringBuilder(); - for (int i = 0; i < s.length(); i++) { - b.append(String.format("\\u%04x", (int) s.charAt(i))); - } - return "" + b; - } - - private void writeEndElement() throws IOException { - try { - out.writeEndElement(); - } catch (final XMLStreamException e) { - throw new IOException(e); - } - } - - private void writeAttribute(final String s, final String value) throws IOException { - validateString(s); - validateString(value); - try { - out.writeAttribute(s, value); - } catch (final XMLStreamException e) { - throw new IOException(e); - } - } - - private void writeCharacters(final String s) throws IOException { - validateString(s); - try { - out.writeCharacters(s); - } catch (final XMLStreamException e) { - throw new IOException(e); - } - } - - // Tag - - @Override - protected void writeTag(final Tag t) throws IOException { - try { - if (t == T_NULL) - out.writeEmptyElement(t.name); - else - out.writeStartElement(t.name); - writeID(); - } catch (final XMLStreamException e) { - throw new IOException(e); - } - } - - // Primitives - - @Override - protected void writePrimitiveValue(final Object o) throws IOException { - writeCharacters("" + o); - writeEndElement(); - } - - @Override - protected void writePrimitive_(final Object o) throws IOException { - final Tag type = getPrimitiveFromWrapper(o.getClass()); - final int size; - final long value; - switch (type) { - case T_BYTE: - size = 1; - value = 0xFFL & ((Byte) o); - break; - case T_SHORT: - size = 2; - value = 0xFFFFL & ((Short) o); - break; - case T_INT: - size = 4; - value = 0xFFFFFFFFL & ((Integer) o); - break; - case T_LONG: - size = 8; - value = (Long) o; - break; - case T_FLOAT: - size = 4; - value = 0xFFFFFFFFL & Float.floatToIntBits((Float) o); - break; - case T_DOUBLE: - size = 8; - value = Double.doubleToLongBits((Double) o); - break; - case T_CHAR: - size = 2; - value = 0xFFFFL & ((Character) o); - break; - case T_BOOLEAN: - size = 0; // results in 1 character - 0 or 1 - value = ((Boolean) o) ? 1 : 0; - break; - //$CASES-OMITTED$ - default: - throw new YggdrasilException("Invalid call to writePrimitive with argument " + o); - } - final String s = Long.toHexString(value).toUpperCase(Locale.ENGLISH); - writeCharacters(StringUtils.multiply('0', Math.max(0, 2 * size - s.length())) + s); - } - - // String - - @Override - protected void writeStringValue(final String s) throws IOException { - writeCharacters(s); - writeEndElement(); - } - - // Array - - @Override - protected void writeArrayComponentType(final Class contentType) throws IOException { - writeAttribute("componentType", getTypeName(contentType)); - } - - @Override - protected void writeArrayLength(final int length) throws IOException { - writeAttribute("length", "" + length); - } - - @Override - protected void writeArrayEnd() throws IOException { - writeEndElement(); - } - - // Enum - - @Override - protected void writeEnumType(final String type) throws IOException { - writeAttribute("type", type); - } - - @Override - protected void writeEnumID(final String id) throws IOException { - writeCharacters(id); - writeEndElement(); - } - - // Class - - @Override - protected void writeClassType(final Class c) throws IOException { - writeCharacters(getTypeName(c)); - writeEndElement(); - } - - // Reference - - @Override - protected void writeReferenceID(final int ref) throws IOException { - writeCharacters("" + ref); - writeEndElement(); - } - - // generic Object - - @Override - protected void writeObjectType(final String type) throws IOException { - writeAttribute("type", type); - } - - @Override - protected void writeNumFields(final short numFields) throws IOException { - writeAttribute("numFields", "" + numFields); - } - - // name of the next field - @Nullable - private String id = null; - - private final void writeID() throws IOException { - if (id != null) { - writeAttribute("id", id); - id = null; - } - } - - @Override - protected void writeFieldID(final String id) throws IOException { - this.id = id; - } - - @Override - protected void writeObjectEnd() throws IOException { - writeEndElement(); - } - - // stream - - @Override - public void flush() throws IOException { - try { - out.flush(); - os.flush(); - } catch (final XMLStreamException e) { - throw new IOException(e); - } - } - - @Override - public void close() throws IOException { - try { - out.writeEndElement(); // - out.writeEndDocument(); - out.close(); - os.close(); - } catch (final XMLStreamException e) { - throw new IOException(e); - } - } - -} diff --git a/src/main/java/ch/njol/yggdrasil/xml/package-info.java b/src/main/java/ch/njol/yggdrasil/xml/package-info.java deleted file mode 100644 index f80c88a2e9f..00000000000 --- a/src/main/java/ch/njol/yggdrasil/xml/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ -/** - * @author Peter Güttinger - */ -@NonNullByDefault({DefaultLocation.PARAMETER, DefaultLocation.RETURN_TYPE, DefaultLocation.FIELD}) -package ch.njol.yggdrasil.xml; - -import org.eclipse.jdt.annotation.DefaultLocation; -import org.eclipse.jdt.annotation.NonNullByDefault; - From f63882f334914f611b05c47239c7d8f7c8d0523e Mon Sep 17 00:00:00 2001 From: kiip1 <25848425+kiip1@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:37:24 +0100 Subject: [PATCH 2/3] Suggestions --- .../java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java | 5 +++-- src/main/java/ch/njol/yggdrasil/Fields.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java b/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java index c2043c3e310..b6fbd025fd9 100644 --- a/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java +++ b/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java @@ -18,11 +18,12 @@ */ package ch.njol.yggdrasil; +import ch.njol.util.coll.CollectionUtils; + import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.StreamCorruptedException; -import java.lang.reflect.Array; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -257,7 +258,7 @@ protected Class readClass() throws IOException { throw new YggdrasilException("Internal error; " + type); } while (dimensions-- > 0) - clazz = Array.newInstance(clazz, 0).getClass(); + clazz = CollectionUtils.arrayType(clazz); return clazz; } diff --git a/src/main/java/ch/njol/yggdrasil/Fields.java b/src/main/java/ch/njol/yggdrasil/Fields.java index 865f3cf2bf3..36198ead5e0 100644 --- a/src/main/java/ch/njol/yggdrasil/Fields.java +++ b/src/main/java/ch/njol/yggdrasil/Fields.java @@ -253,7 +253,7 @@ public static Collection getFields(Class type) throws NotSerializableE Field[] declaredFields = superClass.getDeclaredFields(); for (Field field : declaredFields) { int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) + if (field.isSynthetic() || Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; String id = Yggdrasil.getID(field); if (ids.contains(id)) From 0bcf900f46bc80c49c27a0462a129693f431434a Mon Sep 17 00:00:00 2001 From: Kiip <25848425+kiip1@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:48:04 +0100 Subject: [PATCH 3/3] Suggestion --- .../ch/njol/yggdrasil/DefaultYggdrasilInputStream.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java b/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java index b6fbd025fd9..87142f293a1 100644 --- a/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java +++ b/src/main/java/ch/njol/yggdrasil/DefaultYggdrasilInputStream.java @@ -132,10 +132,10 @@ private int readUnsignedInt() throws IOException { } private long readLong() throws IOException { - return (long) read() << 56 | (long) read() << 48 - | (long) read() << 40 | (long) read() << 32 - | (long) read() << 24 | (long) read() << 16 - | (long) read() << 8 | read(); + return (long) in.read() << 56 | (long) in.read() << 48 + | (long) in.read() << 40 | (long) in.read() << 32 + | (long) in.read() << 24 | (long) in.read() << 16 + | (long) in.read() << 8 | read(); } private float readFloat() throws IOException {