package com.github.steveice10.opennbt.tag.builtin; import com.github.steveice10.opennbt.tag.TagCreateException; import com.github.steveice10.opennbt.tag.TagRegistry; import com.github.steveice10.opennbt.tag.limiter.TagLimiter; import org.jetbrains.annotations.Nullable; import java.io.DataInput; import java.io.DataOutput; import java.io.EOFException; import java.io.IOException; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * A compound tag containing other tags. */ public class CompoundTag extends Tag implements Iterable> { public static final int ID = 10; private Map value; /** * Creates a tag. */ public CompoundTag() { this(new LinkedHashMap<>()); } /** * Creates a tag. * * @param value The value of the tag. */ public CompoundTag(Map value) { this.value = new LinkedHashMap<>(value); } /** * Creates a tag without wrapping the map. * * @param value The value of the tag. */ public CompoundTag(LinkedHashMap value) { if (value == null) { throw new NullPointerException("value cannot be null"); } this.value = value; } @Override public Map getValue() { return this.value; } /** * Sets the value of this tag. * * @param value New value of this tag. */ public void setValue(Map value) { if (value == null) { throw new NullPointerException("value cannot be null"); } this.value = new LinkedHashMap<>(value); } /** * Sets the value of this tag without wrapping the map. * * @param value New value of this tag. */ public void setValue(LinkedHashMap value) { if (value == null) { throw new NullPointerException("value cannot be null"); } this.value = value; } /** * Checks whether the compound tag is empty. * * @return Whether the compound tag is empty. */ public boolean isEmpty() { return this.value.isEmpty(); } /** * Checks whether the compound tag contains a tag. * * @param tagName Name of the tag to check for. * @return Whether the compound tag contains a tag. */ public boolean contains(String tagName) { return this.value.containsKey(tagName); } /** * Gets the tag. * * @param Type of tag to get. * @param tagName Name of the tag. * @return The tag. */ @Nullable public T get(String tagName) { return (T) this.value.get(tagName); } /** * Puts the tag into this compound tag. * * @param Type of tag to put. * @param tagName Name of the tag. * @param tag Tag to put into this compound tag. * @return The previous tag associated with its name, or null if there wasn't one. */ @Nullable public T put(String tagName, T tag) { return (T) this.value.put(tagName, tag); } /** * Removes a tag from this compound tag. * * @param Type of tag to remove. * @param tagName Name of the tag to remove. * @return The removed tag. */ @Nullable public T remove(String tagName) { return (T) this.value.remove(tagName); } /** * Gets a set of keys in this compound tag. * * @return The compound tag's key set. */ public Set keySet() { return this.value.keySet(); } /** * Gets a collection of tags in this compound tag. * * @return This compound tag's tags. */ public Collection values() { return this.value.values(); } /** * Gets the entry set of this compound tag. * * @return The compound tag's entry set. */ public Set> entrySet() { return this.value.entrySet(); } /** * Gets the number of tags in this compound tag. * * @return This compound tag's size. */ public int size() { return this.value.size(); } /** * Clears all tags from this compound tag. */ public void clear() { this.value.clear(); } @Override public Iterator> iterator() { return this.value.entrySet().iterator(); } @Override public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException { try { tagLimiter.checkLevel(nestingLevel); int newNestingLevel = nestingLevel + 1; int id; while (true) { tagLimiter.countByte(); id = in.readByte(); if (id == 0) { // End tag break; } String name = in.readUTF(); tagLimiter.countBytes(2 * name.length()); Tag tag = TagRegistry.createInstance(id); tag.read(in, tagLimiter, newNestingLevel); this.value.put(name, tag); } } catch(TagCreateException e) { throw new IOException("Failed to create tag.", e); } catch(EOFException ignored) { throw new IOException("Closing tag was not found!"); } } @Override public void write(DataOutput out) throws IOException { for(Entry entry : this.value.entrySet()) { Tag tag = entry.getValue(); out.writeByte(tag.getTagId()); out.writeUTF(entry.getKey()); tag.write(out); } // End out.writeByte(0); } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CompoundTag tags = (CompoundTag) o; return this.value.equals(tags.value); } @Override public int hashCode() { return value.hashCode(); } @Override public final CompoundTag clone() { LinkedHashMap newMap = new LinkedHashMap<>(); for(Entry entry : this.value.entrySet()) { newMap.put(entry.getKey(), entry.getValue().clone()); } return new CompoundTag(newMap); } @Override public int getTagId() { return ID; } }