From b5aaef25051f73b18f39789328db8b4e3385dabb Mon Sep 17 00:00:00 2001 From: sk89q Date: Mon, 14 Jul 2014 20:36:27 -0700 Subject: [PATCH] Cleaned up and added many methods to the NBT classes. --- .../java/com/sk89q/jnbt/ByteArrayTag.java | 30 +- src/main/java/com/sk89q/jnbt/ByteTag.java | 28 +- src/main/java/com/sk89q/jnbt/CompoundTag.java | 394 +++++++++++++++++- .../com/sk89q/jnbt/CompoundTagBuilder.java | 107 +++++ src/main/java/com/sk89q/jnbt/DoubleTag.java | 27 +- src/main/java/com/sk89q/jnbt/EndTag.java | 4 +- src/main/java/com/sk89q/jnbt/FloatTag.java | 28 +- src/main/java/com/sk89q/jnbt/IntArrayTag.java | 30 +- src/main/java/com/sk89q/jnbt/IntTag.java | 28 +- src/main/java/com/sk89q/jnbt/ListTag.java | 391 ++++++++++++++++- .../java/com/sk89q/jnbt/ListTagBuilder.java | 129 ++++++ src/main/java/com/sk89q/jnbt/LongTag.java | 27 +- .../java/com/sk89q/jnbt/NBTConstants.java | 6 - .../java/com/sk89q/jnbt/NBTInputStream.java | 52 +-- src/main/java/com/sk89q/jnbt/NBTUtils.java | 41 +- src/main/java/com/sk89q/jnbt/ShortTag.java | 28 +- src/main/java/com/sk89q/jnbt/StringTag.java | 30 +- src/main/java/com/sk89q/jnbt/Tag.java | 27 +- .../world/storage/NBTConversions.java | 58 +++ 19 files changed, 1232 insertions(+), 233 deletions(-) create mode 100644 src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java create mode 100644 src/main/java/com/sk89q/jnbt/ListTagBuilder.java create mode 100644 src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java diff --git a/src/main/java/com/sk89q/jnbt/ByteArrayTag.java b/src/main/java/com/sk89q/jnbt/ByteArrayTag.java index 1d58427be..59e7568e4 100644 --- a/src/main/java/com/sk89q/jnbt/ByteArrayTag.java +++ b/src/main/java/com/sk89q/jnbt/ByteArrayTag.java @@ -19,28 +19,28 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Byte_Array tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Byte_Array} tag. */ public final class ByteArrayTag extends Tag { - /** - * The value. - */ private final byte[] value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public ByteArrayTag(byte[] value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public ByteArrayTag(String name, byte[] value) { super(name); @@ -67,7 +67,7 @@ public final class ByteArrayTag extends Tag { if (name != null && !name.equals("")) { append = "(\"" + this.getName() + "\")"; } - return "TAG_Byte_Array" + append + ": " + hex.toString(); + return "TAG_Byte_Array" + append + ": " + hex; } } diff --git a/src/main/java/com/sk89q/jnbt/ByteTag.java b/src/main/java/com/sk89q/jnbt/ByteTag.java index 3adf3ec8c..56b6d4857 100644 --- a/src/main/java/com/sk89q/jnbt/ByteTag.java +++ b/src/main/java/com/sk89q/jnbt/ByteTag.java @@ -19,28 +19,28 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Byte tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Byte} tag. */ public final class ByteTag extends Tag { - /** - * The value. - */ private final byte value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public ByteTag(byte value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public ByteTag(String name, byte value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/CompoundTag.java b/src/main/java/com/sk89q/jnbt/CompoundTag.java index 28b3d8da5..7550dd159 100644 --- a/src/main/java/com/sk89q/jnbt/CompoundTag.java +++ b/src/main/java/com/sk89q/jnbt/CompoundTag.java @@ -20,40 +20,401 @@ package com.sk89q.jnbt; import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; -import com.sk89q.jnbt.Tag; /** - * The TAG_Compound tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Compound} tag. */ public final class CompoundTag extends Tag { - /** - * The value. - */ private final Map value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public CompoundTag(Map value) { + super(); + this.value = Collections.unmodifiableMap(value); + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public CompoundTag(String name, Map value) { super(name); this.value = Collections.unmodifiableMap(value); } + /** + * Returns whether this compound tag contains the given key. + * + * @param key the given key + * @return true if the tag contains the given key + */ + public boolean containsKey(String key) { + return value.containsKey(key); + } + @Override public Map getValue() { return value; } + /** + * Return a new compound tag with the given values. + * + * @param value the value + * @return the new compound tag + */ + public CompoundTag setValue(Map value) { + return new CompoundTag(getName(), value); + } + + /** + * Create a compound tag builder. + * + * @return the builder + */ + public CompoundTagBuilder createBuilder() { + return new CompoundTagBuilder(new HashMap(value)); + } + + /** + * Get a byte array named with the given key. + * + *

If the key does not exist or its value is not a byte array tag, + * then an empty byte array will be returned.

+ * + * @param key the key + * @return a byte array + */ + public byte[] getByteArray(String key) { + Tag tag = value.get(key); + if (tag instanceof ByteArrayTag) { + return ((ByteArrayTag) tag).getValue(); + } else { + return new byte[0]; + } + } + + /** + * Get a byte named with the given key. + * + *

If the key does not exist or its value is not a byte tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a byte + */ + public byte getByte(String key) { + Tag tag = value.get(key); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + } else { + return (byte) 0; + } + } + + /** + * Get a double named with the given key. + * + *

If the key does not exist or its value is not a double tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a double + */ + public double getDouble(String key) { + Tag tag = value.get(key); + if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a double named with the given key, even if it's another + * type of number. + * + *

If the key does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a double + */ + public double asDouble(String key) { + Tag tag = value.get(key); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue(); + + } else { + return 0; + } + } + + /** + * Get a float named with the given key. + * + *

If the key does not exist or its value is not a float tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a float + */ + public float getFloat(String key) { + Tag tag = value.get(key); + if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a {@code int[]} named with the given key. + * + *

If the key does not exist or its value is not an integer array tag, + * then an empty array will be returned.

+ * + * @param key the key + * @return an integer array + */ + public int[] getIntegerArray(String key) { + Tag tag = value.get(key); + if (tag instanceof IntArrayTag) { + return ((IntArrayTag) tag).getValue(); + } else { + return new int[0]; + } + } + + /** + * Get an integer named with the given key. + * + *

If the key does not exist or its value is not an integer tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return an integer + */ + public int getInteger(String key) { + Tag tag = value.get(key); + if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get an integer named with the given key, even if it's another + * type of number. + * + *

If the key does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return an integer + */ + public int asInteger(String key) { + Tag tag = value.get(key); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue().intValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue().intValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue().intValue(); + + } else { + return 0; + } + } + + /** + * Get a list of tags named with the given key. + * + *

If the key does not exist or its value is not a list tag, + * then an empty list will be returned.

+ * + * @param key the key + * @return a list of tags + */ + public List getList(String key) { + Tag tag = value.get(key); + if (tag instanceof ListTag) { + return ((ListTag) tag).getValue(); + } else { + return Collections.emptyList(); + } + } + + /** + * Get a {@code TagList} named with the given key. + * + *

If the key does not exist or its value is not a list tag, + * then an empty tag list will be returned.

+ * + * @param key the key + * @return a tag list instance + */ + public ListTag getListTag(String key) { + Tag tag = value.get(key); + if (tag instanceof ListTag) { + return (ListTag) tag; + } else { + return new ListTag(key, StringTag.class, Collections.emptyList()); + } + } + + /** + * Get a list of tags named with the given key. + * + *

If the key does not exist or its value is not a list tag, + * then an empty list will be returned. If the given key references + * a list but the list of of a different type, then an empty + * list will also be returned.

+ * + * @param key the key + * @return a list of tags + */ + @SuppressWarnings("unchecked") + public List getList(String key, Class listType) { + Tag tag = value.get(key); + if (tag instanceof ListTag) { + ListTag listTag = (ListTag) tag; + if (listTag.getType().equals(listType)) { + return (List) listTag.getValue(); + } else { + return Collections.emptyList(); + } + } else { + return Collections.emptyList(); + } + } + + /** + * Get a long named with the given key. + * + *

If the key does not exist or its value is not a long tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a long + */ + public long getLong(String key) { + Tag tag = value.get(key); + if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + } else { + return 0L; + } + } + + /** + * Get a long named with the given key, even if it's another + * type of number. + * + *

If the key does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a long + */ + public long asLong(String key) { + Tag tag = value.get(key); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue().longValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue().longValue(); + + } else { + return 0L; + } + } + + /** + * Get a short named with the given key. + * + *

If the key does not exist or its value is not a short tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a short + */ + public short getShort(String key) { + Tag tag = value.get(key); + if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a string named with the given key. + * + *

If the key does not exist or its value is not a string tag, + * then {@code ""} will be returned.

+ * + * @param key the key + * @return a string + */ + public String getString(String key) { + Tag tag = value.get(key); + if (tag instanceof StringTag) { + return ((StringTag) tag).getValue(); + } else { + return ""; + } + } + @Override public String toString() { String name = getName(); @@ -62,12 +423,9 @@ public final class CompoundTag extends Tag { append = "(\"" + this.getName() + "\")"; } StringBuilder bldr = new StringBuilder(); - bldr.append("TAG_Compound" + append + ": " + value.size() - + " entries\r\n{\r\n"); + bldr.append("TAG_Compound").append(append).append(": ").append(value.size()).append(" entries\r\n{\r\n"); for (Map.Entry entry : value.entrySet()) { - bldr.append(" " - + entry.getValue().toString().replaceAll("\r\n", "\r\n ") - + "\r\n"); + bldr.append(" ").append(entry.getValue().toString().replaceAll("\r\n", "\r\n ")).append("\r\n"); } bldr.append("}"); return bldr.toString(); diff --git a/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java b/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java new file mode 100644 index 000000000..58a3320a2 --- /dev/null +++ b/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java @@ -0,0 +1,107 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.jnbt; + +import java.util.HashMap; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helps create compound tags. + */ +public class CompoundTagBuilder { + + private final Map entries; + + /** + * Create a new instance. + */ + CompoundTagBuilder() { + this.entries = new HashMap(); + } + + /** + * Create a new instance and use the given map (which will be modified). + * + * @param value the value + */ + CompoundTagBuilder(Map value) { + checkNotNull(value); + this.entries = value; + } + + /** + * Put the given key and tag into the compound tag. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder put(String key, Tag value) { + checkNotNull(key); + checkNotNull(value); + entries.put(key, value); + return this; + } + + /** + * Put all the entries from the given map into this map. + * + * @param value the map of tags + * @return this object + */ + public CompoundTagBuilder putAll(Map value) { + checkNotNull(value); + for (Map.Entry entry : value.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + /** + * Build an unnamed compound tag with this builder's entries. + * + * @return the new compound tag + */ + public CompoundTag build() { + return new CompoundTag(new HashMap(entries)); + } + + /** + * Build a new compound tag with this builder's entries. + * + * @param name the name of the tag + * @return the created compound tag + */ + public CompoundTag build(String name) { + return new CompoundTag(name, new HashMap(entries)); + } + + /** + * Create a new builder instance. + * + * @return a new builder + */ + public static CompoundTagBuilder create() { + return new CompoundTagBuilder(); + } + +} diff --git a/src/main/java/com/sk89q/jnbt/DoubleTag.java b/src/main/java/com/sk89q/jnbt/DoubleTag.java index f3f69ea05..1ebcdf794 100644 --- a/src/main/java/com/sk89q/jnbt/DoubleTag.java +++ b/src/main/java/com/sk89q/jnbt/DoubleTag.java @@ -19,28 +19,29 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Double tag. - * - * @author Graham Edgecombe + * The {@code TAG_Double} tag. * */ public final class DoubleTag extends Tag { - /** - * The value. - */ private final double value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public DoubleTag(double value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public DoubleTag(String name, double value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/EndTag.java b/src/main/java/com/sk89q/jnbt/EndTag.java index ec5e167f1..5051cc939 100644 --- a/src/main/java/com/sk89q/jnbt/EndTag.java +++ b/src/main/java/com/sk89q/jnbt/EndTag.java @@ -19,8 +19,6 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** * The TAG_End tag. * @@ -33,7 +31,7 @@ public final class EndTag extends Tag { * Creates the tag. */ public EndTag() { - super(""); + super(); } @Override diff --git a/src/main/java/com/sk89q/jnbt/FloatTag.java b/src/main/java/com/sk89q/jnbt/FloatTag.java index bb005face..21b921b69 100644 --- a/src/main/java/com/sk89q/jnbt/FloatTag.java +++ b/src/main/java/com/sk89q/jnbt/FloatTag.java @@ -19,28 +19,28 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Float tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Float} tag. */ public final class FloatTag extends Tag { - /** - * The value. - */ private final float value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public FloatTag(float value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public FloatTag(String name, float value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/IntArrayTag.java b/src/main/java/com/sk89q/jnbt/IntArrayTag.java index 1765e50cf..b8dced001 100644 --- a/src/main/java/com/sk89q/jnbt/IntArrayTag.java +++ b/src/main/java/com/sk89q/jnbt/IntArrayTag.java @@ -19,31 +19,35 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; +import static com.google.common.base.Preconditions.checkNotNull; /** - * The TAG_Int_Array tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Int_Array} tag. */ public final class IntArrayTag extends Tag { - /** - * The value. - */ private final int[] value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public IntArrayTag(int[] value) { + super(); + checkNotNull(value); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public IntArrayTag(String name, int[] value) { super(name); + checkNotNull(value); this.value = value; } diff --git a/src/main/java/com/sk89q/jnbt/IntTag.java b/src/main/java/com/sk89q/jnbt/IntTag.java index 480b54ee9..5591408f6 100644 --- a/src/main/java/com/sk89q/jnbt/IntTag.java +++ b/src/main/java/com/sk89q/jnbt/IntTag.java @@ -19,28 +19,28 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Int tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Int} tag. */ public final class IntTag extends Tag { - /** - * The value. - */ private final int value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public IntTag(int value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public IntTag(String name, int value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/ListTag.java b/src/main/java/com/sk89q/jnbt/ListTag.java index 282791179..681340620 100644 --- a/src/main/java/com/sk89q/jnbt/ListTag.java +++ b/src/main/java/com/sk89q/jnbt/ListTag.java @@ -19,41 +19,44 @@ package com.sk89q.jnbt; +import javax.annotation.Nullable; import java.util.Collections; import java.util.List; -import com.sk89q.jnbt.NBTUtils; -import com.sk89q.jnbt.Tag; +import java.util.NoSuchElementException; + +import static com.google.common.base.Preconditions.checkNotNull; /** - * The TAG_List tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_List} tag. */ public final class ListTag extends Tag { - /** - * The type. - */ private final Class type; + private final List value; /** - * The value. + * Creates the tag with an empty name. + * + * @param type the type of tag + * @param value the value of the tag */ - private final List value; + public ListTag(Class type, List value) { + super(); + checkNotNull(value); + this.type = type; + this.value = Collections.unmodifiableList(value); + } /** * Creates the tag. * - * @param name - * The name. - * @param type - * The type of item in the list. - * @param value - * The value. + * @param name the name of the tag + * @param type the type of tag + * @param value the value of the tag */ public ListTag(String name, Class type, List value) { super(name); + checkNotNull(value); this.type = type; this.value = Collections.unmodifiableList(value); } @@ -72,6 +75,360 @@ public final class ListTag extends Tag { return value; } + /** + * Create a new list tag with this tag's name and type. + * + * @param list the new list + * @return a new list tag + */ + public ListTag setValue(List list) { + return new ListTag(getName(), getType(), list); + } + + /** + * Get the tag if it exists at the given index. + * + * @param index the index + * @return the tag or null + */ + @Nullable + public Tag getIfExists(int index) { + try { + return value.get(index); + } catch (NoSuchElementException e) { + return null; + } + } + + /** + * Get a byte array named with the given index. + * + *

If the index does not exist or its value is not a byte array tag, + * then an empty byte array will be returned.

+ * + * @param index the index + * @return a byte array + */ + public byte[] getByteArray(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ByteArrayTag) { + return ((ByteArrayTag) tag).getValue(); + } else { + return new byte[0]; + } + } + + /** + * Get a byte named with the given index. + * + *

If the index does not exist or its value is not a byte tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a byte + */ + public byte getByte(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + } else { + return (byte) 0; + } + } + + /** + * Get a double named with the given index. + * + *

If the index does not exist or its value is not a double tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a double + */ + public double getDouble(int index) { + Tag tag = getIfExists(index); + if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a double named with the given index, even if it's another + * type of number. + * + *

If the index does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a double + */ + public double asDouble(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue(); + + } else { + return 0; + } + } + + /** + * Get a float named with the given index. + * + *

If the index does not exist or its value is not a float tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a float + */ + public float getFloat(int index) { + Tag tag = getIfExists(index); + if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a {@code int[]} named with the given index. + * + *

If the index does not exist or its value is not an integer array tag, + * then an empty array will be returned.

+ * + * @param index the index + * @return an integer array + */ + public int[] getIntegerArray(int index) { + Tag tag = getIfExists(index); + if (tag instanceof IntArrayTag) { + return ((IntArrayTag) tag).getValue(); + } else { + return new int[0]; + } + } + + /** + * Get an integer named with the given index. + * + *

If the index does not exist or its value is not an integer tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return an integer + */ + public int getInteger(int index) { + Tag tag = getIfExists(index); + if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get an integer named with the given index, even if it's another + * type of number. + * + *

If the index does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return an integer + */ + public int asInteger(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue().intValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue().intValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue().intValue(); + + } else { + return 0; + } + } + + /** + * Get a list of tags named with the given index. + * + *

If the index does not exist or its value is not a list tag, + * then an empty list will be returned.

+ * + * @param index the index + * @return a list of tags + */ + public List getList(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ListTag) { + return ((ListTag) tag).getValue(); + } else { + return Collections.emptyList(); + } + } + + /** + * Get a {@code TagList} named with the given index. + * + *

If the index does not exist or its value is not a list tag, + * then an empty tag list will be returned.

+ * + * @param index the index + * @return a tag list instance + */ + public ListTag getListTag(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ListTag) { + return (ListTag) tag; + } else { + return new ListTag(StringTag.class, Collections.emptyList()); + } + } + + /** + * Get a list of tags named with the given index. + * + *

If the index does not exist or its value is not a list tag, + * then an empty list will be returned. If the given index references + * a list but the list of of a different type, then an empty + * list will also be returned.

+ * + * @param index the index + * @return a list of tags + */ + @SuppressWarnings("unchecked") + public List getList(int index, Class listType) { + Tag tag = getIfExists(index); + if (tag instanceof ListTag) { + ListTag listTag = (ListTag) tag; + if (listTag.getType().equals(listType)) { + return (List) listTag.getValue(); + } else { + return Collections.emptyList(); + } + } else { + return Collections.emptyList(); + } + } + + /** + * Get a long named with the given index. + * + *

If the index does not exist or its value is not a long tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a long + */ + public long getLong(int index) { + Tag tag = getIfExists(index); + if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + } else { + return 0L; + } + } + + /** + * Get a long named with the given index, even if it's another + * type of number. + * + *

If the index does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a long + */ + public long asLong(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue().longValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue().longValue(); + + } else { + return 0; + } + } + + /** + * Get a short named with the given index. + * + *

If the index does not exist or its value is not a short tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a short + */ + public short getShort(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a string named with the given index. + * + *

If the index does not exist or its value is not a string tag, + * then {@code ""} will be returned.

+ * + * @param index the index + * @return a string + */ + public String getString(int index) { + Tag tag = getIfExists(index); + if (tag instanceof StringTag) { + return ((StringTag) tag).getValue(); + } else { + return ""; + } + } + @Override public String toString() { String name = getName(); diff --git a/src/main/java/com/sk89q/jnbt/ListTagBuilder.java b/src/main/java/com/sk89q/jnbt/ListTagBuilder.java new file mode 100644 index 000000000..2934c2255 --- /dev/null +++ b/src/main/java/com/sk89q/jnbt/ListTagBuilder.java @@ -0,0 +1,129 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.jnbt; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helps create list tags. + */ +public class ListTagBuilder { + + private final Class type; + private final List entries; + + /** + * Create a new instance. + * + * @param type of tag contained in this list + */ + ListTagBuilder(Class type) { + checkNotNull(type); + this.type = type; + this.entries = new ArrayList(); + } + + /** + * Add the given tag. + * + * @param value the tag + * @return this object + */ + public ListTagBuilder add(Tag value) { + checkNotNull(value); + if (!type.isInstance(value)) { + throw new IllegalArgumentException(value.getClass().getCanonicalName() + " is not of expected type " + type.getCanonicalName()); + } + entries.add(value); + return this; + } + + /** + * Add all the tags in the given list. + * + * @param value a list of tags + * @return this object + */ + public ListTagBuilder addAll(Collection value) { + checkNotNull(value); + for (Tag v : value) { + add(v); + } + return this; + } + + /** + * Build an unnamed list tag with this builder's entries. + * + * @return the new list tag + */ + public ListTag build() { + return new ListTag(type, new ArrayList(entries)); + } + + /** + * Build a new list tag with this builder's entries. + * + * @param name the name of the tag + * @return the created list tag + */ + public ListTag build(String name) { + return new ListTag(name, type, new ArrayList(entries)); + } + + /** + * Create a new builder instance. + * + * @return a new builder + */ + public static ListTagBuilder create(Class type) { + return new ListTagBuilder(type); + } + + /** + * Create a new builder instance. + * + * @return a new builder + */ + public static ListTagBuilder createWith(T ... entries) { + checkNotNull(entries); + + if (entries.length == 0) { + throw new IllegalArgumentException("This method needs an array of at least one entry"); + } + + Class type = entries[0].getClass(); + for (int i = 1; i < entries.length; i++) { + if (!type.isInstance(entries[i])) { + throw new IllegalArgumentException("An array of different tag types was provided"); + } + } + + ListTagBuilder builder = new ListTagBuilder(type); + builder.addAll(Arrays.asList(entries)); + return builder; + } + +} diff --git a/src/main/java/com/sk89q/jnbt/LongTag.java b/src/main/java/com/sk89q/jnbt/LongTag.java index 121f1365d..26e1902a5 100644 --- a/src/main/java/com/sk89q/jnbt/LongTag.java +++ b/src/main/java/com/sk89q/jnbt/LongTag.java @@ -19,28 +19,29 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Long tag. - * - * @author Graham Edgecombe + * The {@code TAG_Long} tag. * */ public final class LongTag extends Tag { - /** - * The value. - */ private final long value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public LongTag(long value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public LongTag(String name, long value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/NBTConstants.java b/src/main/java/com/sk89q/jnbt/NBTConstants.java index f214f7dec..7c8001b43 100644 --- a/src/main/java/com/sk89q/jnbt/NBTConstants.java +++ b/src/main/java/com/sk89q/jnbt/NBTConstants.java @@ -29,14 +29,8 @@ import java.nio.charset.Charset; */ public final class NBTConstants { - /** - * The character set used by NBT (UTF-8). - */ public static final Charset CHARSET = Charset.forName("UTF-8"); - /** - * Tag type constants. - */ public static final int TYPE_END = 0, TYPE_BYTE = 1, TYPE_SHORT = 2, TYPE_INT = 3, TYPE_LONG = 4, TYPE_FLOAT = 5, TYPE_DOUBLE = 6, TYPE_BYTE_ARRAY = 7, TYPE_STRING = 8, TYPE_LIST = 9, diff --git a/src/main/java/com/sk89q/jnbt/NBTInputStream.java b/src/main/java/com/sk89q/jnbt/NBTInputStream.java index d50873d12..1d1c5215b 100644 --- a/src/main/java/com/sk89q/jnbt/NBTInputStream.java +++ b/src/main/java/com/sk89q/jnbt/NBTInputStream.java @@ -27,20 +27,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import com.sk89q.jnbt.ByteArrayTag; -import com.sk89q.jnbt.ByteTag; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.DoubleTag; -import com.sk89q.jnbt.EndTag; -import com.sk89q.jnbt.FloatTag; -import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.LongTag; -import com.sk89q.jnbt.NBTConstants; -import com.sk89q.jnbt.NBTUtils; -import com.sk89q.jnbt.ShortTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; /** *

@@ -60,19 +46,14 @@ import com.sk89q.jnbt.Tag; */ public final class NBTInputStream implements Closeable { - /** - * The data input stream. - */ private final DataInputStream is; /** - * Creates a new NBTInputStream, which will source its data + * Creates a new {@code NBTInputStream}, which will source its data * from the specified input stream. * - * @param is - * The input stream. - * @throws IOException - * if an I/O error occurs. + * @param is the input stream + * @throws IOException if an I/O error occurs */ public NBTInputStream(InputStream is) throws IOException { this.is = new DataInputStream(is); @@ -82,8 +63,7 @@ public final class NBTInputStream implements Closeable { * Reads an NBT tag from the stream. * * @return The tag that was read. - * @throws IOException - * if an I/O error occurs. + * @throws IOException if an I/O error occurs. */ public Tag readTag() throws IOException { return readTag(0); @@ -92,11 +72,9 @@ public final class NBTInputStream implements Closeable { /** * Reads an NBT from the stream. * - * @param depth - * The depth of this tag. + * @param depth the depth of this tag * @return The tag that was read. - * @throws IOException - * if an I/O error occurs. + * @throws IOException if an I/O error occurs. */ private Tag readTag(int depth) throws IOException { int type = is.readByte() & 0xFF; @@ -117,18 +95,13 @@ public final class NBTInputStream implements Closeable { /** * Reads the payload of a tag, given the name and type. * - * @param type - * The type. - * @param name - * The name. - * @param depth - * The depth. - * @return The tag. - * @throws IOException - * if an I/O error occurs. + * @param type the type + * @param name the name + * @param depth the depth + * @return the tag + * @throws IOException if an I/O error occurs. */ - private Tag readTagPayload(int type, String name, int depth) - throws IOException { + private Tag readTagPayload(int type, String name, int depth) throws IOException { switch (type) { case NBTConstants.TYPE_END: if (depth == 0) { @@ -197,6 +170,7 @@ public final class NBTInputStream implements Closeable { } } + @Override public void close() throws IOException { is.close(); } diff --git a/src/main/java/com/sk89q/jnbt/NBTUtils.java b/src/main/java/com/sk89q/jnbt/NBTUtils.java index 53983022c..20b3e5542 100644 --- a/src/main/java/com/sk89q/jnbt/NBTUtils.java +++ b/src/main/java/com/sk89q/jnbt/NBTUtils.java @@ -19,23 +19,31 @@ package com.sk89q.jnbt; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.storage.InvalidFormatException; import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; + /** * A class which contains NBT-related utility methods. * - * @author Graham Edgecombe - * */ public final class NBTUtils { + /** + * Default private constructor. + */ + private NBTUtils() { + } + /** * Gets the type name of a tag. * - * @param clazz - * The tag class. + * @param clazz the tag class * @return The type name. */ public static String getTypeName(Class clazz) { @@ -72,11 +80,9 @@ public final class NBTUtils { /** * Gets the type code of a tag class. * - * @param clazz - * The tag class. + * @param clazz the tag class * @return The type code. - * @throws IllegalArgumentException - * if the tag class is invalid. + * @throws IllegalArgumentException if the tag class is invalid. */ public static int getTypeCode(Class clazz) { if (clazz.equals(ByteArrayTag.class)) { @@ -112,11 +118,9 @@ public final class NBTUtils { /** * Gets the class of a type of tag. * - * @param type - * The type. + * @param type the type * @return The class. - * @throws IllegalArgumentException - * if the tag type is invalid. + * @throws IllegalArgumentException if the tag type is invalid. */ public static Class getTypeClass(int type) { switch (type) { @@ -151,10 +155,17 @@ public final class NBTUtils { } /** - * Default private constructor. + * Read a vector from a list tag containing ideally three values: the + * X, Y, and Z components. + * + *

For values that are unavailable, their values will be 0.

+ * + * @param listTag the list tag + * @return a vector */ - private NBTUtils() { - + public static Vector toVector(ListTag listTag) { + checkNotNull(listTag); + return new Vector(listTag.asDouble(0), listTag.asDouble(1), listTag.asDouble(2)); } /** diff --git a/src/main/java/com/sk89q/jnbt/ShortTag.java b/src/main/java/com/sk89q/jnbt/ShortTag.java index 0a658fa07..fd3ee3a4e 100644 --- a/src/main/java/com/sk89q/jnbt/ShortTag.java +++ b/src/main/java/com/sk89q/jnbt/ShortTag.java @@ -19,28 +19,28 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Short tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Short} tag. */ public final class ShortTag extends Tag { - /** - * The value. - */ private final short value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public ShortTag(short value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public ShortTag(String name, short value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/StringTag.java b/src/main/java/com/sk89q/jnbt/StringTag.java index b6687c6fd..409511ab5 100644 --- a/src/main/java/com/sk89q/jnbt/StringTag.java +++ b/src/main/java/com/sk89q/jnbt/StringTag.java @@ -19,31 +19,35 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; +import static com.google.common.base.Preconditions.checkNotNull; /** - * The TAG_String tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_String} tag. */ public final class StringTag extends Tag { - /** - * The value. - */ private final String value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public StringTag(String value) { + super(); + checkNotNull(value); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public StringTag(String name, String value) { super(name); + checkNotNull(value); this.value = value; } diff --git a/src/main/java/com/sk89q/jnbt/Tag.java b/src/main/java/com/sk89q/jnbt/Tag.java index 18a0a0420..2ab0861ea 100644 --- a/src/main/java/com/sk89q/jnbt/Tag.java +++ b/src/main/java/com/sk89q/jnbt/Tag.java @@ -19,33 +19,36 @@ package com.sk89q.jnbt; +import static com.google.common.base.Preconditions.checkNotNull; + /** - * Represents a single NBT tag. - * - * @author Graham Edgecombe - * + * Represents a NBT tag. */ public abstract class Tag { - /** - * The name of this tag. - */ private final String name; + /** + * Create a new tag with an empty name. + */ + Tag() { + this(""); + } + /** * Creates the tag with the specified name. * - * @param name - * The name. + * @param name the name */ - public Tag(String name) { + Tag(String name) { + checkNotNull(name); this.name = name; } /** * Gets the name of this tag. * - * @return The name of this tag. + * @return the name of this tag */ public final String getName() { return name; @@ -54,7 +57,7 @@ public abstract class Tag { /** * Gets the value of this tag. * - * @return The value of this tag. + * @return the value */ public abstract Object getValue(); diff --git a/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java b/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java new file mode 100644 index 000000000..43b24c39a --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java @@ -0,0 +1,58 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.storage; + +import com.sk89q.jnbt.ListTag; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Location; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Utility methods for working with NBT data used in Minecraft. + */ +public final class NBTConversions { + + private NBTConversions() { + } + + /** + * Read a {@code Location} from two list tags, the first of which contains + * three numbers for the X, Y, and Z components, and the second of + * which contains two numbers, the yaw and pitch in degrees. + * + *

For values that are unavailable, their values will be 0.

+ * + * @param extent the extent + * @param positionTag the position tag + * @param directionTag the direction tag + * @return a location + */ + public static Location toLocation(Extent extent, ListTag positionTag, ListTag directionTag) { + checkNotNull(extent); + checkNotNull(positionTag); + checkNotNull(directionTag); + return new Location( + extent, + positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), + ((float) Math.toRadians(directionTag.asDouble(0))), ((float) Math.toRadians(directionTag.asDouble(1)))); + } + +}