From c2ea92ab375bcd0813e5f8c25408cce78ecdfc41 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 8 Jan 2013 21:11:29 +0100 Subject: [PATCH] Refactoring: Made NbtList and NbtCompound into generic interfaces. --- .../protocol/wrappers/nbt/NbtCompound.java | 425 +++--------- .../protocol/wrappers/nbt/NbtFactory.java | 32 +- .../protocol/wrappers/nbt/NbtList.java | 387 +++-------- .../wrappers/nbt/WrappedCompound.java | 606 ++++++++++++++++++ .../{NbtElement.java => WrappedElement.java} | 4 +- .../protocol/wrappers/nbt/WrappedList.java | 346 ++++++++++ .../protocol/events/PacketContainerTest.java | 3 +- .../wrappers/nbt/NbtCompoundTest.java | 2 +- .../protocol/wrappers/nbt/NbtFactoryTest.java | 2 +- 9 files changed, 1138 insertions(+), 669 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java rename ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/{NbtElement.java => WrappedElement.java} (98%) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java index 52e60c1a..35cc0075 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java @@ -1,23 +1,5 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * This program 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 2 of - * the License, or (at your option) any later version. - * - * This program 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 this program; - * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - package com.comphenix.protocol.wrappers.nbt; -import java.io.DataOutput; import java.util.Collection; import java.util.Iterator; import java.util.Map; @@ -30,541 +12,290 @@ import java.util.Set; * * @author Kristian */ -public class NbtCompound implements NbtWrapper>>, Iterable> { - // A list container - private NbtElement> container; - - // Saved wrapper map - private ConvertedMap> savedMap; - - /** - * Construct a new NBT compound wrapper. - * @param name - the name of the wrapper. - * @return The wrapped NBT compound. - */ - public static NbtCompound fromName(String name) { - // Simplify things for the caller - return (NbtCompound) NbtFactory.>>ofType(NbtType.TAG_COMPOUND, name); - } - - /** - * Construct a new NBT compound wrapper initialized with a given list of NBT values. - * @param name - the name of the compound wrapper. - * @param list - the list of elements to add. - * @return The new wrapped NBT compound. - */ - public static NbtCompound fromList(String name, Collection> list) { - NbtCompound copy = fromName(name); - - for (NbtBase base : list) - copy.getValue().put(base.getName(), base); - return copy; - } - - /** - * Construct a wrapped compound from a given NMS handle. - * @param handle - the NMS handle. - */ - NbtCompound(Object handle) { - this.container = new NbtElement>(handle); - } - - @Override - public Object getHandle() { - return container.getHandle(); - } - - @Override - public NbtType getType() { - return NbtType.TAG_COMPOUND; - } - - @Override - public String getName() { - return container.getName(); - } - - @Override - public void setName(String name) { - container.setName(name); - } - +public interface NbtCompound extends NbtBase>>, Iterable> { /** * Determine if an entry with the given key exists or not. * @param key - the key to lookup. * @return TRUE if an entry with the given key exists, FALSE otherwise. */ - public boolean containsKey(String key) { - return getValue().containsKey(key); - } - + public abstract boolean containsKey(String key); + /** * Retrieve a Set view of the keys of each entry in this compound. * @return The keys of each entry. */ - public Set getKeys() { - return getValue().keySet(); - } - - @Override - public Map> getValue() { - // Return a wrapper map - if (savedMap == null) { - savedMap = new ConvertedMap>(container.getValue()) { - @Override - protected Object toInner(NbtBase outer) { - if (outer == null) - return null; - return NbtFactory.fromBase(outer).getHandle(); - } - - protected NbtBase toOuter(Object inner) { - if (inner == null) - return null; - return NbtFactory.fromNMS(inner); - }; - - @Override - public String toString() { - return NbtCompound.this.toString(); - } - }; - } - return savedMap; - } + public abstract Set getKeys(); - @Override - public void setValue(Map> newValue) { - // Write all the entries - for (Map.Entry> entry : newValue.entrySet()) { - put(entry.getValue()); - } - } - /** * Retrieve the value of a given entry. * @param key - key of the entry to retrieve. * @return The value of this entry, or NULL if not found. */ - @SuppressWarnings("unchecked") - public NbtBase getValue(String key) { - return (NbtBase) getValue().get(key); - } - + public abstract NbtBase getValue(String key); + /** * Retrieve a value by its key, or assign and return a new NBT element if it doesn't exist. * @param key - the key of the entry to find or create. * @param type - the NBT element we will create if not found. * @return The value that was retrieved or just created. */ - public NbtBase getValueOrDefault(String key, NbtType type) { - NbtBase nbt = getValue(key); + public abstract NbtBase getValueOrDefault(String key, NbtType type); - // Create or get a compound - if (nbt == null) - put(nbt = NbtFactory.ofType(type, key)); - else if (nbt.getType() != type) - throw new IllegalArgumentException("Cannot get tag " + nbt + ": Not a " + type); - - return nbt; - } - - /** - * Retrieve a value, or throw an exception. - * @param key - the key to retrieve. - * @return The value of the entry. - * @throws IllegalArgumentException If the key doesn't exist. - */ - private NbtBase getValueExact(String key) { - NbtBase value = getValue(key); - - // Only return a legal key - if (value != null) - return value; - else - throw new IllegalArgumentException("Cannot find key " + key); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public NbtBase>> deepClone() { - return (NbtBase) container.deepClone(); - } - /** * Set a entry based on its name. * @param entry - entry with a name and value. * @return This compound, for chaining. */ - public NbtCompound put(NbtBase entry) { - getValue().put(entry.getName(), entry); - return this; - } - + public abstract NbtCompound put(NbtBase entry); + /** * Retrieve the string value of an entry identified by a given key. * @param key - the key of the entry. * @return The string value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public String getString(String key) { - return (String) getValueExact(key).getValue(); - } - + public abstract String getString(String key); + /** * Retrieve the string value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public String getStringOrDefault(String key) { - return (String) getValueOrDefault(key, NbtType.TAG_STRING).getValue(); - } - + public abstract String getStringOrDefault(String key); + /** * Associate a NBT string value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, String value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, String value); + /** * Retrieve the byte value of an entry identified by a given key. * @param key - the key of the entry. * @return The byte value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public byte getByte(String key) { - return (Byte) getValueExact(key).getValue(); - } - + public abstract byte getByte(String key); + /** * Retrieve the byte value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public byte getByteOrDefault(String key) { - return (Byte) getValueOrDefault(key, NbtType.TAG_BYTE).getValue(); - } - + public abstract byte getByteOrDefault(String key); + /** * Associate a NBT byte value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, byte value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, byte value); + /** * Retrieve the short value of an entry identified by a given key. * @param key - the key of the entry. * @return The short value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public Short getShort(String key) { - return (Short) getValueExact(key).getValue(); - } - + public abstract Short getShort(String key); + /** * Retrieve the short value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public short getShortOrDefault(String key) { - return (Short) getValueOrDefault(key, NbtType.TAG_SHORT).getValue(); - } - + public abstract short getShortOrDefault(String key); + /** * Associate a NBT short value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, short value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, short value); + /** * Retrieve the integer value of an entry identified by a given key. * @param key - the key of the entry. * @return The integer value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public int getInteger(String key) { - return (Integer) getValueExact(key).getValue(); - } - + public abstract int getInteger(String key); + /** * Retrieve the integer value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public int getIntegerOrDefault(String key) { - return (Integer) getValueOrDefault(key, NbtType.TAG_INT).getValue(); - } - + public abstract int getIntegerOrDefault(String key); + /** * Associate a NBT integer value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, int value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, int value); + /** * Retrieve the long value of an entry identified by a given key. * @param key - the key of the entry. * @return The long value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public long getLong(String key) { - return (Long) getValueExact(key).getValue(); - } - + public abstract long getLong(String key); + /** * Retrieve the long value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public long getLongOrDefault(String key) { - return (Long) getValueOrDefault(key, NbtType.TAG_LONG).getValue(); - } - + public abstract long getLongOrDefault(String key); + /** * Associate a NBT long value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, long value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, long value); + /** * Retrieve the float value of an entry identified by a given key. * @param key - the key of the entry. * @return The float value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public float getFloat(String key) { - return (Float) getValueExact(key).getValue(); - } - + public abstract float getFloat(String key); + /** * Retrieve the float value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public float getFloatOrDefault(String key) { - return (Float) getValueOrDefault(key, NbtType.TAG_FLOAT).getValue(); - } - + public abstract float getFloatOrDefault(String key); + /** * Associate a NBT float value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, float value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, float value); + /** * Retrieve the double value of an entry identified by a given key. * @param key - the key of the entry. * @return The double value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public double getDouble(String key) { - return (Double) getValueExact(key).getValue(); - } - + public abstract double getDouble(String key); + /** * Retrieve the double value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public double getDoubleOrDefault(String key) { - return (Double) getValueOrDefault(key, NbtType.TAG_DOUBlE).getValue(); - } - + public abstract double getDoubleOrDefault(String key); + /** * Associate a NBT double value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, double value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, double value); + /** * Retrieve the byte array value of an entry identified by a given key. * @param key - the key of the entry. * @return The byte array value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public byte[] getByteArray(String key) { - return (byte[]) getValueExact(key).getValue(); - } - + public abstract byte[] getByteArray(String key); + /** * Associate a NBT byte array value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, byte[] value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, byte[] value); + /** * Retrieve the integer array value of an entry identified by a given key. * @param key - the key of the entry. * @return The integer array value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public int[] getIntegerArray(String key) { - return (int[]) getValueExact(key).getValue(); - } - + public abstract int[] getIntegerArray(String key); + /** * Associate a NBT integer array value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, int[] value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, int[] value); + /** * Retrieve the compound (map) value of an entry identified by a given key. * @param key - the key of the entry. * @return The compound value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - @SuppressWarnings("rawtypes") - public NbtCompound getCompound(String key) { - return (NbtCompound) ((NbtBase) getValueExact(key)); - } - + public abstract NbtCompound getCompound(String key); + /** * Retrieve a compound (map) value by its key, or create a new compound if it doesn't exist. * @param key - the key of the entry to find or create. * @return The compound value that was retrieved or just created. */ - public NbtCompound getCompoundOrDefault(String key) { - return (NbtCompound) getValueOrDefault(key, NbtType.TAG_COMPOUND); - } - + public abstract NbtCompound getCompoundOrDefault(String key); + /** * Associate a NBT compound with its name as key. * @param compound - the compound value. * @return This current compound, for chaining. */ - public NbtCompound put(NbtCompound compound) { - getValue().put(compound.getName(), compound); - return this; - } - + public abstract NbtCompound put(WrappedCompound compound); + /** * Retrieve the NBT list value of an entry identified by a given key. * @param key - the key of the entry. * @return The NBT list value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public NbtList getList(String key) { - return (NbtList) getValueExact(key); - } - + public abstract NbtList getList(String key); + /** * Retrieve a NBT list value by its key, or create a new list if it doesn't exist. * @param key - the key of the entry to find or create. * @return The compound value that was retrieved or just created. */ - @SuppressWarnings("unchecked") - public NbtList getListOrDefault(String key) { - return (NbtList) getValueOrDefault(key, NbtType.TAG_LIST); - } - + public abstract NbtList getListOrDefault(String key); + /** * Associate a NBT list with the given key. * @param list - the list value. * @return This current compound, for chaining. */ - public NbtCompound put(NbtList list) { - getValue().put(list.getName(), list); - return this; - } - + public abstract NbtCompound put(WrappedList list); + /** * Associate a new NBT list with the given key. * @param key - the key and name of the new NBT list. * @param list - the list of NBT elements. * @return This current compound, for chaining. */ - public NbtCompound put(String key, Collection> list) { - return put(NbtList.fromList(key, list)); - } - - @Override - public void write(DataOutput destination) { - NbtFactory.toStream(container, destination); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof NbtCompound) { - NbtCompound other = (NbtCompound) obj; - return container.equals(other.container); - } - return false; - } - - @Override - public int hashCode() { - return container.hashCode(); - } + public abstract NbtCompound put(String key, Collection> list); - @Override - public Iterator> iterator() { - return getValue().values().iterator(); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - builder.append("{"); - builder.append("\"name\": \"" + getName() + "\""); - - for (NbtBase element : this) { - builder.append(", "); - - // Wrap in quotation marks - if (element.getType() == NbtType.TAG_STRING) - builder.append("\"" + element.getName() + "\": \"" + element.getValue() + "\""); - else - builder.append("\"" + element.getName() + "\": " + element.getValue()); - } - - builder.append("}"); - return builder.toString(); - } -} + /** + * Retrieve an iterator view of the NBT tags stored in this compound. + * @return The tags stored in this compound. + */ + public abstract Iterator> iterator(); +} \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java index 0a512869..53c9d7c6 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -85,16 +85,16 @@ public class NbtFactory { */ @SuppressWarnings("unchecked") public static NbtWrapper fromBase(NbtBase base) { - if (base instanceof NbtElement) { - return (NbtElement) base; - } else if (base instanceof NbtCompound) { + if (base instanceof WrappedElement) { + return (WrappedElement) base; + } else if (base instanceof WrappedCompound) { return (NbtWrapper) base; - } else if (base instanceof NbtList) { + } else if (base instanceof WrappedList) { return (NbtWrapper) base; } else { if (base.getType() == NbtType.TAG_COMPOUND) { // Load into a NBT-backed wrapper - NbtCompound copy = NbtCompound.fromName(base.getName()); + WrappedCompound copy = WrappedCompound.fromName(base.getName()); T value = base.getValue(); copy.setValue((Map>) value); @@ -102,7 +102,7 @@ public class NbtFactory { } else if (base.getType() == NbtType.TAG_LIST) { // As above - NbtList copy = NbtList.fromName(base.getName()); + WrappedList copy = WrappedList.fromName(base.getName()); copy.setValue((List>) base.getValue()); return (NbtWrapper) copy; @@ -156,13 +156,13 @@ public class NbtFactory { */ @SuppressWarnings({"unchecked", "rawtypes"}) public static NbtWrapper fromNMS(Object handle) { - NbtElement partial = new NbtElement(handle); + WrappedElement partial = new WrappedElement(handle); // See if this is actually a compound tag if (partial.getType() == NbtType.TAG_COMPOUND) - return (NbtWrapper) new NbtCompound(handle); + return (NbtWrapper) new WrappedCompound(handle); else if (partial.getType() == NbtType.TAG_LIST) - return new NbtList(handle); + return new WrappedList(handle); else return partial; } @@ -306,7 +306,7 @@ public class NbtFactory { * @return The new wrapped NBT compound. */ public static NbtCompound ofCompound(String name, Collection> list) { - return NbtCompound.fromList(name, list); + return WrappedCompound.fromList(name, list); } /** @@ -314,8 +314,8 @@ public class NbtFactory { * @param name - the name of the compound wrapper. * @return The new wrapped NBT compound. */ - public static NbtCompound ofCompound(String name) { - return NbtCompound.fromName(name); + public static WrappedCompound ofCompound(String name) { + return WrappedCompound.fromName(name); } /** @@ -325,7 +325,7 @@ public class NbtFactory { * @return The new filled NBT list. */ public static NbtList ofList(String name, T... elements) { - return NbtList.fromArray(name, elements); + return WrappedList.fromArray(name, elements); } /** @@ -354,11 +354,11 @@ public class NbtFactory { Object handle = methodCreateTag.invoke(null, (byte) type.getRawID(), name); if (type == NbtType.TAG_COMPOUND) - return (NbtWrapper) new NbtCompound(handle); + return (NbtWrapper) new WrappedCompound(handle); else if (type == NbtType.TAG_LIST) - return (NbtWrapper) new NbtList(handle); + return (NbtWrapper) new WrappedList(handle); else - return new NbtElement(handle); + return new WrappedElement(handle); } catch (Exception e) { // Inform the caller diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java index 16514c31..f4db93e2 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java @@ -1,33 +1,9 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * This program 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 2 of - * the License, or (at your option) any later version. - * - * This program 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 this program; - * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - package com.comphenix.protocol.wrappers.nbt; -import java.io.DataOutput; import java.util.Collection; import java.util.Iterator; import java.util.List; -import javax.annotation.Nullable; - -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.collect.Iterables; - /** * Represents a list of NBT tags of the same type without names. *

@@ -37,299 +13,108 @@ import com.google.common.collect.Iterables; * * @param - the value type of each NBT tag. */ -public class NbtList implements NbtWrapper>>, Iterable { +public interface NbtList extends NbtBase>>, Iterable { /** * The name of every NBT tag in a list. */ public static String EMPTY_NAME = ""; - // A list container - private NbtElement> container; - - // Saved wrapper list - private ConvertedList> savedList; - - /** - * Construct a new empty NBT list. - * @param name - name of this list. - * @return The new empty NBT list. - */ - public static NbtList fromName(String name) { - return (NbtList) NbtFactory.>>ofType(NbtType.TAG_LIST, name); - } - - /** - * Construct a NBT list of out an array of values.. - * @param name - name of this list. - * @param elements - values to add. - * @return The new filled NBT list. - */ - public static NbtList fromArray(String name, T... elements) { - NbtList result = fromName(name); - - for (T element : elements) { - if (element == null) - throw new IllegalArgumentException("An NBT list cannot contain a null element!"); - result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); - } - return result; - } - - /** - * Construct a NBT list of out a list of NBT elements. - * @param name - name of this list. - * @param elements - elements to add. - * @return The new filled NBT list. - */ - public static NbtList fromList(String name, Collection elements) { - NbtList result = fromName(name); - - for (T element : elements) { - if (element == null) - throw new IllegalArgumentException("An NBT list cannot contain a null element!"); - result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); - } - return result; - } - - NbtList(Object handle) { - this.container = new NbtElement>(handle); - } - - @Override - public Object getHandle() { - return container.getHandle(); - } - - @Override - public NbtType getType() { - return NbtType.TAG_LIST; - } - /** * Get the type of each element. * @return Element type. */ - public NbtType getElementType() { - return container.getSubType(); - } - - @Override - public String getName() { - return container.getName(); - } + public abstract NbtType getElementType(); - @Override - public void setName(String name) { - container.setName(name); - } + /** + * Add a NBT list or NBT compound to the list. + * @param element + */ + public abstract void add(NbtBase element); - @Override - public List> getValue() { - if (savedList == null) { - savedList = new ConvertedList>(container.getValue()) { - // Check and see if the element is valid - private void verifyElement(NbtBase element) { - if (element == null) - throw new IllegalArgumentException("Cannot store NULL elements in list."); - if (!element.getName().equals(EMPTY_NAME)) - throw new IllegalArgumentException("Cannot add a the named NBT tag " + element + " to a list."); - - // Check element type - if (size() > 0) { - if (!element.getType().equals(getElementType())) { - throw new IllegalArgumentException( - "Cannot add " + element + " of " + element.getType() + " to a list of type " + getElementType()); - } - } else { - container.setSubType(element.getType()); - } - } - - @Override - public boolean add(NbtBase e) { - verifyElement(e); - return super.add(e); - } - - @Override - public void add(int index, NbtBase element) { - verifyElement(element); - super.add(index, element); - } - - @Override - public boolean addAll(Collection> c) { - boolean empty = size() == 0; - boolean result = false; - - for (NbtBase element : c) { - add(element); - result = true; - } - - // See if we now added our first object(s) - if (empty && result) { - container.setSubType(get(0).getType()); - } - return result; - } - - @Override - protected Object toInner(NbtBase outer) { - if (outer == null) - return null; - return NbtFactory.fromBase(outer).getHandle(); - } - - @Override - protected NbtBase toOuter(Object inner) { - if (inner == null) - return null; - return NbtFactory.fromNMS(inner); - } - - @Override - public String toString() { - return NbtList.this.toString(); - } - }; - } - return savedList; - } + /** + * Add a new string element to the list. + * @param value - the string element to add. + * @throws IllegalArgumentException If this is not a list of strings. + */ + public abstract void add(String value); - @SuppressWarnings({"unchecked", "rawtypes"}) - public NbtBase>> deepClone() { - return (NbtBase) container.deepClone(); - } - - public void add(NbtBase element) { - getValue().add(element); - } - - @SuppressWarnings("unchecked") - public void add(String value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(byte value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(short value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(int value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(long value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } + /** + * Add a new byte element to the list. + * @param value - the byte element to add. + * @throws IllegalArgumentException If this is not a list of bytes. + */ + public abstract void add(byte value); - @SuppressWarnings("unchecked") - public void add(double value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(byte[] value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(int[] value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - public int size() { - return getValue().size(); - } - - public TType getValue(int index) { - return getValue().get(index).getValue(); - } + /** + * Add a new short element to the list. + * @param value - the short element to add. + * @throws IllegalArgumentException If this is not a list of shorts. + */ + public abstract void add(short value); + + /** + * Add a new integer element to the list. + * @param value - the string element to add. + * @throws IllegalArgumentException If this is not a list of integers. + */ + public abstract void add(int value); + + /** + * Add a new long element to the list. + * @param value - the string element to add. + * @throws IllegalArgumentException If this is not a list of longs. + */ + public abstract void add(long value); + + /** + * Add a new double element to the list. + * @param value - the double element to add. + * @throws IllegalArgumentException If this is not a list of doubles. + */ + public abstract void add(double value); + + /** + * Add a new byte array element to the list. + * @param value - the byte array element to add. + * @throws IllegalArgumentException If this is not a list of byte arrays. + */ + public abstract void add(byte[] value); + + /** + * Add a new int array element to the list. + * @param value - the int array element to add. + * @throws IllegalArgumentException If this is not a list of int arrays. + */ + public abstract void add(int[] value); + + /** + * Remove a given object from the list. + * @param remove - the object to remove. + */ + public abstract void remove(Object remove); + + /** + * Retrieve an element by index. + * @param index - index of the element to retrieve. + * @return The element to retrieve. + * @throws IndexOutOfBoundsException If the index is out of range (index < 0 || index >= size()) + */ + public abstract TType getValue(int index); + /** + * Retrieve the number of elements in this list. + * @return The number of elements in this list. + */ + public abstract int size(); + /** * Retrieve each NBT tag in this list. * @return A view of NBT tag in this list. */ - public Collection> asCollection() { - return getValue(); - } - - @Override - public void setValue(List> newValue) { - NbtBase lastElement = null; - List list = container.getValue(); - list.clear(); - - // Set each underlying element - for (NbtBase type : newValue) { - if (type != null) { - lastElement = type; - list.add(NbtFactory.fromBase(type).getHandle()); - } else { - list.add(null); - } - } - - // Update the sub type as well - if (lastElement != null) { - container.setSubType(lastElement.getType()); - } - } - - @Override - public void write(DataOutput destination) { - NbtFactory.toStream(container, destination); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof NbtList) { - @SuppressWarnings("unchecked") - NbtList other = (NbtList) obj; - return container.equals(other.container); - } - return false; - } - - @Override - public int hashCode() { - return container.hashCode(); - } + public abstract Collection> asCollection(); - @Override - public Iterator iterator() { - return Iterables.transform(getValue(), new Function, TType>() { - @Override - public TType apply(@Nullable NbtBase param) { - return param.getValue(); - } - }).iterator(); - } - - @Override - public String toString() { - // Essentially JSON - StringBuilder builder = new StringBuilder(); - - builder.append("{\"name\": \"" + getName() + "\", \"value\": ["); - - if (size() > 0) { - if (getElementType() == NbtType.TAG_STRING) - builder.append("\"" + Joiner.on("\", \"").join(this) + "\""); - else - builder.append(Joiner.on(", ").join(this)); - } - - builder.append("]}"); - return builder.toString(); - } -} + /** + * Iterate over all the elements in this list. + */ + public abstract Iterator iterator(); +} \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java new file mode 100644 index 00000000..2a5315dd --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java @@ -0,0 +1,606 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program 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 2 of + * the License, or (at your option) any later version. + * + * This program 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 this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.comphenix.protocol.wrappers.nbt; + +import java.io.DataOutput; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A concrete implementation of an NbtCompound that wraps an underlying NMS Compound. + * + * @author Kristian + */ +public class WrappedCompound implements NbtWrapper>>, Iterable>, NbtCompound { + // A list container + private WrappedElement> container; + + // Saved wrapper map + private ConvertedMap> savedMap; + + /** + * Construct a new NBT compound wrapper. + * @param name - the name of the wrapper. + * @return The wrapped NBT compound. + */ + public static WrappedCompound fromName(String name) { + // Simplify things for the caller + return (WrappedCompound) NbtFactory.>>ofType(NbtType.TAG_COMPOUND, name); + } + + /** + * Construct a new NBT compound wrapper initialized with a given list of NBT values. + * @param name - the name of the compound wrapper. + * @param list - the list of elements to add. + * @return The new wrapped NBT compound. + */ + public static NbtCompound fromList(String name, Collection> list) { + WrappedCompound copy = fromName(name); + + for (NbtBase base : list) + copy.getValue().put(base.getName(), base); + return copy; + } + + /** + * Construct a wrapped compound from a given NMS handle. + * @param handle - the NMS handle. + */ + WrappedCompound(Object handle) { + this.container = new WrappedElement>(handle); + } + + @Override + public Object getHandle() { + return container.getHandle(); + } + + @Override + public NbtType getType() { + return NbtType.TAG_COMPOUND; + } + + @Override + public String getName() { + return container.getName(); + } + + @Override + public void setName(String name) { + container.setName(name); + } + + /** + * Determine if an entry with the given key exists or not. + * @param key - the key to lookup. + * @return TRUE if an entry with the given key exists, FALSE otherwise. + */ + @Override + public boolean containsKey(String key) { + return getValue().containsKey(key); + } + + /** + * Retrieve a Set view of the keys of each entry in this compound. + * @return The keys of each entry. + */ + @Override + public Set getKeys() { + return getValue().keySet(); + } + + @Override + public Map> getValue() { + // Return a wrapper map + if (savedMap == null) { + savedMap = new ConvertedMap>(container.getValue()) { + @Override + protected Object toInner(NbtBase outer) { + if (outer == null) + return null; + return NbtFactory.fromBase(outer).getHandle(); + } + + protected NbtBase toOuter(Object inner) { + if (inner == null) + return null; + return NbtFactory.fromNMS(inner); + }; + + @Override + public String toString() { + return WrappedCompound.this.toString(); + } + }; + } + return savedMap; + } + + @Override + public void setValue(Map> newValue) { + // Write all the entries + for (Map.Entry> entry : newValue.entrySet()) { + put(entry.getValue()); + } + } + + /** + * Retrieve the value of a given entry. + * @param key - key of the entry to retrieve. + * @return The value of this entry, or NULL if not found. + */ + @Override + @SuppressWarnings("unchecked") + public NbtBase getValue(String key) { + return (NbtBase) getValue().get(key); + } + + /** + * Retrieve a value by its key, or assign and return a new NBT element if it doesn't exist. + * @param key - the key of the entry to find or create. + * @param type - the NBT element we will create if not found. + * @return The value that was retrieved or just created. + */ + @Override + public NbtBase getValueOrDefault(String key, NbtType type) { + NbtBase nbt = getValue(key); + + // Create or get a compound + if (nbt == null) + put(nbt = NbtFactory.ofType(type, key)); + else if (nbt.getType() != type) + throw new IllegalArgumentException("Cannot get tag " + nbt + ": Not a " + type); + + return nbt; + } + + /** + * Retrieve a value, or throw an exception. + * @param key - the key to retrieve. + * @return The value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + private NbtBase getValueExact(String key) { + NbtBase value = getValue(key); + + // Only return a legal key + if (value != null) + return value; + else + throw new IllegalArgumentException("Cannot find key " + key); + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public NbtBase>> deepClone() { + return (NbtBase) container.deepClone(); + } + + /** + * Set a entry based on its name. + * @param entry - entry with a name and value. + * @return This compound, for chaining. + */ + @Override + public NbtCompound put(NbtBase entry) { + getValue().put(entry.getName(), entry); + return this; + } + + /** + * Retrieve the string value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The string value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public String getString(String key) { + return (String) getValueExact(key).getValue(); + } + + /** + * Retrieve the string value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public String getStringOrDefault(String key) { + return (String) getValueOrDefault(key, NbtType.TAG_STRING).getValue(); + } + + /** + * Associate a NBT string value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, String value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the byte value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The byte value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public byte getByte(String key) { + return (Byte) getValueExact(key).getValue(); + } + + /** + * Retrieve the byte value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public byte getByteOrDefault(String key) { + return (Byte) getValueOrDefault(key, NbtType.TAG_BYTE).getValue(); + } + + /** + * Associate a NBT byte value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, byte value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the short value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The short value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public Short getShort(String key) { + return (Short) getValueExact(key).getValue(); + } + + /** + * Retrieve the short value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public short getShortOrDefault(String key) { + return (Short) getValueOrDefault(key, NbtType.TAG_SHORT).getValue(); + } + + /** + * Associate a NBT short value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, short value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the integer value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The integer value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public int getInteger(String key) { + return (Integer) getValueExact(key).getValue(); + } + + /** + * Retrieve the integer value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public int getIntegerOrDefault(String key) { + return (Integer) getValueOrDefault(key, NbtType.TAG_INT).getValue(); + } + + /** + * Associate a NBT integer value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, int value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the long value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The long value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public long getLong(String key) { + return (Long) getValueExact(key).getValue(); + } + + /** + * Retrieve the long value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public long getLongOrDefault(String key) { + return (Long) getValueOrDefault(key, NbtType.TAG_LONG).getValue(); + } + + /** + * Associate a NBT long value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, long value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the float value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The float value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public float getFloat(String key) { + return (Float) getValueExact(key).getValue(); + } + + /** + * Retrieve the float value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public float getFloatOrDefault(String key) { + return (Float) getValueOrDefault(key, NbtType.TAG_FLOAT).getValue(); + } + + /** + * Associate a NBT float value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, float value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the double value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The double value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public double getDouble(String key) { + return (Double) getValueExact(key).getValue(); + } + + /** + * Retrieve the double value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public double getDoubleOrDefault(String key) { + return (Double) getValueOrDefault(key, NbtType.TAG_DOUBlE).getValue(); + } + + /** + * Associate a NBT double value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, double value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the byte array value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The byte array value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public byte[] getByteArray(String key) { + return (byte[]) getValueExact(key).getValue(); + } + + /** + * Associate a NBT byte array value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, byte[] value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the integer array value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The integer array value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public int[] getIntegerArray(String key) { + return (int[]) getValueExact(key).getValue(); + } + + /** + * Associate a NBT integer array value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, int[] value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the compound (map) value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The compound value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + @SuppressWarnings("rawtypes") + public NbtCompound getCompound(String key) { + return (NbtCompound) ((NbtBase) getValueExact(key)); + } + + /** + * Retrieve a compound (map) value by its key, or create a new compound if it doesn't exist. + * @param key - the key of the entry to find or create. + * @return The compound value that was retrieved or just created. + */ + @Override + public NbtCompound getCompoundOrDefault(String key) { + return (NbtCompound) getValueOrDefault(key, NbtType.TAG_COMPOUND); + } + + /** + * Associate a NBT compound with its name as key. + * @param compound - the compound value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(WrappedCompound compound) { + getValue().put(compound.getName(), compound); + return this; + } + + /** + * Retrieve the NBT list value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The NBT list value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public NbtList getList(String key) { + return (NbtList) getValueExact(key); + } + + /** + * Retrieve a NBT list value by its key, or create a new list if it doesn't exist. + * @param key - the key of the entry to find or create. + * @return The compound value that was retrieved or just created. + */ + @Override + @SuppressWarnings("unchecked") + public NbtList getListOrDefault(String key) { + return (NbtList) getValueOrDefault(key, NbtType.TAG_LIST); + } + + /** + * Associate a NBT list with the given key. + * @param list - the list value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(WrappedList list) { + getValue().put(list.getName(), list); + return this; + } + + /** + * Associate a new NBT list with the given key. + * @param key - the key and name of the new NBT list. + * @param list - the list of NBT elements. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, Collection> list) { + return put(WrappedList.fromList(key, list)); + } + + @Override + public void write(DataOutput destination) { + NbtFactory.toStream(container, destination); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof WrappedCompound) { + WrappedCompound other = (WrappedCompound) obj; + return container.equals(other.container); + } + return false; + } + + @Override + public int hashCode() { + return container.hashCode(); + } + + @Override + public Iterator> iterator() { + return getValue().values().iterator(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append("{"); + builder.append("\"name\": \"" + getName() + "\""); + + for (NbtBase element : this) { + builder.append(", "); + + // Wrap in quotation marks + if (element.getType() == NbtType.TAG_STRING) + builder.append("\"" + element.getName() + "\": \"" + element.getValue() + "\""); + else + builder.append("\"" + element.getName() + "\": " + element.getValue()); + } + + builder.append("}"); + return builder.toString(); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedElement.java similarity index 98% rename from ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java rename to ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedElement.java index 488e681a..963d7bdf 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedElement.java @@ -34,7 +34,7 @@ import com.google.common.base.Objects; * * @param - type of the value field. */ -public class NbtElement implements NbtWrapper { +public class WrappedElement implements NbtWrapper { // Structure modifier for the base class private static volatile StructureModifier baseModifier; @@ -57,7 +57,7 @@ public class NbtElement implements NbtWrapper { * Initialize a NBT wrapper for a generic element. * @param handle - the NBT element to wrap. */ - NbtElement(Object handle) { + WrappedElement(Object handle) { this.handle = handle; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java new file mode 100644 index 00000000..4f2df203 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java @@ -0,0 +1,346 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program 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 2 of + * the License, or (at your option) any later version. + * + * This program 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 this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.comphenix.protocol.wrappers.nbt; + +import java.io.DataOutput; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.collect.Iterables; + +/** + * Represents a concrete implementation of an NBT list that wraps an underlying NMS list. + * @author Kristian + * + * @param - the type of the value in each NBT sub element. + */ +public class WrappedList implements NbtWrapper>>, Iterable, NbtList { + // A list container + private WrappedElement> container; + + // Saved wrapper list + private ConvertedList> savedList; + + /** + * Construct a new empty NBT list. + * @param name - name of this list. + * @return The new empty NBT list. + */ + public static WrappedList fromName(String name) { + return (WrappedList) NbtFactory.>>ofType(NbtType.TAG_LIST, name); + } + + /** + * Construct a NBT list of out an array of values.. + * @param name - name of this list. + * @param elements - values to add. + * @return The new filled NBT list. + */ + public static WrappedList fromArray(String name, T... elements) { + WrappedList result = fromName(name); + + for (T element : elements) { + if (element == null) + throw new IllegalArgumentException("An NBT list cannot contain a null element!"); + result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); + } + return result; + } + + /** + * Construct a NBT list of out a list of NBT elements. + * @param name - name of this list. + * @param elements - elements to add. + * @return The new filled NBT list. + */ + public static WrappedList fromList(String name, Collection elements) { + WrappedList result = fromName(name); + + for (T element : elements) { + if (element == null) + throw new IllegalArgumentException("An NBT list cannot contain a null element!"); + result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); + } + return result; + } + + public WrappedList(Object handle) { + this.container = new WrappedElement>(handle); + } + + @Override + public Object getHandle() { + return container.getHandle(); + } + + @Override + public NbtType getType() { + return NbtType.TAG_LIST; + } + + /** + * Get the type of each element. + * @return Element type. + */ + @Override + public NbtType getElementType() { + return container.getSubType(); + } + + @Override + public String getName() { + return container.getName(); + } + + @Override + public void setName(String name) { + container.setName(name); + } + + @Override + public List> getValue() { + if (savedList == null) { + savedList = new ConvertedList>(container.getValue()) { + // Check and see if the element is valid + private void verifyElement(NbtBase element) { + if (element == null) + throw new IllegalArgumentException("Cannot store NULL elements in list."); + if (!element.getName().equals(EMPTY_NAME)) + throw new IllegalArgumentException("Cannot add a the named NBT tag " + element + " to a list."); + + // Check element type + if (size() > 0) { + if (!element.getType().equals(getElementType())) { + throw new IllegalArgumentException( + "Cannot add " + element + " of " + element.getType() + " to a list of type " + getElementType()); + } + } else { + container.setSubType(element.getType()); + } + } + + @Override + public boolean add(NbtBase e) { + verifyElement(e); + return super.add(e); + } + + @Override + public void add(int index, NbtBase element) { + verifyElement(element); + super.add(index, element); + } + + @Override + public boolean addAll(Collection> c) { + boolean empty = size() == 0; + boolean result = false; + + for (NbtBase element : c) { + add(element); + result = true; + } + + // See if we now added our first object(s) + if (empty && result) { + container.setSubType(get(0).getType()); + } + return result; + } + + @Override + protected Object toInner(NbtBase outer) { + if (outer == null) + return null; + return NbtFactory.fromBase(outer).getHandle(); + } + + @Override + protected NbtBase toOuter(Object inner) { + if (inner == null) + return null; + return NbtFactory.fromNMS(inner); + } + + @Override + public String toString() { + return WrappedList.this.toString(); + } + }; + } + return savedList; + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public NbtBase>> deepClone() { + return (NbtBase) container.deepClone(); + } + + @Override + public void add(NbtBase element) { + getValue().add(element); + } + + @Override + @SuppressWarnings("unchecked") + public void add(String value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(byte value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(short value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(int value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(long value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(double value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(byte[] value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(int[] value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + public int size() { + return getValue().size(); + } + + @Override + public TType getValue(int index) { + return getValue().get(index).getValue(); + } + + /** + * Retrieve each NBT tag in this list. + * @return A view of NBT tag in this list. + */ + @Override + public Collection> asCollection() { + return getValue(); + } + + @Override + public void setValue(List> newValue) { + NbtBase lastElement = null; + List list = container.getValue(); + list.clear(); + + // Set each underlying element + for (NbtBase type : newValue) { + if (type != null) { + lastElement = type; + list.add(NbtFactory.fromBase(type).getHandle()); + } else { + list.add(null); + } + } + + // Update the sub type as well + if (lastElement != null) { + container.setSubType(lastElement.getType()); + } + } + + @Override + public void write(DataOutput destination) { + NbtFactory.toStream(container, destination); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof WrappedList) { + @SuppressWarnings("unchecked") + WrappedList other = (WrappedList) obj; + return container.equals(other.container); + } + return false; + } + + @Override + public int hashCode() { + return container.hashCode(); + } + + @Override + public Iterator iterator() { + return Iterables.transform(getValue(), new Function, TType>() { + @Override + public TType apply(@Nullable NbtBase param) { + return param.getValue(); + } + }).iterator(); + } + + @Override + public String toString() { + // Essentially JSON + StringBuilder builder = new StringBuilder(); + + builder.append("{\"name\": \"" + getName() + "\", \"value\": ["); + + if (size() > 0) { + if (getElementType() == NbtType.TAG_STRING) + builder.append("\"" + Joiner.on("\", \"").join(this) + "\""); + else + builder.append(Joiner.on(", ").join(this)); + } + + builder.append("]}"); + return builder.toString(); + } + + @Override + public void remove(Object remove) { + getValue().remove(remove); + } +} diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index 8359c753..6df0bf45 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -50,6 +50,7 @@ import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.comphenix.protocol.wrappers.nbt.NbtCompound; +import com.comphenix.protocol.wrappers.nbt.WrappedCompound; import com.comphenix.protocol.wrappers.nbt.NbtFactory; import com.google.common.collect.Iterables; @@ -256,7 +257,7 @@ public class PacketContainerTest { public void testGetNbtModifier() { PacketContainer updateTileEntity = new PacketContainer(132); - NbtCompound compound = NbtFactory.ofCompound("test"); + WrappedCompound compound = NbtFactory.ofCompound("test"); compound.put("test", "name"); compound.put(NbtFactory.ofList("ages", 1, 2, 3)); diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java index a485fc31..3b807eb3 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java @@ -34,7 +34,7 @@ public class NbtCompoundTest { public void testCustomTags() { NbtCustomTag test = new NbtCustomTag("hello", 12); - NbtCompound map = NbtCompound.fromName("test"); + WrappedCompound map = WrappedCompound.fromName("test"); map.put(test); // Note that the custom tag will be cloned diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java index 531fb58c..f31d301b 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java @@ -40,7 +40,7 @@ public class NbtFactoryTest { @Test public void testFromStream() { - NbtCompound compound = NbtCompound.fromName("tag"); + WrappedCompound compound = WrappedCompound.fromName("tag"); compound.put("name", "Test Testerson"); compound.put("age", 42);