/**
* ****************************************************************************\
* |* Copyright © 2012 LB-Stuff *| |* All rights reserved. *| |* *| |*
* Redistribution and use in source and binary forms, with or without *| |*
* modification, are permitted provided that the following conditions *| |* are
* met: *| |* *| |* 1. Redistributions of source code must retain the above
* copyright *| |* notice, this list of conditions and the following disclaimer.
* *| |* *| |* 2. Redistributions in binary form must reproduce the above
* copyright *| |* notice, this list of conditions and the following disclaimer
* in the *| |* documentation and/or other materials provided with the
* distribution. *| |* *| |* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND
* CONTRIBUTORS "AS IS" AND *| |* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE *| |* IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE *| |* ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR OR CONTRIBUTORS BE LIABLE *| |* FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *| |* DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS *| |* OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *| |* HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT *| |* LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *| |* OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *| |* SUCH DAMAGE. *|
* \*****************************************************************************
*/
package jo.sm.plugins.ship.imp.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* The main class used by this NBT package, its static subclasses extend and
* implement it. The equals() and hashCode() methods are based entirely on the
* name of the tag. I am aware that this means you can do nonsensical things
* such as Tag.Byte.Compound.Short.List...
*
* @author LB
* @version 19133
*/
public abstract class Tag implements Cloneable {
/**
* The name of this tag.
*/
private java.lang.String name;
/**
* The sole constructor for the abstract tag class.
*
* @param _name The name of the new tag. If this tag must always have a
* name, this parameter must not be null. If this tag must never have a
* name, this parameter must be null.
*/
public Tag(java.lang.String _name) {
name = _name;
}
/**
* Returns the name of this tag, or null if this tag doesn't have a name.
*
* @return The name of this tag, or null if this tag doesn't have a name.
*/
public java.lang.String Name() {
return name;
}
/**
* The enumeration class that represents the different tag types in the NBT
* format.
*/
public static enum Type {
END,
BYTE, SHORT, INT, LONG,
FLOAT, DOUBLE,
BYTEARRAY,
STRING,
LIST,
COMPOUND,
INTARRAY;
/**
* Converts an integer ordinal to a tag type.
*
* @param ordinal The integer ID of the tag.
* @return The Tag.Type corresponding to the ID.
* @throws IndexOutOfBoundsException if the ordinal is not with a valid
* range.
*/
public static Type FromID(int ordinal) throws IndexOutOfBoundsException {
switch (ordinal) {
case 0:
return END;
case 1:
return BYTE;
case 2:
return SHORT;
case 3:
return INT;
case 4:
return LONG;
case 5:
return FLOAT;
case 6:
return DOUBLE;
case 7:
return BYTEARRAY;
case 8:
return STRING;
case 9:
return LIST;
case 10:
return COMPOUND;
case 11:
return INTARRAY;
}
throw new IndexOutOfBoundsException("Tag ID out of bounds: " + ordinal);
}
/**
* Converts this type to its respective class.
*
* @return The class object for this type's relevant class.
*/
public Class<? extends Tag> ToClass() {
switch (this) {
case END:
return End.class;
case BYTE:
return Byte.class;
case SHORT:
return Short.class;
case INT:
return Int.class;
case LONG:
return Long.class;
case FLOAT:
return Float.class;
case DOUBLE:
return Double.class;
case BYTEARRAY:
return ByteArray.class;
case STRING:
return String.class;
case LIST:
return List.class;
case COMPOUND:
return Compound.class;
case INTARRAY:
return IntArray.class;
}
throw new IllegalArgumentException();
}
/**
* A toString method that gives a nicer-looking name than one in ALL
* CAPS.
*
* @return The simple name of this type's relevant class (e.g.
* Compound).
*/
@Override
public java.lang.String toString() {
switch (this) {
case BYTEARRAY:
return "Byte Array";
case INTARRAY:
return "Int Array";
default:
return ToClass().getSimpleName();
}
}
/**
* The exception thrown by <code>Tag.List</code> when a tag is added
* that doesn't match the tag's tag type.
*/
@SuppressWarnings("serial")
public static class MismatchException extends Exception {
public MismatchException(java.lang.String msg) {
super(msg);
}
}
}
/**
* The polymorphic method to get which tag type this tag corresponds to.
*
* @return The tag type this tag corresponds to.
*/
public abstract Type Type();
/**
* The main serialization function. Serializes raw, uncompressed NBT data by
* polymorphically calling a payload serialization function.
*
* @param o The <code>OutputStream</code> to serialize to.
* @throws IOException if the output operation generates an exception.
*/
public final void Serialize(OutputStream o) throws IOException {
PreSerialize(o);
SerializePayload(o);
}
/**
* The polymorphic method used to serialize only the tag's payload.
*
* @param o The <code>OutputStream</code> to serialize to.
* @throws IOException if the output operation generates an exception.
*/
protected abstract void SerializePayload(OutputStream o) throws IOException;
/**
* Represents the UTF-8 <code>Charset</code>.
*/
protected static final java.nio.charset.Charset UTF8;
static {
java.nio.charset.Charset temp = null;
try {
temp = java.nio.charset.Charset.forName("UTF-8");
} catch (Throwable t) {
} finally {
UTF8 = temp;
}
}
/**
* The method used to serialize the type and name of a tag.
*
* @param o The <code>OutputStream</code> to serialize to.
* @throws IOException if the output operation generates an exception.
*/
private void PreSerialize(OutputStream o) throws IOException {
o.write((byte) Type().ordinal());
if (name != null) {
new String(null, name).SerializePayload(o);
}
}
/**
* Used to create a visual, text-based representation of this tag.
*
* @return A visual, text-based representation of this tag.
*/
@Override
public abstract java.lang.String toString();
/**
* A utility method for either quoting the tag's name or returning nothing
* if the name is <code>null</code>.
*
* @return A space followed by the quoted name of the tag, or nothing if the
* name is null.
*/
protected final java.lang.String QuoteName() {
if (name != null) {
return " \"" + name + "\"";
}
return "";
}
/**
* Returns the hash code of the name of this tag.
*
* @return The hash code of the name of this tag.
*/
@Override
public final int hashCode() {
if (name == null) {
return 0;
}
return name.hashCode();
}
/**
* Returns whether the given object is a Tag and has the same name.
*
* @param o The object to compare to.
* @return Whether the given object is a Tag and has the same name.
*/
@Override
public final boolean equals(Object o) {
if (o instanceof Tag) {
Tag t = (Tag) o;
if (name == null) {
return t.name == null;
} else if (t.name != null) {
return name.equals(t.name);
}
}
return false;
}
/**
* Returns whether the given tag has the same name.
*
* @param t The tag to compare to.
* @return Whether the given tag has the same name.
*/
public final boolean equals(Tag t) {
if (name == null) {
return t.name == null;
} else if (t.name != null) {
return name.equals(t.name);
}
return false;
}
/**
* Returns an independent clone of this tag.
*
* @return An independent clone of this tag.
*/
@Override
public Tag clone() {
try {
return (Tag) super.clone();
} catch (CloneNotSupportedException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Returns an independent clone of this tag with the given name.
*
* @param newname The new name for the clone, or null if the clone should
* not have a name.
* @return An independent clone of this tag with the given name.
*/
public final Tag clone(java.lang.String newname) {
Tag t = clone();
if (this != End.TAG) {
t.name = newname;
}
return t;
}
/**
* TAG_End
*/
public static final class End extends Tag {
/**
* The single instance of this tag.
*/
public static final End TAG = new End();
/**
* Used to construct the single instance of this tag.
*/
private End() throws UnsupportedOperationException {
super(null);
if (TAG != null) {
throw new UnsupportedOperationException();
}
}
/**
* Returns the tag type corresponding to TAG_End.
*
* @return <code>Type.END</code>.
*/
@Override
public Type Type() {
return Type.END;
}
/**
* Does nothing, but prevents this class from being abstract.
*
* @param o The <code>OutputStream</code> to serialize to.
* @throws IOException when pigs fly.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
}
/**
* Returns "End".
*
* @return "End".
*/
@Override
public java.lang.String toString() {
return "End";
}
/**
* Returns <code>TAG</code>.
*
* @return <code>TAG</code>.
*/
@Override
public End clone() {
return TAG;
}
}
/**
* TAG_Byte
*/
public static final class Byte extends Tag {
/**
* The value of this byte.
*/
public byte v;
/**
* The normal constructor.
*
* @param name The name of this byte.
* @param b The initial value of the byte.
*/
public Byte(java.lang.String name, byte b) {
super(name);
v = b;
}
/**
* The DeserializePayload constructor.
*
* @param name The name of this byte.
* @param i The <code>InputStream</code> to deserialize the byte from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException when pigs fly.
*/
public Byte(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
this(name, (byte) i.read());
}
/**
* Returns the tag type corresponding to TAG_Byte.
*
* @return <code>Type.BYTE</code>
*/
@Override
public Type Type() {
return Type.BYTE;
}
/**
* Serializes the byte to the <code>OutputStream</code>.
*
* @param o The <code>OutputStream</code> to serialize the byte to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
o.write(v);
}
/**
* Gives a textual representation of this byte in base-10.
*
* @return A textual representation of this byte in base-10.
*/
@Override
public java.lang.String toString() {
return "Byte" + QuoteName() + ": " + v + "";
}
}
/**
* TAG_Short
*/
public static final class Short extends Tag {
/**
* The value of this short.
*/
public short v;
/**
* The normal constructor.
*
* @param name The name of this short.
* @param s The initial value of the short.
*/
public Short(java.lang.String name, short s) {
super(name);
v = s;
}
/**
* The DeserializePayload constructor.
*
* @param name The name of this short.
* @param i The <code>InputStream</code> to deserialize the short from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException when pigs fly.
*/
public Short(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
this(name, new DataInputStream(i).readShort());
}
/**
* returns the tag type corresponding to TAG_Short.
*
* @return <code>Type.SHORT</code>
*/
@Override
public Type Type() {
return Type.SHORT;
}
/**
* Serializes the short to the <code>OutputStream</code>.
*
* @param o The <code>OutputStream</code> to serialize the short to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
new DataOutputStream(o).writeShort(v);
}
/**
* Gives a textual representation of this short in base-10.
*
* @return A textual representation of this short in base-10.
*/
@Override
public java.lang.String toString() {
return "Short" + QuoteName() + ": " + v + "";
}
}
/**
* TAG_Int
*/
public static final class Int extends Tag {
/**
* The value of this integer.
*/
public int v;
/**
* The normal constructor.
*
* @param name The name of this integer.
* @param i The initial value of the integer.
*/
public Int(java.lang.String name, int i) {
super(name);
v = i;
}
/**
* The DeserializePayload constructor.
*
* @param name The name of this integer.
* @param i The <code>InputStream</code> to deserialize the integer
* from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException when pigs fly.
*/
public Int(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
this(name, new DataInputStream(i).readInt());
}
/**
* Returns the tag type corresponding to TAG_Int.
*
* @return <code>Type.INT</code>.
*/
@Override
public Type Type() {
return Type.INT;
}
/**
* Serializes the integer to the <code>OutputStream</code>.
*
* @param o The <code>OutputStream</code> to serialize the integer to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
new DataOutputStream(o).writeInt(v);
}
/**
* Gives a textual representation of this integer in base-10.
*
* @return A textual representation of this integer in base-10.
*/
@Override
public java.lang.String toString() {
return "Int" + QuoteName() + ": " + v + "";
}
}
/**
* TAG_Long
*/
public static final class Long extends Tag {
/**
* The value of this long.
*/
public long v;
/**
* The normal constructor.
*
* @param name The name of this long.
* @param l The initial value of the long.
*/
public Long(java.lang.String name, long l) {
super(name);
v = l;
}
/**
* The DeserializePayload constructor.
*
* @param name The name of this long.
* @param i The <code>InputStream</code> to deserialize the long from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException when pigs fly.
*/
public Long(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
this(name, new DataInputStream(i).readLong());
}
/**
* Returns the tag type corresponding to TAG_Long.
*
* @return <code>Type.LONG</code>.
*/
@Override
public Type Type() {
return Type.LONG;
}
/**
* Serializes the long to the <code>OutputStream</code>.
*
* @param o The <code>OutputStream</code> to serialize the long to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
new DataOutputStream(o).writeLong(v);
}
/**
* Gives a textual representation of this long in base-10.
*
* @return A textual representation of this long in base-10.
*/
@Override
public java.lang.String toString() {
return "Long" + QuoteName() + ": " + v + "";
}
}
/**
* TAG_Float
*/
public static final class Float extends Tag {
/**
* The value of this float.
*/
public float v;
/**
* The normal constructor.
*
* @param name The name of this float.
* @param f The initial value of the float.
*/
public Float(java.lang.String name, float f) {
super(name);
v = f;
}
/**
* The DeserializePayload constructor.
*
* @param name The name of this float.
* @param i The <code>InputStream</code> to deserialize the float from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException when pigs fly.
*/
public Float(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
this(name, new DataInputStream(i).readFloat());
}
/**
* Returns the tag type corresponding to TAG_Float.
*
* @return <code>Type.FLOAT</code>.
*/
@Override
public Type Type() {
return Type.FLOAT;
}
/**
* Serializes this float to the <code>OutputStream</code>
*
* @param o The <code>OutputStream</code> to serialize this long to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
new DataOutputStream(o).writeFloat(v);
}
/**
* Gives a textual representation of this float in base-10.
*
* @return A textual representation of this float in base-10.
*/
@Override
public java.lang.String toString() {
return "Float" + QuoteName() + ": " + v + "";
}
}
/**
* TAG_Double
*/
public static final class Double extends Tag {
/**
* The value of this double.
*/
public double v;
/**
* The normal constructor.
*
* @param name The name of this double.
* @param d The initial value of the double.
*/
public Double(java.lang.String name, double d) {
super(name);
v = d;
}
/**
* The DeserializePayload constructor.
*
* @param name The name of this double.
* @param i The <code>InputStream</code> to deserialize the double from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException when pigs fly.
*/
public Double(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
this(name, new DataInputStream(i).readDouble());
}
/**
* Returns the tag type corresponding to TAG_Double.
*
* @return <code>Type.DOUBLE</code>.
*/
@Override
public Type Type() {
return Type.DOUBLE;
}
/**
* Serializes the double to the <code>OutputStream</code>.
*
* @param o The <code>OutputStream</code> to serialize this double to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
new DataOutputStream(o).writeDouble(v);
}
/**
* Gives a textual representation of this double in base-10.
*
* @return A textual representation of this double in base-10.
*/
@Override
public java.lang.String toString() {
return "Double" + QuoteName() + ": " + v + "";
}
}
/**
* TAG_Byte_Array
*/
public static final class ByteArray extends Tag {
/**
* The byte array in raw form.
*/
public byte[] v;
/**
* The normal constructor.
*
* @param name The name of this byte array.
* @param b The initial byte array.
*/
public ByteArray(java.lang.String name, byte[] b) {
super(name);
v = b;
}
/**
* The DeserializePayload constructor.
*
* @param name The name of this byte array.
* @param i The <code>InputStream</code> to deserialize the byte array
* from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException if the byte array
* size is negative.
*/
public ByteArray(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
this(name, (byte[]) null);
DataInputStream dis = new DataInputStream(i);
int size = dis.readInt();
if (size < 0) {
throw new FormatException("Byte Array size was negative: " + size);
}
//if (size > 4096)
// System.out.println("Suspect size="+size);
if (Runtime.getRuntime().freeMemory() < 1024 * 1024) {
System.err.println("Low memory! " + Runtime.getRuntime().freeMemory());
}
try {
v = new byte[size];
} catch (OutOfMemoryError e) {
System.err.println("Out of memory allocating " + size + "! " + Runtime.getRuntime().freeMemory());
Runtime.getRuntime().gc();
System.err.println("After gc: " + Runtime.getRuntime().freeMemory());
v = new byte[size];
}
dis.readFully(v);
}
/**
* Returns the tag type that corresponds to TAG_Byte_Array.
*
* @return <code>Type.BYTEARRAY</code>.
*/
@Override
public Type Type() {
return Type.BYTEARRAY;
}
/**
* Serializes the byte array to the <code>OutputStream</code>.
*
* @param o The <code>OutputStream</code> to serialize this byte array
* to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
DataOutputStream dos = new DataOutputStream(o);
dos.writeInt(v.length);
dos.write(v);
}
/**
* Gives a textual representation of this byte array with each byte in
* base-10.
*
* @return A textual representation of this byte array with each byte in
* base-10.
*/
@Override
public java.lang.String toString() {
java.lang.String s = "";
for (byte b : v) {
if (s.length() != 0) {
s += ", ";
}
s += b;
}
return "Byte Array" + QuoteName() + ": [" + s + "]";
}
/**
* Returns an independent clone of this Byte Array.
*
* @return An independent clone of this Byte Array.
*/
@Override
public ByteArray clone() {
ByteArray ba = (ByteArray) super.clone();
ba.v = Arrays.copyOf(v, v.length);
return ba;
}
}
/**
* TAG_String
* <p>
* The reason for the use of the fully qualified name
* <code>java.lang.String</code> throughout this code.
*/
public static final class String extends Tag {
/**
* The value of this string.
*/
public java.lang.String v;
/**
* The normal constructor.
*
* @param name The name of this string.
* @param s The initial string.
*/
public String(java.lang.String name, java.lang.String s) {
super(name);
v = s;
}
/**
* The DeserializePayload constructor.
*
* @param name The name of this string.
* @param i The <code>InputStream</code> to deserialize this string
* from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException if the string
* length is negative.
*/
public String(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
this(name, "");
DataInputStream dis = new DataInputStream(i);
short length = dis.readShort();
if (length < 0) {
throw new FormatException("String length was negative: " + length);
}
byte[] str = new byte[length];
dis.readFully(str);
v = new java.lang.String(str, UTF8);
}
/**
* Returns the tag type that corresponds to TAG_String.
*
* @return <code>Type.STRING</code>.
*/
@Override
public Type Type() {
return Type.STRING;
}
/**
* Serializes this string to the <code>OutputStream</code>.
*
* @param o The <code>OutputStream</code> to serialize this string to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
byte[] sarr = v.getBytes(UTF8);
new DataOutputStream(o).writeShort((short) sarr.length);
o.write(sarr);
}
/**
* Gives a textual representation of this string.
*
* @return A textual representation of this string.
*/
@Override
public java.lang.String toString() {
return "String" + QuoteName() + ": \"" + v + "\"";
}
}
/**
* TAG_List
*/
public static final class List extends Tag implements Iterable<Tag> {
/**
* The tag type this tags supports.
*/
private final Type type;
/**
* The list of tags in this list.
*/
private java.util.List<Tag> list = new ArrayList<Tag>();
/**
* The normal constructor.
*
* @param name The name of the tags.
* @param _type The tag type this tags supports.
* @throws IllegalArgumentException if the tag type is TAG_End or null,
* or a given tag is not a supported type or has a name that is not
* null.
*/
public List(java.lang.String name, Type _type, Tag... tags) throws IllegalArgumentException {
super(name);
if (_type == Type.END) {
throw new IllegalArgumentException("Can't have a list of TAG_End");
} else if (_type == null) {
throw new IllegalArgumentException("The tag type was null");
}
type = _type;
for (Tag t : tags) {
if (t.Name() != null) {
throw new IllegalArgumentException("Tags in Lists must have null names; given tag had name: \"" + t.Name() + "\"");
} else if (t.Type() == type) {
list.add(t);
} else {
throw new IllegalArgumentException(new Type.MismatchException(type + " required, given " + t.Type()));
}
}
}
/**
* The DeserializePayload constructor.
*
* @param name The name of the tags.
* @param i The <code>InputStream</code> to deserialize the tags from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException if the tag type is
* TAG_End, the tags size is negative, or some other exception is thrown
* while deserializing the tags.
*/
public List(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
super(name);
Type _type = Type.FromID(i.read());
if (_type == Type.END) {
throw new FormatException("Found a list of TAG_End");
}
type = _type;
int size = new DataInputStream(i).readInt();
if (size < 0) {
throw new FormatException("List size is negative: " + size);
}
try {
java.lang.reflect.Constructor<? extends Tag> c = type.ToClass().getConstructor(java.lang.String.class, InputStream.class);
for (int j = 0; j < size; ++j) {
list.add(c.newInstance(null, i));
}
} catch (Exception e) {
throw new FormatException(e);
}
}
/**
* Returns the tag type that corresponds to TAG_List.
*
* @return <code>Type.LIST</code>.
*/
@Override
public Type Type() {
return Type.LIST;
}
/**
* Serializes this tags to the <code>OutputStream</code>.
*
* @param o The <code>OutputStream</code> to serialize this tags to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
o.write((byte) type.ordinal());
new DataOutputStream(o).writeInt(list.size());
for (int i = 0; i < list.size(); ++i) {
list.get(i).SerializePayload(o);
}
}
/**
* Gives a textual representation of this tags with nice indenting even
* with nesting.
*
* @return A textual representation of this tags with nice indenting
* even with nesting.
*/
@Override
public java.lang.String toString() {
java.lang.String s = "";
for (int i = 0; i < list.size(); ++i) {
if (i != 0) {
s += ",\n";
}
s += list.get(i);
}
return "List of " + type + "" + QuoteName() + ": \n[\n" + Compound.PreceedLinesWithTabs(s) + "\n]";
}
/**
* Adds all given tags to this list until a given tag is of a type not
* supported by this list.
*
* @param tags The tags to be added.
* @throws jo.sm.plugins.ship.imp.nbt.Tag.Type.MismatchException if the
* tag is of a type not supported by this tags.
*/
public void Add(Tag... tags) throws Type.MismatchException {
for (Tag t : tags) {
if (t.Name() != null) {
throw new IllegalArgumentException("Tags in Lists must have null names; given tag had name: \"" + t.Name() + "\"");
} else if (t.Type() != type) {
throw new Type.MismatchException(type + " required, given " + t.Type());
}
list.add(t);
}
}
/**
* Adds all the tags from the given list to this list.
*
* @param l The list from which to add the tags.
* @throws jo.sm.plugins.ship.imp.nbt.Tag.Type.MismatchException if the
* tags in the given list are not supported by this list.
*/
public void AddFrom(Tag.List l) throws Type.MismatchException {
if (l.Supports() != type) {
throw new Type.MismatchException(type + " required, given list of " + l.Supports());
}
for (Tag t : l.list) {
list.add(t);
}
}
/**
* Adds all the tags from the given collection to this list.
*
* @param c The collection from which to add the tags.
* @throws jo.sm.plugins.ship.imp.nbt.Tag.Type.MismatchException if the
* tags in the given collection are not supported by this list.
*/
public void AddFrom(Collection<? extends Tag> c) throws Type.MismatchException {
for (Tag t : c) {
Add(t);
}
}
/**
* Returns the number of tags in this list tag.
*
* @return The number of tags in this list tag.
*/
public int Size() {
return list.size();
}
/**
* Sets the tag at the given index in the list to the given tag.
*
* @param index The index to set.
* @param t The tag that the index will be set to.
* @throws jo.sm.plugins.ship.imp.nbt.Tag.Type.Type.MismatchException if
* the tag is of a type not supported by this tags.
*/
public void Set(int index, Tag t) throws Type.MismatchException {
if (t.Type() != type) {
throw new Type.MismatchException(type + " required, given " + t.Type());
}
list.set(index, t);
}
/**
* Inserts the given tags to this list at the specified index.
*
* @param index The index before which to insert.
* @param tags The tags to insert.
* @throws jo.sm.plugins.ship.imp.nbt.Tag.Type.MismatchException if the
* tag is of a type not supported by this tags.
*/
public void Insert(int index, Tag... tags) throws Type.MismatchException {
for (Tag t : tags) {
if (t.Name() != null) {
throw new IllegalArgumentException("Tags in Lists must have null names; given tag had name: \"" + t.Name() + "\"");
} else if (t.Type() != type) {
throw new Type.MismatchException(type + " required, given " + t.Type());
}
list.add(index++, t);
}
}
/**
* Inserts the tags from the given list to this list at the specified
* index.
*
* @param index The index at which to insert the tags.
* @param l The list from which to insert the tags.
* @throws jo.sm.plugins.ship.imp.nbt.Tag.Type.MismatchException if the
* tags in the given list are not supported by this list.
*/
public void InsertFrom(int index, Tag.List l) throws Type.MismatchException {
if (l.Supports() != type) {
throw new Type.MismatchException(type + " required, given list of " + l.Supports());
}
for (Tag t : l.list) {
list.add(index++, t);
}
}
/**
* Inserts the tags from the given collection to this list at the
* specified index.
*
* @param index The index at which to insert the tags.
* @param c The collection from which to insert the tags.
* @throws jo.sm.plugins.ship.imp.nbt.Tag.Type.MismatchException if the
* tags in the given collection are not supported by this list.
*/
public void InsertFrom(int index, Collection<? extends Tag> c) throws Type.MismatchException {
for (Tag t : c) {
Insert(index++, t);
}
}
/**
* Getter for individual tags in this list tag.
*
* @param index The index to get a tag from.
* @return The tag at the specified index.
*/
public Tag Get(int index) {
return list.get(index);
}
/**
* Removes the tag at the specified index from this list tag.
*
* @param index The index of the tag to remove.
* @return The tag that was removed.
*/
public Tag Remove(int index) {
return list.remove(index);
}
/**
* Returns the tag type supported by this tags.
*
* @return The tag type supported by this tags.
*/
public Type Supports() {
return type;
}
/**
* Returns an iterator over this list tag.
*
* @return An iterator over this list tag.
*/
@Override
public Iterator<Tag> iterator() {
return list.iterator();
}
/**
* Returns an independent clone of this List tag.
*
* @return An independent clone of this List tag.
*/
@Override
public List clone() {
List li = (List) super.clone();
li.list = new ArrayList<Tag>();
for (int i = 0; i < list.size(); ++i) {
li.list.add(list.get(i).clone());
}
return li;
}
}
/**
* TAG_Compound
*/
public static final class Compound extends Tag implements Iterable<Tag> {
/**
* The list of tags in this compound tag.
*/
private HashMap<java.lang.String, Tag> tags = new HashMap<java.lang.String, Tag>();
/**
* The normal constructor.
*
* @param name The name of this compound tag.
* @param tags The initial tags in this compound tag.
* @throws IllegalArgumentException if the name of one of the given tags
* is null.
*/
public Compound(java.lang.String name, Tag... tags) throws IllegalArgumentException {
super(name);
for (Tag t : tags) {
java.lang.String n = t.Name();
if (n == null) {
throw new IllegalArgumentException("Tag names cannot be null");
}
if (t == End.TAG) {
throw new IllegalArgumentException("Cannot manually add the End tag!");
}
this.tags.put(n, t);
}
}
/**
* The DeserializePayload constructor.
*
* @param name The name of this compound tag.
* @param i The <code>InputStream</code> to deserialize the compound tag
* from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException if some other
* exception is thrown while deserializing the compound tag.
*/
public Compound(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
this(name);
try {
Type t;
while ((t = Type.FromID(i.read())) != Type.END) {
java.lang.String n = new String(null, i).v;
tags.put(n, t.ToClass().getConstructor(java.lang.String.class, InputStream.class).newInstance(n, i));
}
} catch (IOException e) {
throw e;
} catch (Throwable e) {
throw new FormatException("Exception while deserializing Compound tag", e, this);
}
}
/**
* Returns the tag type that corresponds to TAG_Compound.
*
* @return <code>Type.COMPOUND</code>.
*/
@Override
public Type Type() {
return Type.COMPOUND;
}
/**
* Serializes this compound tag to the <code>OutputStream</code>.
*
* @param o The <code>OutputStream</code> to serialize this compound tag
* to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
for (Tag t : tags.values()) {
t.Serialize(o);
}
End.TAG.Serialize(o);
}
/**
* Gives a textual representation of this compound tag with nice
* indenting even with nesting.
*
* @return A textual representation of this compound tag with nice
* indenting even with nesting.
*/
@Override
public java.lang.String toString() {
java.lang.String s = "";
for (Tag t : tags.values()) {
if (s.length() != 0) {
s += ",\n";
}
s += t;
}
return "Compound" + QuoteName() + ":\n{\n" + PreceedLinesWithTabs(s) + "\n}";
}
/**
* Utility function used by this class and the List class for preceeding
* lines with tabs.
*
* @param s The string for which each line should be prefixed with a
* tab.
* @return The string with each line prefixed with an additional tab.
*/
/*default*/ static java.lang.String PreceedLinesWithTabs(java.lang.String s) {
return "\t" + s.replaceAll("\n", "\n\t");
}
/**
* Adds the tags to this compound tag.
*
* @param tags The tags to be added.
* @throws IllegalArgumentException if the name of one of the given tags
* is null.
*/
public void Add(Tag... tags) throws IllegalArgumentException {
for (Tag t : tags) {
java.lang.String n = t.Name();
if (n == null) {
throw new IllegalArgumentException("Tag names cannot be null");
}
if (t.Type() == Type.END) {
throw new IllegalArgumentException("Cannot manually add a TAG_End!");
}
this.tags.put(n, t);
}
}
/**
* Adds all the tags from the given compound tag to this compound tag.
*
* @param c The compound tag from which to add the tags.
*/
public void AddFrom(Tag.Compound c) {
tags.putAll(c.tags);
}
/**
* Adds the tags from the given collection to this compound tag.
*
* @param c The collection from which to add the tags.
* @throws IllegalArgumentException if the name of one of the given tags
* is null.
*/
public void AddFrom(Collection<? extends Tag> c) throws IllegalArgumentException {
for (Tag t : c) {
Add(t);
}
}
/**
* Returns the number of tags in this compound tag.
*
* @return The number of tags in this compound tag.
*/
public int Size() {
return tags.size();
}
/**
* Returns the tag with the given name.
*
* @param name The name of the tag.
* @return The tag with the given name, or null if the tag does not
* exist.
*/
public Tag Get(java.lang.String name) {
return tags.get(name);
}
/**
* Returns the tag with the given type and name or throws an exception.
*
* @param type The type of tag.
* @param n The name of the tag.
* @return The tag, guaranteed to be of the type specified.
* @throws FormatException if the tag doesn't exist or is of the wrong
* type.
*/
public Tag Find(Type type, java.lang.String n) throws FormatException {
Tag t = tags.get(n);
if (t == null) {
throw new FormatException("No tag with the name \"" + n + "\"", this);
} else if (t.Type() != type) {
throw new FormatException("\"" + n + "\" is " + t.Type() + " instead of " + type, this);
}
return t;
}
/**
* Finds the List tag from the given name that supports.
*
* @param n The name of the list.
* @param type The type of tag the list must support.
* @return The List tag, guaranteed to support the type specified.
* @throws FormatException if the list doesn't exist or supports the
* wrong type.
*/
public List Find(java.lang.String n, Type type) throws FormatException {
Tag t = tags.get(n);
if (t == null) {
throw new FormatException("No List tag with the name \"" + n + "\"", this);
} else if (t.Type() != Type.LIST) {
throw new FormatException("\"" + n + "\" is " + t.Type() + " instead of a List tag", this);
}
List l = (List) t;
if (l.Supports() != type) {
throw new FormatException("\"" + n + "\" supports " + l.Supports() + " instead of " + type, this);
}
return l;
}
/**
* Removes the tag with the given name.
*
* @param n The name of the tag to remove.
* @return The tag that was removed, or null if the tag didn't exist.
*/
public Tag Remove(java.lang.String n) {
return tags.remove(n);
}
/**
* Returns an iterator over this compound tag.
*
* @return An iterator over this compound tag.
*/
@Override
public Iterator<Tag> iterator() {
return tags.values().iterator();
}
/**
* Returns an independent clone of this Compound tag.
*
* @return An independent clone of this Compound tag.
*/
@Override
public Compound clone() {
Compound c = (Compound) super.clone();
c.tags = new HashMap<java.lang.String, Tag>();
for (Map.Entry<java.lang.String, Tag> e : tags.entrySet()) {
c.tags.put(e.getKey(), e.getValue().clone());
}
return c;
}
}
/**
* TAG_Int_Array
*/
public static final class IntArray extends Tag {
/**
* The integer array in raw form.
*/
public int[] v;
/**
* The normal constructor.
*
* @param name The name of this integer array.
* @param i The initial integer array.
*/
public IntArray(java.lang.String name, int[] i) {
super(name);
v = i;
}
/**
* The DeserializePayload constructor.
*
* @param name The name of this integer array.
* @param i The <code>InputStream</code> to deserialize the integer
* array from.
* @throws IOException if the input operation generates an exception.
* @throws jo.sm.plugins.ship.imp.nbt.FormatException if the integer
* array size is negative.
*/
public IntArray(java.lang.String name, InputStream i) throws IOException, FormatException //DeserializePayload
{
this(name, (int[]) null);
DataInputStream dis = new DataInputStream(i);
int size = dis.readInt();
if (size < 0) {
throw new FormatException("Integer Array size was negative: " + size);
}
v = new int[size];
for (int j = 0; j < size; ++j) {
v[j] = dis.readInt();
}
}
/**
* Returns the tag type that corresponds to TAG_Int_Array (?).
*
* @return <code>Type.INTARRAY</code>.
*/
@Override
public Type Type() {
return Type.INTARRAY;
}
/**
* Serializes the integer array to the <code>OutputStream</code>.
*
* @param o The <code>OutputStream</code> to serialize this integer
* array to.
* @throws IOException if the output operation generates an exception.
*/
@Override
protected void SerializePayload(OutputStream o) throws IOException {
DataOutputStream dos = new DataOutputStream(o);
dos.writeInt(v.length);
for (int i : v) {
dos.writeInt(i);
}
}
/**
* Gives a textual representation of this integer array with each
* integer in base-10.
*
* @return A textual representation of this integer array with each
* integer in base-10.
*/
@Override
public java.lang.String toString() {
java.lang.String s = "";
for (int i : v) {
if (s.length() != 0) {
s += ", ";
}
s += i;
}
return "Int Array" + QuoteName() + ": [" + s + "]";
}
/**
* Returns an independent clone of this Integer Array.
*
* @return An independent clone of this Integer Array.
*/
@Override
public IntArray clone() {
IntArray ia = (IntArray) super.clone();
ia.v = Arrays.copyOf(v, v.length);
return ia;
}
}
}