package net.glowstone.util.mojangson;
import net.glowstone.util.mojangson.ex.MojangsonParseException;
import net.glowstone.util.nbt.*;
import java.util.ArrayList;
import java.util.List;
import static net.glowstone.util.mojangson.MojangsonToken.*;
public class Mojangson {
/*
* You shall not construct.
*/
private Mojangson() {}
/**
* Detects the Tag type of the Mojangson string, and parses it. Convenience method for other parse methods.
* This method will fall back to an IntTag if it could not find an appropriate Tag type, and to String if the value
* could not be parsed as an Integer either.
* @param mojangson The Mojangson string
* @return The parsed NBT Tag
* @throws MojangsonParseException if the given Mojangson string could not be parsed.
*/
public static Tag parseTag(String mojangson) throws MojangsonParseException {
if (mojangson.startsWith(String.valueOf(STRING_QUOTES.getSymbol())) && mojangson.endsWith(String.valueOf(STRING_QUOTES.getSymbol()))) {
return parseString(mojangson);
}
if (mojangson.endsWith(String.valueOf(BYTE_SUFFIX.getSymbol()))) {
return parseByte(mojangson);
}
if (mojangson.endsWith(String.valueOf(DOUBLE_SUFFIX.getSymbol()))) {
return parseDouble(mojangson);
}
if (mojangson.endsWith(String.valueOf(LONG_SUFFIX.getSymbol()))) {
return parseLong(mojangson);
}
if (mojangson.endsWith(String.valueOf(FLOAT_SUFFIX.getSymbol()))) {
return parseFloat(mojangson);
}
if (mojangson.endsWith(String.valueOf(SHORT_SUFFIX.getSymbol()))) {
return parseShort(mojangson);
}
if (mojangson.startsWith(String.valueOf(ARRAY_START.getSymbol())) && mojangson.endsWith(String.valueOf(ARRAY_END.getSymbol()))) {
return parseArray(mojangson);
}
if (mojangson.startsWith(String.valueOf(COMPOUND_START.getSymbol())) && mojangson.endsWith(String.valueOf(COMPOUND_END.getSymbol()))) {
return parseCompound(mojangson);
}
try {
return parseInt(mojangson); // Check if the value is a valid integer
} catch (MojangsonParseException e) {
try {
return parseLong(mojangson); // Could be a long if the number is too large
} catch (MojangsonParseException e1) {
try {
return parseDouble(mojangson); // Could be a decimal number without a type assignation, defaults to double
} catch (MojangsonParseException e2) {
return parseString(mojangson); // Couldn't find anything matching it, assuming it is a String.
}
}
}
}
/**
* Parses an Integer value from a Mojangson string as an NBT IntTag
* @param mojangson The Mojangson string
* @return the parsed IntTag NBT value
* @throws MojangsonParseException if the Mojangson string could not be parsed as an Integer value.
*/
public static IntTag parseInt(String mojangson) throws MojangsonParseException {
try {
return new IntTag(Integer.valueOf(mojangson));
} catch (NumberFormatException nfe) {
throw new MojangsonParseException("\'" + mojangson + "\'", MojangsonParseException.ParseExceptionReason.INVALID_FORMAT_NUM);
}
}
/**
* Parses an String value from a Mojangson string as an NBT StringTag
* @param mojangson The Mojangson string
* @return the parsed StringTag NBT value
*/
public static StringTag parseString(String mojangson) {
Character lastChar = mojangson.charAt(mojangson.length() - 1);
Character firstChar = mojangson.charAt(0);
if (firstChar == STRING_QUOTES.getSymbol() && lastChar == STRING_QUOTES.getSymbol()) {
return new StringTag(mojangson.substring(1, mojangson.length() - 1));
} else {
return new StringTag(mojangson);
}
}
/**
* Parses a Long value from a Mojangson string as an NBT LongTag
* @param mojangson The Mojangson string
* @return the parsed LongTag NBT value
* @throws MojangsonParseException if the Mojangson string could not be parsed as a Long value.
*/
public static LongTag parseLong(String mojangson) throws MojangsonParseException {
Character lastChar = mojangson.charAt(mojangson.length() - 1);
if (lastChar.toString().toLowerCase().charAt(0) == MojangsonToken.LONG_SUFFIX.getSymbol()) {
mojangson = mojangson.substring(0, mojangson.length() - 1);
}
try {
return new LongTag(Long.valueOf(mojangson));
} catch (NumberFormatException nfe) {
throw new MojangsonParseException("\'" + mojangson + "\'", MojangsonParseException.ParseExceptionReason.INVALID_FORMAT_NUM);
}
}
/**
* Parses a Double value from a Mojangson string as an NBT DoubleTag
* @param mojangson The Mojangson string
* @return the parsed DoubleTag NBT value
* @throws MojangsonParseException if the Mojangson string could not be parsed as a Double value.
*/
public static DoubleTag parseDouble(String mojangson) throws MojangsonParseException {
Character lastChar = mojangson.charAt(mojangson.length() - 1);
if (lastChar.toString().toLowerCase().charAt(0) == MojangsonToken.DOUBLE_SUFFIX.getSymbol()) {
mojangson = mojangson.substring(0, mojangson.length() - 1);
}
try {
return new DoubleTag(Double.valueOf(mojangson));
} catch (NumberFormatException nfe) {
throw new MojangsonParseException("\'" + mojangson + "\'", MojangsonParseException.ParseExceptionReason.INVALID_FORMAT_NUM);
}
}
/**
* Parses a Float value from a Mojangson string as an NBT FloatTag
* @param mojangson The Mojangson string
* @return the parsed FloatTag NBT value
* @throws MojangsonParseException if the Mojangson string could not be parsed as a Flaot value.
*/
public static FloatTag parseFloat(String mojangson) throws MojangsonParseException {
Character lastChar = mojangson.charAt(mojangson.length() - 1);
if (lastChar.toString().toLowerCase().charAt(0) == MojangsonToken.FLOAT_SUFFIX.getSymbol()) {
mojangson = mojangson.substring(0, mojangson.length() - 1);
}
try {
return new FloatTag(Float.valueOf(mojangson));
} catch (NumberFormatException nfe) {
throw new MojangsonParseException("\'" + mojangson + "\'", MojangsonParseException.ParseExceptionReason.INVALID_FORMAT_NUM);
}
}
/**
* Parses a Short value from a Mojangson string as an NBT ShortTag
* @param mojangson The Mojangson string
* @return the parsed ShortTag NBT value
* @throws MojangsonParseException if the Mojangson string could not be parsed as a Short value.
*/
public static ShortTag parseShort(String mojangson) throws MojangsonParseException {
Character lastChar = mojangson.charAt(mojangson.length() - 1);
if (lastChar.toString().toLowerCase().charAt(0) == MojangsonToken.SHORT_SUFFIX.getSymbol()) {
mojangson = mojangson.substring(0, mojangson.length() - 1);
}
try {
return new ShortTag(Short.valueOf(mojangson));
} catch (NumberFormatException nfe) {
throw new MojangsonParseException("\'" + mojangson + "\'", MojangsonParseException.ParseExceptionReason.INVALID_FORMAT_NUM);
}
}
/**
* Parses a Byte value from a Mojangson string as an NBT ByteTag
* @param mojangson The Mojangson string
* @return the parsed ByteTag NBT value
* @throws MojangsonParseException if the Mojangson string could not be parsed as a Byte value.
*/
public static ByteTag parseByte(String mojangson) throws MojangsonParseException {
Character lastChar = mojangson.charAt(mojangson.length() - 1);
if (lastChar.toString().toLowerCase().charAt(0) == BYTE_SUFFIX.getSymbol()) {
mojangson = mojangson.substring(0, mojangson.length() - 1);
}
try {
return new ByteTag(Byte.valueOf(mojangson));
} catch (NumberFormatException nfe) {
throw new MojangsonParseException("\'" + mojangson + "\'", MojangsonParseException.ParseExceptionReason.INVALID_FORMAT_NUM);
}
}
/**
* Parses a Compound from a Mojangson string as an NBT CompoundTag
* @param mojangson The Mojangson string
* @return the parsed CompoundTag NBT value
* @throws MojangsonParseException if the Mojangson string could not be parsed as a Compound value.
*/
public static CompoundTag parseCompound(String mojangson) throws MojangsonParseException {
final int parseCompoundStart = 0; // Parsing context magic value
final int parseCompoundPairKey = 1; // Parsing context magic value
final int parseCompoundPairValue = 2; // Parsing context magic value
int context = parseCompoundStart; // The current context of the parser
String tmpval = "", tmpkey = ""; // Temporary key/value being parsed, in its raw form
int scope = 0; // The scope level of the compound, this allows coherent nested arrays and compounds.
boolean inString = false; // The current character is part of a string inclusion
CompoundTag tag = new CompoundTag();
for (int index = 0; index < mojangson.length(); index++) {
Character character = mojangson.charAt(index);
if (character == STRING_QUOTES.getSymbol()) {
inString = !inString;
}
if (character == WHITE_SPACE.getSymbol()) {
if (!inString)
continue;
}
if ((character == COMPOUND_START.getSymbol() || character == ARRAY_START.getSymbol()) && !inString) {
scope++;
}
if ((character == COMPOUND_END.getSymbol() || character == ARRAY_END.getSymbol()) && !inString) {
scope--;
}
if (context == parseCompoundStart) {
if (character != COMPOUND_START.getSymbol()) {
throw new MojangsonParseException("Index: " + index + ", symbol: \'" + character + "\'", MojangsonParseException.ParseExceptionReason.UNEXPECTED_SYMBOL);
}
context++;
continue;
}
if (context == parseCompoundPairKey) {
if (character == ELEMENT_PAIR_SEPERATOR.getSymbol() && scope <= 1) {
context++;
continue;
}
tmpkey += character;
continue;
}
if (context == parseCompoundPairValue) {
if ((character == ELEMENT_SEPERATOR.getSymbol() || character == COMPOUND_END.getSymbol()) && scope <= 1 && !inString) {
context = parseCompoundPairKey;
tag.getValue().put(tmpkey, parseTag(tmpval));
tmpkey = tmpval = "";
continue;
}
tmpval += character;
}
}
return tag;
}
/**
* Parses an Array value from a Mojangson string
* @param mojangson The Mojangson string
* @return a ByteArrayTag value if the array contains byte values, an IntArrayTag value if the array contains int values or a ListTag with the array's elements.
* @throws MojangsonParseException if the Mojangson string could not be parsed as an Array value.
*/
public static Tag parseArray(String mojangson) throws MojangsonParseException {
final int parseArrayStart = 0; // Parsing context magic value
final int parseArrayElement = 1; // Parsing context magic value
int context = parseArrayStart; // The current context of the parser
String tmpval = ""; // Temporary value being parsed, in its raw form
int scope = 0; // The scope level of the array, this allows coherent nested arrays and compounds.
boolean inString = false; // The current character is part of a string inclusion
TagType tagType = null; // The element content type.
List<Tag> values = new ArrayList<>();
for (int index = 0; index < mojangson.length(); index++) {
Character character = mojangson.charAt(index);
if (character == STRING_QUOTES.getSymbol()) {
inString = !inString;
}
if (character == WHITE_SPACE.getSymbol()) {
if (!inString)
continue;
}
if ((character == COMPOUND_START.getSymbol() || character == ARRAY_START.getSymbol()) && !inString) {
scope++;
}
if ((character == COMPOUND_END.getSymbol() || character == ARRAY_END.getSymbol()) && !inString) {
scope--;
}
if (context == parseArrayStart) {
if (character != ARRAY_START.getSymbol()) {
throw new MojangsonParseException("Index: " + index + ", symbol: \'" + character + "\'", MojangsonParseException.ParseExceptionReason.UNEXPECTED_SYMBOL);
}
context++;
continue;
}
if (context == parseArrayElement) {
if ((character == ELEMENT_SEPERATOR.getSymbol() || character == ARRAY_END.getSymbol()) && scope <= 1 && !inString) {
if (tmpval.length() == 0) {
continue;
}
Tag val = parseTag(tmpval);
if (tagType == null) {
tagType = val.getType();
} else if (tagType != val.getType()) {
throw new MojangsonParseException("Index: " + index + ", value: \'" + tmpval + "\'", MojangsonParseException.ParseExceptionReason.INCOMPATIBLE_TYPE);
}
values.add(val);
tmpval = "";
continue;
}
tmpval += character;
}
}
if (tagType == TagType.BYTE) {
byte[] bytes = new byte[values.size()];
for (int i = 0; i < values.size(); i++) {
bytes[i] = (byte) values.get(i).getValue();
}
return new ByteArrayTag(bytes);
} else if (tagType == TagType.INT) {
int[] ints = new int[values.size()];
for (int i = 0; i < values.size(); i++) {
ints[i] = (int) values.get(i).getValue();
}
return new IntArrayTag(ints);
} else {
return new ListTag<>(tagType, values);
}
}
/**
* Creates a Mojangson string from the given NBT Tag.
* Convenience method for generic tags (Tag).
*
* @param tag the NBT Tag to convert
* @return the converted Mojangson string
*/
public static String fromGenericTag(Tag tag) {
switch (tag.getType()) {
case BYTE:
return fromTag((ByteTag) tag);
case BYTE_ARRAY:
return fromTag((ByteArrayTag) tag);
case COMPOUND:
return fromTag((CompoundTag) tag);
case DOUBLE:
return fromTag((DoubleTag) tag);
case FLOAT:
return fromTag((FloatTag) tag);
case INT:
return fromTag((IntTag) tag);
case INT_ARRAY:
return fromTag((IntArrayTag) tag);
case LIST:
return fromTag((ListTag) tag);
case LONG:
return fromTag((LongTag) tag);
case SHORT:
return fromTag((ShortTag) tag);
case STRING:
return fromTag((StringTag) tag);
}
return null;
}
/**
* Creates a Mojangson string from the given Byte Tag.
*
* @param tag the Byte Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(ByteTag tag) {
return String.valueOf(tag.getValue()) + BYTE_SUFFIX;
}
/**
* Creates a Mojangson string from the given ByteArray Tag.
*
* @param tag the ByteArray Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(ByteArrayTag tag) {
StringBuilder builder = new StringBuilder();
builder.append(ARRAY_START);
boolean start = true;
for (byte value : tag.getValue()) {
ByteTag b = new ByteTag(value);
if (start) {
start = false;
} else {
builder.append(ELEMENT_SEPERATOR);
}
builder.append(fromTag(b));
}
builder.append(ARRAY_END);
return builder.toString();
}
/**
* Creates a Mojangson string from the given Compound Tag.
*
* @param tag the Compound Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(CompoundTag tag) {
StringBuilder builder = new StringBuilder();
builder.append(COMPOUND_START);
boolean start = true;
for (String key : tag.getValue().keySet()) {
if (start) {
start = false;
} else {
builder.append(ELEMENT_SEPERATOR);
}
builder.append(key).append(ELEMENT_PAIR_SEPERATOR);
Tag value = tag.getValue().get(key);
builder.append(fromGenericTag(value));
}
builder.append(COMPOUND_END);
return builder.toString();
}
/**
* Creates a Mojangson string from the given Double Tag.
*
* @param tag the Double Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(DoubleTag tag) {
return String.valueOf(tag.getValue()) + MojangsonToken.DOUBLE_SUFFIX;
}
/**
* Creates a Mojangson string from the given Float Tag.
*
* @param tag the Float Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(FloatTag tag) {
return String.valueOf(tag.getValue()) + MojangsonToken.FLOAT_SUFFIX;
}
/**
* Creates a Mojangson string from the given Int Tag.
*
* @param tag the Int Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(IntTag tag) {
return String.valueOf(tag.getValue());
}
/**
* Creates a Mojangson string from the given IntArray Tag.
*
* @param tag the IntArray Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(IntArrayTag tag) {
StringBuilder builder = new StringBuilder();
builder.append(ARRAY_START);
boolean start = true;
for (int value : tag.getValue()) {
IntTag i = new IntTag(value);
if (start) {
start = false;
} else {
builder.append(ELEMENT_SEPERATOR);
}
builder.append(fromTag(i));
}
builder.append(ARRAY_END);
return builder.toString();
}
/**
* Creates a Mojangson string from the given List Tag.
*
* @param tag the List Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(ListTag<Tag> tag) {
StringBuilder builder = new StringBuilder();
builder.append(ARRAY_START);
boolean start = true;
for (Tag value : tag.getValue()) {
if (start) {
start = false;
} else {
builder.append(ELEMENT_SEPERATOR);
}
builder.append(fromGenericTag(value));
}
builder.append(ARRAY_END);
return builder.toString();
}
/**
* Creates a Mojangson string from the given Long Tag.
*
* @param tag the Long Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(LongTag tag) {
return String.valueOf(tag.getValue()) + MojangsonToken.LONG_SUFFIX;
}
/**
* Creates a Mojangson string from the given Short Tag.
*
* @param tag the Short Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(ShortTag tag) {
return String.valueOf(tag.getValue()) + MojangsonToken.SHORT_SUFFIX;
}
/**
* Creates a Mojangson string from the given String Tag.
*
* @param tag the String Tag to convert
* @return the converted Mojangson string
*/
public static String fromTag(StringTag tag) {
return String.valueOf(MojangsonToken.STRING_QUOTES) + tag.getValue() + MojangsonToken.STRING_QUOTES;
}
}