diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index ea183cdb..4490df11 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -20,38 +20,10 @@ public abstract class BaseComponent BaseComponent parent; /** - * The color of this component and any child components (unless overridden) + * The component's style. */ - private ChatColor color; - /** - * The font of this component and any child components (unless overridden) - */ - private String font; - /** - * Whether this component and any child components (unless overridden) is - * bold - */ - private Boolean bold; - /** - * Whether this component and any child components (unless overridden) is - * italic - */ - private Boolean italic; - /** - * Whether this component and any child components (unless overridden) is - * underlined - */ - private Boolean underlined; - /** - * Whether this component and any child components (unless overridden) is - * strikethrough - */ - private Boolean strikethrough; - /** - * Whether this component and any child components (unless overridden) is - * obfuscated - */ - private Boolean obfuscated; + @Getter + private ComponentStyle style = new ComponentStyle(); /** * The text to insert into the chat when this component (and child * components) are clicked while pressing the shift key @@ -153,31 +125,31 @@ public abstract class BaseComponent } if ( retention == FormatRetention.FORMATTING || retention == FormatRetention.ALL ) { - if ( replace || color == null ) + if ( replace || !style.hasColor() ) { setColor( component.getColorRaw() ); } - if ( replace || font == null ) + if ( replace || !style.hasFont() ) { setFont( component.getFontRaw() ); } - if ( replace || bold == null ) + if ( replace || style.isBoldRaw() == null ) { setBold( component.isBoldRaw() ); } - if ( replace || italic == null ) + if ( replace || style.isItalicRaw() == null ) { setItalic( component.isItalicRaw() ); } - if ( replace || underlined == null ) + if ( replace || style.isUnderlinedRaw() == null ) { setUnderlined( component.isUnderlinedRaw() ); } - if ( replace || strikethrough == null ) + if ( replace || style.isStrikethroughRaw() == null ) { setStrikethrough( component.isStrikethroughRaw() ); } - if ( replace || obfuscated == null ) + if ( replace || style.isObfuscatedRaw() == null ) { setObfuscated( component.isObfuscatedRaw() ); } @@ -266,6 +238,29 @@ public abstract class BaseComponent return builder.toString(); } + /** + * Set the {@link ComponentStyle} for this component. + *

+ * Unlike {@link #applyStyle(ComponentStyle)}, this method will overwrite + * all style values on this component. + * + * @param style the style to set, or null to set all style values to default + */ + public void setStyle(ComponentStyle style) + { + this.style = ( style != null ) ? style.clone() : new ComponentStyle(); + } + + /** + * Set this component's color. + * + * @param color the component color, or null to use the default + */ + public void setColor(ChatColor color) + { + this.style.setColor( color ); + } + /** * Returns the color of this component. This uses the parent's color if this * component doesn't have one. {@link net.md_5.bungee.api.ChatColor#WHITE} @@ -275,7 +270,7 @@ public abstract class BaseComponent */ public ChatColor getColor() { - if ( color == null ) + if ( !style.hasColor() ) { if ( parent == null ) { @@ -283,7 +278,7 @@ public abstract class BaseComponent } return parent.getColor(); } - return color; + return style.getColor(); } /** @@ -294,7 +289,17 @@ public abstract class BaseComponent */ public ChatColor getColorRaw() { - return color; + return style.getColor(); + } + + /** + * Set this component's font. + * + * @param font the font to set, or null to use the default + */ + public void setFont(String font) + { + this.style.setFont( font ); } /** @@ -305,7 +310,7 @@ public abstract class BaseComponent */ public String getFont() { - if ( font == null ) + if ( !style.hasFont() ) { if ( parent == null ) { @@ -313,7 +318,7 @@ public abstract class BaseComponent } return parent.getFont(); } - return font; + return style.getFont(); } /** @@ -324,7 +329,17 @@ public abstract class BaseComponent */ public String getFontRaw() { - return font; + return style.getFont(); + } + + /** + * Set whether or not this component is bold. + * + * @param bold the new bold state, or null to use the default + */ + public void setBold(Boolean bold) + { + this.style.setBold( bold ); } /** @@ -336,11 +351,11 @@ public abstract class BaseComponent */ public boolean isBold() { - if ( bold == null ) + if ( style.isBoldRaw() == null ) { return parent != null && parent.isBold(); } - return bold; + return style.isBold(); } /** @@ -351,7 +366,17 @@ public abstract class BaseComponent */ public Boolean isBoldRaw() { - return bold; + return style.isBoldRaw(); + } + + /** + * Set whether or not this component is italic. + * + * @param italic the new italic state, or null to use the default + */ + public void setItalic(Boolean italic) + { + this.style.setItalic( italic ); } /** @@ -363,11 +388,11 @@ public abstract class BaseComponent */ public boolean isItalic() { - if ( italic == null ) + if ( style.isItalicRaw() == null ) { return parent != null && parent.isItalic(); } - return italic; + return style.isItalic(); } /** @@ -378,7 +403,17 @@ public abstract class BaseComponent */ public Boolean isItalicRaw() { - return italic; + return style.isItalicRaw(); + } + + /** + * Set whether or not this component is underlined. + * + * @param underlined the new underlined state, or null to use the default + */ + public void setUnderlined(Boolean underlined) + { + this.style.setUnderlined( underlined ); } /** @@ -390,11 +425,11 @@ public abstract class BaseComponent */ public boolean isUnderlined() { - if ( underlined == null ) + if ( style.isUnderlinedRaw() == null ) { return parent != null && parent.isUnderlined(); } - return underlined; + return style.isUnderlined(); } /** @@ -405,7 +440,18 @@ public abstract class BaseComponent */ public Boolean isUnderlinedRaw() { - return underlined; + return style.isUnderlinedRaw(); + } + + /** + * Set whether or not this component is strikethrough. + * + * @param strikethrough the new strikethrough state, or null to use the + * default + */ + public void setStrikethrough(Boolean strikethrough) + { + this.style.setStrikethrough( strikethrough ); } /** @@ -417,11 +463,11 @@ public abstract class BaseComponent */ public boolean isStrikethrough() { - if ( strikethrough == null ) + if ( style.isStrikethroughRaw() == null ) { return parent != null && parent.isStrikethrough(); } - return strikethrough; + return style.isStrikethrough(); } /** @@ -432,7 +478,17 @@ public abstract class BaseComponent */ public Boolean isStrikethroughRaw() { - return strikethrough; + return style.isStrikethroughRaw(); + } + + /** + * Set whether or not this component is obfuscated. + * + * @param obfuscated the new obfuscated state, or null to use the default + */ + public void setObfuscated(Boolean obfuscated) + { + this.style.setObfuscated( obfuscated ); } /** @@ -444,11 +500,11 @@ public abstract class BaseComponent */ public boolean isObfuscated() { - if ( obfuscated == null ) + if ( style.isObfuscatedRaw() == null ) { return parent != null && parent.isObfuscated(); } - return obfuscated; + return style.isObfuscated(); } /** @@ -459,7 +515,48 @@ public abstract class BaseComponent */ public Boolean isObfuscatedRaw() { - return obfuscated; + return style.isObfuscatedRaw(); + } + + /** + * Apply the style from the given {@link ComponentStyle} to this component. + *

+ * Any style values that have been explicitly set in the style will be + * applied to this component. If a value is not set in the style, it will + * not override the style set in this component. + * + * @param style the style to apply + */ + public void applyStyle(ComponentStyle style) + { + if ( style.hasColor() ) + { + setColor( style.getColor() ); + } + if ( style.hasFont() ) + { + setFont( style.getFont() ); + } + if ( style.isBoldRaw() != null ) + { + setBold( style.isBoldRaw() ); + } + if ( style.isItalicRaw() != null ) + { + setItalic( style.isItalicRaw() ); + } + if ( style.isUnderlinedRaw() != null ) + { + setUnderlined( style.isUnderlinedRaw() ); + } + if ( style.isStrikethroughRaw() != null ) + { + setStrikethrough( style.isStrikethroughRaw() ); + } + if ( style.isObfuscatedRaw() != null ) + { + setObfuscated( style.isObfuscatedRaw() ); + } } public void setExtra(List components) @@ -498,6 +595,16 @@ public abstract class BaseComponent extra.add( component ); } + /** + * Returns whether the component has any styling applied to it. + * + * @return Whether any styling is applied + */ + public boolean hasStyle() + { + return !style.isEmpty(); + } + /** * Returns whether the component has any formatting or events applied to it * @@ -505,10 +612,8 @@ public abstract class BaseComponent */ public boolean hasFormatting() { - return color != null || font != null || bold != null - || italic != null || underlined != null - || strikethrough != null || obfuscated != null - || insertion != null || hoverEvent != null || clickEvent != null; + return hasStyle() || insertion != null + || hoverEvent != null || clickEvent != null; } /** diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java index 537a0662..9ddb92e3 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java @@ -423,6 +423,18 @@ public final class ComponentBuilder return this; } + /** + * Applies the provided {@link ComponentStyle} to the current part. + * + * @param style the style to apply + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder style(ComponentStyle style) + { + getCurrentComponent().applyStyle( style ); + return this; + } + /** * Sets the insertion text for the current part. * diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java new file mode 100644 index 00000000..c19d92fa --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java @@ -0,0 +1,234 @@ +package net.md_5.bungee.api.chat; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.Setter; +import net.md_5.bungee.api.ChatColor; + +/** + * Represents a style that may be applied to a {@link BaseComponent}. + */ +@Setter +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode +public final class ComponentStyle implements Cloneable +{ + + /** + * The color of this style. + */ + private ChatColor color; + /** + * The font of this style. + */ + private String font; + /** + * Whether this style is bold. + */ + private Boolean bold; + /** + * Whether this style is italic. + */ + private Boolean italic; + /** + * Whether this style is underlined. + */ + private Boolean underlined; + /** + * Whether this style is strikethrough. + */ + private Boolean strikethrough; + /** + * Whether this style is obfuscated. + */ + private Boolean obfuscated; + + /** + * Returns the color of this style. May return null. + * + * @return the color of this style, or null if default color + */ + public ChatColor getColor() + { + return color; + } + + /** + * Returns whether or not this style has a color set. + * + * @return whether a color is set + */ + public boolean hasColor() + { + return ( color != null ); + } + + /** + * Returns the font of this style. May return null. + * + * @return the font of this style, or null if default font + */ + public String getFont() + { + return font; + } + + /** + * Returns whether or not this style has a font set. + * + * @return whether a font is set + */ + public boolean hasFont() + { + return ( font != null ); + } + + /** + * Returns whether this style is bold. + * + * @return whether the style is bold + */ + public boolean isBold() + { + return ( bold != null ) && bold.booleanValue(); + } + + /** + * Returns whether this style is bold. May return null. + * + * @return whether the style is bold, or null if not set + */ + public Boolean isBoldRaw() + { + return bold; + } + + /** + * Returns whether this style is italic. May return null. + * + * @return whether the style is italic + */ + public boolean isItalic() + { + return ( italic != null ) && italic.booleanValue(); + } + + /** + * Returns whether this style is italic. May return null. + * + * @return whether the style is italic, or null if not set + */ + public Boolean isItalicRaw() + { + return italic; + } + + /** + * Returns whether this style is underlined. + * + * @return whether the style is underlined + */ + public boolean isUnderlined() + { + return ( underlined != null ) && underlined.booleanValue(); + } + + /** + * Returns whether this style is underlined. May return null. + * + * @return whether the style is underlined, or null if not set + */ + public Boolean isUnderlinedRaw() + { + return underlined; + } + + /** + * Returns whether this style is strikethrough + * + * @return whether the style is strikethrough + */ + public boolean isStrikethrough() + { + return ( strikethrough != null ) && strikethrough.booleanValue(); + } + + /** + * Returns whether this style is strikethrough. May return null. + * + * @return whether the style is strikethrough, or null if not set + */ + public Boolean isStrikethroughRaw() + { + return strikethrough; + } + + /** + * Returns whether this style is obfuscated. + * + * @return whether the style is obfuscated + */ + public boolean isObfuscated() + { + return ( obfuscated != null ) && obfuscated.booleanValue(); + } + + /** + * Returns whether this style is obfuscated. May return null. + * + * @return whether the style is obfuscated, or null if not set + */ + public Boolean isObfuscatedRaw() + { + return obfuscated; + } + + /** + * Returns whether this style has any formatting explicitly set. + * + * @return true if at least one value is set, false if none are set + */ + public boolean isEmpty() + { + return color != null || font != null || bold != null + || italic != null || underlined != null + || strikethrough != null || obfuscated != null; + } + + @Override + public ComponentStyle clone() + { + return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated ); + } + + /** + * Get a new {@link ComponentStyleBuilder}. + * + * @return the builder + */ + public static ComponentStyleBuilder builder() + { + return new ComponentStyleBuilder(); + } + + /** + * Get a new {@link ComponentStyleBuilder} with values initialized to the + * style values of the supplied {@link ComponentStyle}. + * + * @param other the component style whose values to copy into the builder + * @return the builder + */ + public static ComponentStyleBuilder builder(ComponentStyle other) + { + return new ComponentStyleBuilder() + .color( other.color ) + .font( other.font ) + .bold( other.bold ) + .italic( other.italic ) + .underlined( other.underlined ) + .strikethrough( other.strikethrough ) + .obfuscated( other.obfuscated ); + } +} diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyleBuilder.java b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyleBuilder.java new file mode 100644 index 00000000..6481ae81 --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyleBuilder.java @@ -0,0 +1,126 @@ +package net.md_5.bungee.api.chat; + +import net.md_5.bungee.api.ChatColor; + +/** + *

+ * ComponentStyleBuilder simplifies creating component styles by allowing the + * use of a chainable builder. + *

+ *
+ * ComponentStyle style = ComponentStyle.builder()
+ *     .color(ChatColor.RED)
+ *     .font("custom:font")
+ *     .bold(true).italic(true).create();
+ *
+ * BaseComponent component = new ComponentBuilder("Hello world").style(style).create();
+ * // Or it can be used directly on a component
+ * TextComponent text = new TextComponent("Hello world");
+ * text.applyStyle(style);
+ * 
+ * + * @see ComponentStyle#builder() + * @see ComponentStyle#builder(ComponentStyle) + */ +public final class ComponentStyleBuilder +{ + + private ChatColor color; + private String font; + private Boolean bold, italic, underlined, strikethrough, obfuscated; + + /** + * Set the style color. + * + * @param color the color to set, or null to use the default + * @return this ComponentStyleBuilder for chaining + */ + public ComponentStyleBuilder color(ChatColor color) + { + this.color = color; + return this; + } + + /** + * Set the style font. + * + * @param font the font key to set, or null to use the default + * @return this ComponentStyleBuilder for chaining + */ + public ComponentStyleBuilder font(String font) + { + this.font = font; + return this; + } + + /** + * Set the style's bold property. + * + * @param bold the bold value to set, or null to use the default + * @return this ComponentStyleBuilder for chaining + */ + public ComponentStyleBuilder bold(Boolean bold) + { + this.bold = bold; + return this; + } + + /** + * Set the style's italic property. + * + * @param italic the italic value to set, or null to use the default + * @return this ComponentStyleBuilder for chaining + */ + public ComponentStyleBuilder italic(Boolean italic) + { + this.italic = italic; + return this; + } + + /** + * Set the style's underlined property. + * + * @param underlined the underlined value to set, or null to use the default + * @return this ComponentStyleBuilder for chaining + */ + public ComponentStyleBuilder underlined(Boolean underlined) + { + this.underlined = underlined; + return this; + } + + /** + * Set the style's strikethrough property. + * + * @param strikethrough the strikethrough value to set, or null to use the + * default + * @return this ComponentStyleBuilder for chaining + */ + public ComponentStyleBuilder strikethrough(Boolean strikethrough) + { + this.strikethrough = strikethrough; + return this; + } + + /** + * Set the style's obfuscated property. + * + * @param obfuscated the obfuscated value to set, or null to use the default + * @return this ComponentStyleBuilder for chaining + */ + public ComponentStyleBuilder obfuscated(Boolean obfuscated) + { + this.obfuscated = obfuscated; + return this; + } + + /** + * Build the {@link ComponentStyle} using the values set in this builder. + * + * @return the created ComponentStyle + */ + public ComponentStyle build() + { + return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated ); + } +} diff --git a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java index b7e85f1f..86bb0cb5 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -4,72 +4,25 @@ import com.google.common.base.Preconditions; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Locale; -import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentStyle; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.hover.content.Content; public class BaseComponentSerializer { - private static boolean getAsBoolean(JsonElement el) - { - if ( el.isJsonPrimitive() ) - { - JsonPrimitive primitive = (JsonPrimitive) el; - - if ( primitive.isBoolean() ) - { - return primitive.getAsBoolean(); - } - - if ( primitive.isNumber() ) - { - Number number = primitive.getAsNumber(); - if ( number instanceof Byte ) - { - return number.byteValue() != 0; - } - } - } - - return false; - } - protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context) { - if ( object.has( "bold" ) ) - { - component.setBold( getAsBoolean( object.get( "bold" ) ) ); - } - if ( object.has( "italic" ) ) - { - component.setItalic( getAsBoolean( object.get( "italic" ) ) ); - } - if ( object.has( "underlined" ) ) - { - component.setUnderlined( getAsBoolean( object.get( "underlined" ) ) ); - } - if ( object.has( "strikethrough" ) ) - { - component.setStrikethrough( getAsBoolean( object.get( "strikethrough" ) ) ); - } - if ( object.has( "obfuscated" ) ) - { - component.setObfuscated( getAsBoolean( object.get( "obfuscated" ) ) ); - } - if ( object.has( "color" ) ) - { - component.setColor( ChatColor.of( object.get( "color" ).getAsString() ) ); - } + component.applyStyle( context.deserialize( object, ComponentStyle.class ) ); + if ( object.has( "insertion" ) ) { component.setInsertion( object.get( "insertion" ).getAsString() ); @@ -131,10 +84,6 @@ public class BaseComponentSerializer } } - if ( object.has( "font" ) ) - { - component.setFont( object.get( "font" ).getAsString() ); - } if ( object.has( "extra" ) ) { component.setExtra( Arrays.asList( context.deserialize( object.get( "extra" ), BaseComponent[].class ) ) ); @@ -153,30 +102,9 @@ public class BaseComponentSerializer { Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" ); ComponentSerializer.serializedComponents.get().add( component ); - if ( component.isBoldRaw() != null ) - { - object.addProperty( "bold", component.isBoldRaw() ); - } - if ( component.isItalicRaw() != null ) - { - object.addProperty( "italic", component.isItalicRaw() ); - } - if ( component.isUnderlinedRaw() != null ) - { - object.addProperty( "underlined", component.isUnderlinedRaw() ); - } - if ( component.isStrikethroughRaw() != null ) - { - object.addProperty( "strikethrough", component.isStrikethroughRaw() ); - } - if ( component.isObfuscatedRaw() != null ) - { - object.addProperty( "obfuscated", component.isObfuscatedRaw() ); - } - if ( component.getColorRaw() != null ) - { - object.addProperty( "color", component.getColorRaw().getName() ); - } + + ComponentStyleSerializer.serializeTo( component.getStyle(), object ); + if ( component.getInsertion() != null ) { object.addProperty( "insertion", component.getInsertion() ); @@ -205,10 +133,6 @@ public class BaseComponentSerializer object.add( "hoverEvent", hoverEvent ); } - if ( component.getFontRaw() != null ) - { - object.addProperty( "font", component.getFontRaw() ); - } if ( component.getExtra() != null ) { object.add( "extra", context.serialize( component.getExtra() ) ); diff --git a/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java index d4de81c3..1164804f 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java @@ -13,6 +13,7 @@ import com.google.gson.JsonPrimitive; import java.lang.reflect.Type; import java.util.Set; import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ComponentStyle; import net.md_5.bungee.api.chat.ItemTag; import net.md_5.bungee.api.chat.KeybindComponent; import net.md_5.bungee.api.chat.ScoreComponent; @@ -36,6 +37,7 @@ public class ComponentSerializer implements JsonDeserializer registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer() ). registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() ). registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() ). + registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() ). registerTypeAdapter( Entity.class, new EntitySerializer() ). registerTypeAdapter( Text.class, new TextSerializer() ). registerTypeAdapter( Item.class, new ItemSerializer() ). @@ -118,11 +120,44 @@ public class ComponentSerializer implements JsonDeserializer return gson.fromJson( jsonElement, BaseComponent.class ); } + /** + * Deserialize a JSON-compliant String as a component style. + * + * @param json the component style json to parse + * @return the deserialized component style + * @throws IllegalArgumentException if anything other than a valid JSON + * component style string is passed as input + */ + public static ComponentStyle deserializeStyle(String json) + { + JsonElement jsonElement = JsonParser.parseString( json ); + + return deserializeStyle( jsonElement ); + } + + /** + * Deserialize a JSON element as a component style. + * + * @param jsonElement the component style json to parse + * @return the deserialized component style + * @throws IllegalArgumentException if anything other than a valid JSON + * component style is passed as input + */ + public static ComponentStyle deserializeStyle(JsonElement jsonElement) + { + return gson.fromJson( jsonElement, ComponentStyle.class ); + } + public static JsonElement toJson(BaseComponent component) { return gson.toJsonTree( component ); } + public static JsonElement toJson(ComponentStyle style) + { + return gson.toJsonTree( style ); + } + public static String toString(Object object) { return gson.toJson( object ); @@ -144,6 +179,11 @@ public class ComponentSerializer implements JsonDeserializer } } + public static String toString(ComponentStyle style) + { + return gson.toJson( style ); + } + @Override public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { diff --git a/chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java new file mode 100644 index 00000000..f3d1d026 --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java @@ -0,0 +1,118 @@ +package net.md_5.bungee.chat; + +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.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Type; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.ComponentStyle; +import net.md_5.bungee.api.chat.ComponentStyleBuilder; + +public class ComponentStyleSerializer implements JsonSerializer, JsonDeserializer +{ + + private static boolean getAsBoolean(JsonElement el) + { + if ( el.isJsonPrimitive() ) + { + JsonPrimitive primitive = (JsonPrimitive) el; + + if ( primitive.isBoolean() ) + { + return primitive.getAsBoolean(); + } + + if ( primitive.isNumber() ) + { + Number number = primitive.getAsNumber(); + if ( number instanceof Byte ) + { + return number.byteValue() != 0; + } + } + } + + return false; + } + + static void serializeTo(ComponentStyle style, JsonObject object) + { + if ( style.isBoldRaw() != null ) + { + object.addProperty( "bold", style.isBoldRaw() ); + } + if ( style.isItalicRaw() != null ) + { + object.addProperty( "italic", style.isItalicRaw() ); + } + if ( style.isUnderlinedRaw() != null ) + { + object.addProperty( "underlined", style.isUnderlinedRaw() ); + } + if ( style.isStrikethroughRaw() != null ) + { + object.addProperty( "strikethrough", style.isStrikethroughRaw() ); + } + if ( style.isObfuscatedRaw() != null ) + { + object.addProperty( "obfuscated", style.isObfuscatedRaw() ); + } + if ( style.hasColor() ) + { + object.addProperty( "color", style.getColor().getName() ); + } + if ( style.hasFont() ) + { + object.addProperty( "font", style.getFont() ); + } + } + + @Override + public ComponentStyle deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException + { + ComponentStyleBuilder builder = ComponentStyle.builder(); + JsonObject object = json.getAsJsonObject(); + if ( object.has( "bold" ) ) + { + builder.bold( getAsBoolean( object.get( "bold" ) ) ); + } + if ( object.has( "italic" ) ) + { + builder.italic( getAsBoolean( object.get( "italic" ) ) ); + } + if ( object.has( "underlined" ) ) + { + builder.underlined( getAsBoolean( object.get( "underlined" ) ) ); + } + if ( object.has( "strikethrough" ) ) + { + builder.strikethrough( getAsBoolean( object.get( "strikethrough" ) ) ); + } + if ( object.has( "obfuscated" ) ) + { + builder.obfuscated( getAsBoolean( object.get( "obfuscated" ) ) ); + } + if ( object.has( "color" ) ) + { + builder.color( ChatColor.of( object.get( "color" ).getAsString() ) ); + } + if ( object.has( "font" ) ) + { + builder.font( object.get( "font" ).getAsString() ); + } + return builder.build(); + } + + @Override + public JsonElement serialize(ComponentStyle src, Type typeOfSrc, JsonSerializationContext context) + { + JsonObject object = new JsonObject(); + serializeTo( src, object ); + return object; + } +} diff --git a/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java b/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java index b4595b32..555dd333 100644 --- a/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java +++ b/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java @@ -430,6 +430,16 @@ public class ComponentsTest assertArrayEquals( component, reparsed ); } + @Test + public void testStyle() + { + ComponentStyle style = ComponentSerializer.deserializeStyle( "{\"color\":\"red\",\"font\":\"minecraft:example\",\"bold\":true,\"italic\":false,\"obfuscated\":true}" ); + String text = ComponentSerializer.toString( style ); + ComponentStyle reparsed = ComponentSerializer.deserializeStyle( text ); + + assertEquals( style, reparsed ); + } + @Test public void testBuilderAppendCreate() {