package net.minecraft.command.type.custom.json;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapterFactory;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.ParsingUtilities.PrimitiveCallback;
import net.minecraft.command.SyntaxErrorException;
import net.minecraft.command.arg.CommandArg;
import net.minecraft.command.collections.TypeIDs;
import net.minecraft.command.parser.Parser;
public class JsonUtilities
{
private JsonUtilities()
{
}
public static class DeserializationManager
{
private final ThreadLocal<Map<JsonElement, Map<Type, CacheResult>>> cached;
private final Gson gson;
private final Gson uncachedGson;
public DeserializationManager(final ThreadLocal<Map<JsonElement, Map<Type, CacheResult>>> cached, final Gson gson, final Gson uncachedGson)
{
this.cached = cached;
this.gson = gson;
this.uncachedGson = uncachedGson;
}
public DeserializationManager(final ThreadLocal<Map<JsonElement, Map<Type, CacheResult>>> cached, final Gson gson)
{
this.cached = cached;
this.gson = gson;
this.uncachedGson = gson;
}
public Map<Type, CacheResult> get(final JsonElement json)
{
return this.cached.get().get(json);
}
private Map<JsonElement, Map<Type, CacheResult>> get()
{
return this.cached.get();
}
private void set(final Map<JsonElement, Map<Type, CacheResult>> cached)
{
this.cached.set(cached);
}
public void resetCache()
{
this.cached.set(null);
}
public void initCache()
{
this.cached.set(new IdentityHashMap<JsonElement, Map<Type, CacheResult>>());
}
public void procCache(final JsonElement json, final Set<Type> types)
{
final Map<Type, CacheResult> results = new HashMap<>();
if (types != null)
for (final Type type : types)
try
{
results.put(type, new CacheResult.Res(this.uncachedGson.fromJson(json, type)));
} catch (final JsonSyntaxException ex)
{
results.put(type, new CacheResult.Ex(ex));
}
this.cached.get().put(json, results);
}
public <T> T fromJson(final JsonElement json, final Type typeOfT) throws JsonParseException
{
return this.gson.fromJson(json, typeOfT);
}
public <T> T fromJsonUncached(final JsonElement json, final Type typeOfT) throws JsonParseException
{
return this.uncachedGson.fromJson(json, typeOfT);
}
public abstract static class CacheResult
{
public abstract <T> T get() throws JsonParseException;
public static class Res extends CacheResult
{
private final Object res;
public Res(final Object res)
{
this.res = res;
}
@SuppressWarnings("unchecked")
@Override
public <T> T get() throws JsonParseException
{
return (T) this.res;
}
}
public static class Ex extends CacheResult
{
private final JsonParseException ex;
public Ex(final JsonParseException ex)
{
this.ex = ex;
}
@Override
public <T> T get() throws JsonParseException
{
throw this.ex;
}
}
}
public <T> CommandArg<T> createCmdArg(final CommandArg<JsonElement> element, final Type typeOfT)
{
final Map<JsonElement, Map<Type, CacheResult>> cached = this.get();
this.resetCache();
return new CommandArg<T>()
{
@Override
public T eval(final ICommandSender sender) throws CommandException
{
try
{
DeserializationManager.this.set(cached);
return DeserializationManager.this.fromJson(element.eval(sender), typeOfT);
} catch (final JsonParseException e)
{
throw new CommandException("Unable to parse JSON" + e.getMessage() == null ? "" : (" : " + e.getMessage()));
} finally
{
DeserializationManager.this.resetCache();
}
}
};
}
public static class Builder
{
private final ThreadLocal<Map<JsonElement, Map<Type, CacheResult>>> cached = new ThreadLocal<>();
private final GsonBuilder gson;
public Builder(final GsonBuilder gson)
{
this.gson = gson;
}
public Builder()
{
this.gson = new GsonBuilder();
}
public <T> JsonDeserializer<T> cachedDeserializer(final JsonDeserializer<T> deserializer)
{
return new JsonDeserializer<T>()
{
final ThreadLocal<Map<JsonElement, Map<Type, CacheResult>>> cached = Builder.this.cached;
@Override
public T deserialize(final JsonElement json, final Type type, final JsonDeserializationContext context) throws JsonParseException
{
final Map<Type, CacheResult> cached = this.cached.get().get(json);
if (cached == null)
return deserializer.deserialize(json, type, context);
final CacheResult res = cached.get(type);
if (res != null)
return res.get();
try
{
final T tmp = deserializer.deserialize(json, type, context);
cached.put(type, new CacheResult.Res(tmp));
return tmp;
} catch (final JsonParseException ex)
{
cached.put(type, new CacheResult.Ex(ex));
throw ex;
}
}
};
}
public Builder registerTypeHierarchyAdapter(final Class<?> baseType, final JsonDeserializer<?> typeAdapter)
{
this.gson.registerTypeHierarchyAdapter(baseType, this.cachedDeserializer(typeAdapter));
return this;
}
public Builder registerTypeAdapterFactory(final TypeAdapterFactory factory)
{
this.gson.registerTypeAdapterFactory(factory);
return this;
}
public DeserializationManager create(final Gson uncachedGson)
{
return new DeserializationManager(this.cached, this.gson.create(), uncachedGson);
}
public DeserializationManager create()
{
return new DeserializationManager(this.cached, this.gson.create());
}
}
}
public static abstract class JsonData implements PrimitiveCallback<String>
{
private final Map<JsonElement, Set<Type>> toProcess = new IdentityHashMap<>();
private final DeserializationManager manager;
public JsonData(final DeserializationManager manager)
{
this.manager = manager;
}
public JsonData(final JsonData data)
{
this.manager = data.manager;
}
public void put(final JsonElement json, final Set<Type> type)
{
this.put(json);
this.toProcess.put(json, type);
}
public abstract void put(final JsonElement json);
public abstract void add(CommandArg<JsonElement> data);
public void procCache()
{
for (final Entry<JsonElement, Set<Type>> data : this.toProcess.entrySet())
this.manager.procCache(data.getKey(), data.getValue());
}
@Override
public CommandArg<String> call(final Parser parser, final String s) throws SyntaxErrorException
{
this.put(new JsonPrimitive(s));
return null;
}
}
public static CommandArg<JsonElement> tranfsorm(final CommandArg<String> toTransform)
{
return new CommandArg<JsonElement>()
{
@Override
public JsonElement eval(final ICommandSender sender) throws CommandException
{
return new JsonPrimitive(toTransform.eval(sender));
}
};
}
public static CommandArg<JsonElement> parseSelector(final Parser parser) throws SyntaxErrorException
{
return JsonUtilities.tranfsorm(TypeIDs.String.selectorParser.parse(parser).arg());
}
public static CommandArg<JsonElement> parseLabel(final Parser parser) throws SyntaxErrorException
{
return JsonUtilities.tranfsorm(TypeIDs.String.labelParser.parse(parser).arg());
}
}