Java tutorial
/* * This file is part of text, licensed under the MIT License. * * Copyright (c) 2017-2019 KyoriPowered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package net.kyori.text.serializer.gson; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import net.kyori.text.BlockNbtComponent; import net.kyori.text.BuildableComponent; import net.kyori.text.Component; import net.kyori.text.ComponentBuilder; import net.kyori.text.EntityNbtComponent; import net.kyori.text.KeybindComponent; import net.kyori.text.NbtComponent; import net.kyori.text.ScoreComponent; import net.kyori.text.SelectorComponent; import net.kyori.text.TextComponent; import net.kyori.text.TranslatableComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.event.HoverEvent; import net.kyori.text.format.Style; import net.kyori.text.format.TextColor; import net.kyori.text.format.TextDecoration; import net.kyori.text.serializer.ComponentSerializer; import org.checkerframework.checker.nullness.qual.NonNull; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; public class GsonComponentSerializer implements ComponentSerializer<Component, Component, String>, JsonDeserializer<Component>, JsonSerializer<Component> { /** * A component serializer for JSON-based serialization and deserialization. */ public static final GsonComponentSerializer INSTANCE = new GsonComponentSerializer(); static final Gson GSON = populate(new GsonBuilder()).create(); /** * Populate a builder with our serializers. * * @param builder the gson builder * @return the gson builder */ public static @NonNull GsonBuilder populate(final @NonNull GsonBuilder builder) { builder.registerTypeHierarchyAdapter(Component.class, INSTANCE) .registerTypeAdapter(Style.class, StyleSerializer.INSTANCE) .registerTypeAdapter(ClickEvent.Action.class, new NameMapSerializer<>("click action", ClickEvent.Action.NAMES)) .registerTypeAdapter(HoverEvent.Action.class, new NameMapSerializer<>("hover action", HoverEvent.Action.NAMES)) .registerTypeAdapter(TextColorWrapper.class, new TextColorWrapper.Serializer()) .registerTypeAdapter(TextColor.class, new NameMapSerializer<>("text color", TextColor.NAMES)) .registerTypeAdapter(TextDecoration.class, new NameMapSerializer<>("text decoration", TextDecoration.NAMES)) .registerTypeHierarchyAdapter(BlockNbtComponent.Pos.class, BlockNbtComponentPosSerializer.INSTANCE); return builder; } static final String TEXT = "text"; static final String TRANSLATE = "translate"; static final String TRANSLATE_WITH = "with"; static final String SCORE = "score"; static final String SCORE_NAME = "name"; static final String SCORE_OBJECTIVE = "objective"; static final String SCORE_VALUE = "value"; static final String SELECTOR = "selector"; static final String KEYBIND = "keybind"; static final String EXTRA = "extra"; static final String NBT = "nbt"; static final String NBT_INTERPRET = "interpret"; static final String NBT_BLOCK = "block"; static final String NBT_ENTITY = "entity"; @Override public @NonNull Component deserialize(final @NonNull String string) { return GSON.fromJson(string, Component.class); } @Override public @NonNull String serialize(final @NonNull Component component) { return GSON.toJson(component); } // Not part of the API. @Deprecated @Override public Component deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException { return this.deserialize0(json, context); } private BuildableComponent<?, ?> deserialize0(final JsonElement element, final JsonDeserializationContext context) throws JsonParseException { if (element.isJsonPrimitive()) { return TextComponent.of(element.getAsString()); } else if (element.isJsonArray()) { ComponentBuilder<?, ?> parent = null; for (final JsonElement childElement : element.getAsJsonArray()) { final BuildableComponent<?, ?> child = this.deserialize0(childElement, context); if (parent == null) { parent = child.toBuilder(); } else { parent.append(child); } } if (parent == null) { throw new JsonParseException("Don't know how to turn " + element + " into a Component"); } return parent.build(); } else if (!element.isJsonObject()) { throw new JsonParseException("Don't know how to turn " + element + " into a Component"); } final JsonObject object = element.getAsJsonObject(); final ComponentBuilder<?, ?> component; if (object.has(TEXT)) { component = TextComponent.builder(object.get(TEXT).getAsString()); } else if (object.has(TRANSLATE)) { final String key = object.get(TRANSLATE).getAsString(); if (!object.has(TRANSLATE_WITH)) { component = TranslatableComponent.builder(key); } else { final JsonArray with = object.getAsJsonArray(TRANSLATE_WITH); final List<Component> args = new ArrayList<>(with.size()); for (int i = 0, size = with.size(); i < size; i++) { final JsonElement argElement = with.get(i); args.add(this.deserialize0(argElement, context)); } component = TranslatableComponent.builder(key).args(args); } } else if (object.has(SCORE)) { final JsonObject score = object.getAsJsonObject(SCORE); if (!score.has(SCORE_NAME) || !score.has(SCORE_OBJECTIVE)) { throw new JsonParseException( "A score component requires a " + SCORE_NAME + " and " + SCORE_OBJECTIVE); } // score components can have a value sometimes, let's grab it if (score.has(SCORE_VALUE)) { component = ScoreComponent.builder().name(score.get(SCORE_NAME).getAsString()) .objective(score.get(SCORE_OBJECTIVE).getAsString()) .value(score.get(SCORE_VALUE).getAsString()); } else { component = ScoreComponent.builder().name(score.get(SCORE_NAME).getAsString()) .objective(score.get(SCORE_OBJECTIVE).getAsString()); } } else if (object.has(SELECTOR)) { component = SelectorComponent.builder().pattern(object.get(SELECTOR).getAsString()); } else if (object.has(KEYBIND)) { component = KeybindComponent.builder().keybind(object.get(KEYBIND).getAsString()); } else if (object.has(NBT)) { final String nbt = object.get(NBT).getAsString(); final boolean interpret = object.has(NBT_INTERPRET) && object.getAsJsonPrimitive(NBT_INTERPRET).getAsBoolean(); if (object.has(NBT_BLOCK)) { final BlockNbtComponent.Pos position = context.deserialize(object.get(NBT_BLOCK), BlockNbtComponent.Pos.class); component = BlockNbtComponent.builder().nbtPath(nbt).interpret(interpret).pos(position); } else if (object.has(NBT_ENTITY)) { component = EntityNbtComponent.builder().nbtPath(nbt).interpret(interpret) .selector(object.get(NBT_ENTITY).getAsString()); } else { throw notSureHowToDeserialize(element); } } else { throw notSureHowToDeserialize(element); } if (object.has(EXTRA)) { final JsonArray extra = object.getAsJsonArray(EXTRA); for (int i = 0, size = extra.size(); i < size; i++) { final JsonElement extraElement = extra.get(i); component.append(this.deserialize0(extraElement, context)); } } final Style style = context.deserialize(element, Style.class); if (!style.isEmpty()) { component.style(style); } return component.build(); } // Not part of the API. @Deprecated @Override public JsonElement serialize(final Component src, final Type typeOfSrc, final JsonSerializationContext context) { final JsonObject object = new JsonObject(); if (src instanceof TextComponent) { object.addProperty(TEXT, ((TextComponent) src).content()); } else if (src instanceof TranslatableComponent) { final TranslatableComponent tc = (TranslatableComponent) src; object.addProperty(TRANSLATE, tc.key()); if (!tc.args().isEmpty()) { final JsonArray with = new JsonArray(); for (final Component arg : tc.args()) { with.add(context.serialize(arg)); } object.add(TRANSLATE_WITH, with); } } else if (src instanceof ScoreComponent) { final ScoreComponent sc = (ScoreComponent) src; final JsonObject score = new JsonObject(); score.addProperty(SCORE_NAME, sc.name()); score.addProperty(SCORE_OBJECTIVE, sc.objective()); // score component value is optional if (sc.value() != null) score.addProperty(SCORE_VALUE, sc.value()); object.add(SCORE, score); } else if (src instanceof SelectorComponent) { object.addProperty(SELECTOR, ((SelectorComponent) src).pattern()); } else if (src instanceof KeybindComponent) { object.addProperty(KEYBIND, ((KeybindComponent) src).keybind()); } else if (src instanceof NbtComponent) { final NbtComponent<?, ?> nc = (NbtComponent<?, ?>) src; object.addProperty(NBT, nc.nbtPath()); object.addProperty(NBT_INTERPRET, nc.interpret()); if (src instanceof BlockNbtComponent) { final JsonElement position = context.serialize(((BlockNbtComponent) nc).pos()); object.add(NBT_BLOCK, position); } else if (src instanceof EntityNbtComponent) { object.addProperty(NBT_ENTITY, ((EntityNbtComponent) nc).selector()); } else { throw notSureHowToSerialize(src); } } else { throw notSureHowToSerialize(src); } if (!src.children().isEmpty()) { final JsonArray extra = new JsonArray(); for (final Component child : src.children()) { extra.add(context.serialize(child)); } object.add(EXTRA, extra); } if (src.hasStyling()) { final JsonElement style = context.serialize(src.style()); if (style.isJsonObject()) { for (final Map.Entry<String, JsonElement> entry : ((JsonObject) style).entrySet()) { object.add(entry.getKey(), entry.getValue()); } } } return object; } private static JsonParseException notSureHowToDeserialize(final JsonElement element) { return new JsonParseException("Don't know how to turn " + element + " into a Component"); } private static IllegalArgumentException notSureHowToSerialize(final Component component) { return new IllegalArgumentException("Don't know how to serialize " + component + " as a Component"); } }