Refactoring: Made NbtList and NbtCompound into generic interfaces.

This commit is contained in:
Kristian S. Stangeland 2013-01-08 21:11:29 +01:00
parent 749f006621
commit c2ea92ab37
9 changed files with 1138 additions and 669 deletions

View File

@ -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,126 +12,26 @@ import java.util.Set;
*
* @author Kristian
*/
public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterable<NbtBase<?>> {
// A list container
private NbtElement<Map<String, Object>> container;
// Saved wrapper map
private ConvertedMap<String, Object, NbtBase<?>> 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.<Map<String, NbtBase<?>>>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<? extends NbtBase<?>> 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<Map<String,Object>>(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<Map<String, NbtBase<?>>>, Iterable<NbtBase<?>> {
/**
* 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<String> getKeys() {
return getValue().keySet();
}
@Override
public Map<String, NbtBase<?>> getValue() {
// Return a wrapper map
if (savedMap == null) {
savedMap = new ConvertedMap<String, Object, NbtBase<?>>(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;
}
@Override
public void setValue(Map<String, NbtBase<?>> newValue) {
// Write all the entries
for (Map.Entry<String, NbtBase<?>> entry : newValue.entrySet()) {
put(entry.getValue());
}
}
public abstract Set<String> getKeys();
/**
* 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 <T> NbtBase<T> getValue(String key) {
return (NbtBase<T>) getValue().get(key);
}
public abstract <T> NbtBase<T> getValue(String key);
/**
* Retrieve a value by its key, or assign and return a new NBT element if it doesn't exist.
@ -157,48 +39,14 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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);
// 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 <T> NbtBase<T> getValueExact(String key) {
NbtBase<T> 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<Map<String, NbtBase<?>>> deepClone() {
return (NbtBase) container.deepClone();
}
public abstract NbtBase<?> getValueOrDefault(String key, NbtType type);
/**
* Set a entry based on its name.
* @param entry - entry with a name and value.
* @return This compound, for chaining.
*/
public <T> NbtCompound put(NbtBase<T> entry) {
getValue().put(entry.getName(), entry);
return this;
}
public abstract <T> NbtCompound put(NbtBase<T> entry);
/**
* Retrieve the string value of an entry identified by a given key.
@ -206,18 +54,14 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -225,10 +69,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -236,18 +77,14 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -255,10 +92,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -266,18 +100,14 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -285,10 +115,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -296,18 +123,14 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -315,10 +138,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -326,18 +146,14 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -345,10 +161,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -356,18 +169,14 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -375,10 +184,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -386,18 +192,14 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -405,10 +207,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -416,9 +215,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -426,10 +223,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -437,9 +231,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -447,10 +239,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -458,29 +247,21 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @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.
@ -488,30 +269,21 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @return The NBT list value of the entry.
* @throws IllegalArgumentException If the key doesn't exist.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> NbtList<T> getList(String key) {
return (NbtList) getValueExact(key);
}
public abstract <T> NbtList<T> 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 <T> NbtList<T> getListOrDefault(String key) {
return (NbtList<T>) getValueOrDefault(key, NbtType.TAG_LIST);
}
public abstract <T> NbtList<T> getListOrDefault(String key);
/**
* Associate a NBT list with the given key.
* @param list - the list value.
* @return This current compound, for chaining.
*/
public <T> NbtCompound put(NbtList<T> list) {
getValue().put(list.getName(), list);
return this;
}
public abstract <T> NbtCompound put(WrappedList<T> list);
/**
* Associate a new NBT list with the given key.
@ -519,52 +291,11 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
* @param list - the list of NBT elements.
* @return This current compound, for chaining.
*/
public <T> NbtCompound put(String key, Collection<? extends NbtBase<T>> list) {
return put(NbtList.fromList(key, list));
}
public abstract <T> NbtCompound put(String key, Collection<? extends NbtBase<T>> 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();
}
@Override
public Iterator<NbtBase<?>> 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<NbtBase<?>> iterator();
}

View File

@ -85,16 +85,16 @@ public class NbtFactory {
*/
@SuppressWarnings("unchecked")
public static <T> NbtWrapper<T> fromBase(NbtBase<T> base) {
if (base instanceof NbtElement) {
return (NbtElement<T>) base;
} else if (base instanceof NbtCompound) {
if (base instanceof WrappedElement) {
return (WrappedElement<T>) base;
} else if (base instanceof WrappedCompound) {
return (NbtWrapper<T>) base;
} else if (base instanceof NbtList) {
} else if (base instanceof WrappedList) {
return (NbtWrapper<T>) 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<String, NbtBase<?>>) value);
@ -102,7 +102,7 @@ public class NbtFactory {
} else if (base.getType() == NbtType.TAG_LIST) {
// As above
NbtList<T> copy = NbtList.fromName(base.getName());
WrappedList<T> copy = WrappedList.fromName(base.getName());
copy.setValue((List<NbtBase<T>>) base.getValue());
return (NbtWrapper<T>) copy;
@ -156,13 +156,13 @@ public class NbtFactory {
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static <T> NbtWrapper<T> fromNMS(Object handle) {
NbtElement<T> partial = new NbtElement<T>(handle);
WrappedElement<T> partial = new WrappedElement<T>(handle);
// See if this is actually a compound tag
if (partial.getType() == NbtType.TAG_COMPOUND)
return (NbtWrapper<T>) new NbtCompound(handle);
return (NbtWrapper<T>) 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<? extends NbtBase<?>> 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 <T> NbtList<T> 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<T>) new NbtCompound(handle);
return (NbtWrapper<T>) new WrappedCompound(handle);
else if (type == NbtType.TAG_LIST)
return (NbtWrapper<T>) new NbtList(handle);
return (NbtWrapper<T>) new WrappedList(handle);
else
return new NbtElement<T>(handle);
return new WrappedElement<T>(handle);
} catch (Exception e) {
// Inform the caller

View File

@ -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.
* <p>
@ -37,299 +13,108 @@ import com.google.common.collect.Iterables;
*
* @param <TType> - the value type of each NBT tag.
*/
public class NbtList<TType> implements NbtWrapper<List<NbtBase<TType>>>, Iterable<TType> {
public interface NbtList<TType> extends NbtBase<List<NbtBase<TType>>>, Iterable<TType> {
/**
* The name of every NBT tag in a list.
*/
public static String EMPTY_NAME = "";
// A list container
private NbtElement<List<Object>> container;
// Saved wrapper list
private ConvertedList<Object, NbtBase<TType>> savedList;
/**
* Construct a new empty NBT list.
* @param name - name of this list.
* @return The new empty NBT list.
*/
public static <T> NbtList<T> fromName(String name) {
return (NbtList<T>) NbtFactory.<List<NbtBase<T>>>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 <T> NbtList<T> fromArray(String name, T... elements) {
NbtList<T> 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 <T> NbtList<T> fromList(String name, Collection<? extends T> elements) {
NbtList<T> 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<List<Object>>(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();
}
public abstract NbtType getElementType();
@Override
public String getName() {
return container.getName();
}
/**
* Add a NBT list or NBT compound to the list.
* @param element
*/
public abstract void add(NbtBase<TType> element);
@Override
public void setName(String name) {
container.setName(name);
}
/**
* 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);
@Override
public List<NbtBase<TType>> getValue() {
if (savedList == null) {
savedList = new ConvertedList<Object, NbtBase<TType>>(container.getValue()) {
// Check and see if the element is valid
private void verifyElement(NbtBase<TType> 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.");
/**
* 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);
// 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());
}
}
/**
* 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);
@Override
public boolean add(NbtBase<TType> e) {
verifyElement(e);
return super.add(e);
}
/**
* 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);
@Override
public void add(int index, NbtBase<TType> element) {
verifyElement(element);
super.add(index, element);
}
/**
* 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);
@Override
public boolean addAll(Collection<? extends NbtBase<TType>> c) {
boolean empty = size() == 0;
boolean result = false;
/**
* 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);
for (NbtBase<TType> element : c) {
add(element);
result = true;
}
/**
* 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);
// See if we now added our first object(s)
if (empty && result) {
container.setSubType(get(0).getType());
}
return result;
}
/**
* 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);
@Override
protected Object toInner(NbtBase<TType> outer) {
if (outer == null)
return null;
return NbtFactory.fromBase(outer).getHandle();
}
/**
* Remove a given object from the list.
* @param remove - the object to remove.
*/
public abstract void remove(Object remove);
@Override
protected NbtBase<TType> toOuter(Object inner) {
if (inner == null)
return null;
return NbtFactory.fromNMS(inner);
}
/**
* 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);
@Override
public String toString() {
return NbtList.this.toString();
}
};
}
return savedList;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public NbtBase<List<NbtBase<TType>>> deepClone() {
return (NbtBase) container.deepClone();
}
public void add(NbtBase<TType> element) {
getValue().add(element);
}
@SuppressWarnings("unchecked")
public void add(String value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@SuppressWarnings("unchecked")
public void add(byte value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@SuppressWarnings("unchecked")
public void add(short value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@SuppressWarnings("unchecked")
public void add(int value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@SuppressWarnings("unchecked")
public void add(long value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@SuppressWarnings("unchecked")
public void add(double value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@SuppressWarnings("unchecked")
public void add(byte[] value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@SuppressWarnings("unchecked")
public void add(int[] value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
public int size() {
return getValue().size();
}
public TType getValue(int index) {
return getValue().get(index).getValue();
}
/**
* 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<NbtBase<TType>> asCollection() {
return getValue();
}
public abstract Collection<NbtBase<TType>> asCollection();
@Override
public void setValue(List<NbtBase<TType>> newValue) {
NbtBase<TType> lastElement = null;
List<Object> list = container.getValue();
list.clear();
// Set each underlying element
for (NbtBase<TType> 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<TType> other = (NbtList<TType>) obj;
return container.equals(other.container);
}
return false;
}
@Override
public int hashCode() {
return container.hashCode();
}
@Override
public Iterator<TType> iterator() {
return Iterables.transform(getValue(), new Function<NbtBase<TType>, TType>() {
@Override
public TType apply(@Nullable NbtBase<TType> 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<TType> iterator();
}

View File

@ -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<Map<String, NbtBase<?>>>, Iterable<NbtBase<?>>, NbtCompound {
// A list container
private WrappedElement<Map<String, Object>> container;
// Saved wrapper map
private ConvertedMap<String, Object, NbtBase<?>> 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.<Map<String, NbtBase<?>>>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<? extends NbtBase<?>> 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<Map<String,Object>>(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<String> getKeys() {
return getValue().keySet();
}
@Override
public Map<String, NbtBase<?>> getValue() {
// Return a wrapper map
if (savedMap == null) {
savedMap = new ConvertedMap<String, Object, NbtBase<?>>(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<String, NbtBase<?>> newValue) {
// Write all the entries
for (Map.Entry<String, NbtBase<?>> 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 <T> NbtBase<T> getValue(String key) {
return (NbtBase<T>) 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 <T> NbtBase<T> getValueExact(String key) {
NbtBase<T> 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<Map<String, 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 <T> NbtCompound put(NbtBase<T> 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 <T> NbtList<T> 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 <T> NbtList<T> getListOrDefault(String key) {
return (NbtList<T>) 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 <T> NbtCompound put(WrappedList<T> 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 <T> NbtCompound put(String key, Collection<? extends NbtBase<T>> 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<NbtBase<?>> 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();
}
}

View File

@ -34,7 +34,7 @@ import com.google.common.base.Objects;
*
* @param <TType> - type of the value field.
*/
public class NbtElement<TType> implements NbtWrapper<TType> {
public class WrappedElement<TType> implements NbtWrapper<TType> {
// Structure modifier for the base class
private static volatile StructureModifier<Object> baseModifier;
@ -57,7 +57,7 @@ public class NbtElement<TType> implements NbtWrapper<TType> {
* Initialize a NBT wrapper for a generic element.
* @param handle - the NBT element to wrap.
*/
NbtElement(Object handle) {
WrappedElement(Object handle) {
this.handle = handle;
}

View File

@ -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 <TType> - the type of the value in each NBT sub element.
*/
public class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, Iterable<TType>, NbtList<TType> {
// A list container
private WrappedElement<List<Object>> container;
// Saved wrapper list
private ConvertedList<Object, NbtBase<TType>> savedList;
/**
* Construct a new empty NBT list.
* @param name - name of this list.
* @return The new empty NBT list.
*/
public static <T> WrappedList<T> fromName(String name) {
return (WrappedList<T>) NbtFactory.<List<NbtBase<T>>>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 <T> WrappedList<T> fromArray(String name, T... elements) {
WrappedList<T> 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 <T> WrappedList<T> fromList(String name, Collection<? extends T> elements) {
WrappedList<T> 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<List<Object>>(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<NbtBase<TType>> getValue() {
if (savedList == null) {
savedList = new ConvertedList<Object, NbtBase<TType>>(container.getValue()) {
// Check and see if the element is valid
private void verifyElement(NbtBase<TType> 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<TType> e) {
verifyElement(e);
return super.add(e);
}
@Override
public void add(int index, NbtBase<TType> element) {
verifyElement(element);
super.add(index, element);
}
@Override
public boolean addAll(Collection<? extends NbtBase<TType>> c) {
boolean empty = size() == 0;
boolean result = false;
for (NbtBase<TType> 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<TType> outer) {
if (outer == null)
return null;
return NbtFactory.fromBase(outer).getHandle();
}
@Override
protected NbtBase<TType> 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<List<NbtBase<TType>>> deepClone() {
return (NbtBase) container.deepClone();
}
@Override
public void add(NbtBase<TType> element) {
getValue().add(element);
}
@Override
@SuppressWarnings("unchecked")
public void add(String value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@Override
@SuppressWarnings("unchecked")
public void add(byte value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@Override
@SuppressWarnings("unchecked")
public void add(short value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@Override
@SuppressWarnings("unchecked")
public void add(int value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@Override
@SuppressWarnings("unchecked")
public void add(long value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@Override
@SuppressWarnings("unchecked")
public void add(double value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@Override
@SuppressWarnings("unchecked")
public void add(byte[] value) {
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
}
@Override
@SuppressWarnings("unchecked")
public void add(int[] value) {
add((NbtBase<TType>) 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<NbtBase<TType>> asCollection() {
return getValue();
}
@Override
public void setValue(List<NbtBase<TType>> newValue) {
NbtBase<TType> lastElement = null;
List<Object> list = container.getValue();
list.clear();
// Set each underlying element
for (NbtBase<TType> 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<TType> other = (WrappedList<TType>) obj;
return container.equals(other.container);
}
return false;
}
@Override
public int hashCode() {
return container.hashCode();
}
@Override
public Iterator<TType> iterator() {
return Iterables.transform(getValue(), new Function<NbtBase<TType>, TType>() {
@Override
public TType apply(@Nullable NbtBase<TType> 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);
}
}

View File

@ -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));

View File

@ -34,7 +34,7 @@ public class NbtCompoundTest {
public void testCustomTags() {
NbtCustomTag<Integer> test = new NbtCustomTag<Integer>("hello", 12);
NbtCompound map = NbtCompound.fromName("test");
WrappedCompound map = WrappedCompound.fromName("test");
map.put(test);
// Note that the custom tag will be cloned

View File

@ -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);