From 7ffcabae9ed5a74806faa0c05049a859066e3fcf Mon Sep 17 00:00:00 2001 From: Parker Hawke Date: Wed, 6 Dec 2023 16:47:12 -0500 Subject: [PATCH 1/5] Separate component styling into a ComponentStyle class --- .../md_5/bungee/api/chat/BaseComponent.java | 172 ++++++++----- .../bungee/api/chat/ComponentBuilder.java | 12 + .../md_5/bungee/api/chat/ComponentStyle.java | 237 ++++++++++++++++++ 3 files changed, 358 insertions(+), 63 deletions(-) create mode 100644 chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java 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 ea183cdb5f..6fd9a68304 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 void copyFormatting(BaseComponent component, FormatRetention retention, b } 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,11 @@ public static String toPlainText(BaseComponent... components) return builder.toString(); } + 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 +252,7 @@ public static String toPlainText(BaseComponent... components) */ public ChatColor getColor() { - if ( color == null ) + if ( !style.hasColor() ) { if ( parent == null ) { @@ -283,7 +260,7 @@ public ChatColor getColor() } return parent.getColor(); } - return color; + return style.getColor(); } /** @@ -294,7 +271,12 @@ public ChatColor getColor() */ public ChatColor getColorRaw() { - return color; + return style.getColor(); + } + + public void setFont(String font) + { + this.style.setFont( font ); } /** @@ -305,7 +287,7 @@ public ChatColor getColorRaw() */ public String getFont() { - if ( font == null ) + if ( !style.hasFont() ) { if ( parent == null ) { @@ -313,7 +295,7 @@ public String getFont() } return parent.getFont(); } - return font; + return style.getFont(); } /** @@ -324,7 +306,12 @@ public String getFont() */ public String getFontRaw() { - return font; + return style.getFont(); + } + + public void setBold(Boolean bold) + { + this.style.setBold( bold ); } /** @@ -336,11 +323,11 @@ public String getFontRaw() */ public boolean isBold() { - if ( bold == null ) + if ( style.isBoldRaw() == null ) { return parent != null && parent.isBold(); } - return bold; + return style.isBold(); } /** @@ -351,7 +338,12 @@ public boolean isBold() */ public Boolean isBoldRaw() { - return bold; + return style.isBoldRaw(); + } + + public void setItalic(Boolean italic) + { + this.style.setItalic( italic ); } /** @@ -363,11 +355,11 @@ public Boolean isBoldRaw() */ public boolean isItalic() { - if ( italic == null ) + if ( style.isItalicRaw() == null ) { return parent != null && parent.isItalic(); } - return italic; + return style.isItalic(); } /** @@ -378,7 +370,12 @@ public boolean isItalic() */ public Boolean isItalicRaw() { - return italic; + return style.isItalicRaw(); + } + + public void setUnderlined(Boolean underlined) + { + this.style.setUnderlined( underlined ); } /** @@ -390,11 +387,11 @@ public Boolean isItalicRaw() */ public boolean isUnderlined() { - if ( underlined == null ) + if ( style.isUnderlinedRaw() == null ) { return parent != null && parent.isUnderlined(); } - return underlined; + return style.isUnderlined(); } /** @@ -405,7 +402,12 @@ public boolean isUnderlined() */ public Boolean isUnderlinedRaw() { - return underlined; + return style.isUnderlinedRaw(); + } + + public void setStrikethrough(Boolean strikethrough) + { + this.style.setStrikethrough( strikethrough ); } /** @@ -417,11 +419,11 @@ public Boolean isUnderlinedRaw() */ public boolean isStrikethrough() { - if ( strikethrough == null ) + if ( style.isStrikethroughRaw() == null ) { return parent != null && parent.isStrikethrough(); } - return strikethrough; + return style.isStrikethrough(); } /** @@ -432,7 +434,12 @@ public boolean isStrikethrough() */ public Boolean isStrikethroughRaw() { - return strikethrough; + return style.isStrikethroughRaw(); + } + + public void setObfuscated(Boolean obfuscated) + { + this.style.setObfuscated( obfuscated ); } /** @@ -444,11 +451,11 @@ public Boolean isStrikethroughRaw() */ public boolean isObfuscated() { - if ( obfuscated == null ) + if ( style.isObfuscatedRaw() == null ) { return parent != null && parent.isObfuscated(); } - return obfuscated; + return style.isObfuscated(); } /** @@ -459,7 +466,48 @@ public boolean isObfuscated() */ 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) @@ -505,10 +553,8 @@ public void addExtra(BaseComponent component) */ 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 !style.isEmpty() || 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 537a06620d..9ddb92e330 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 ComponentBuilder obfuscated(boolean obfuscated) 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 0000000000..1013ed6410 --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java @@ -0,0 +1,237 @@ +package net.md_5.bungee.api.chat; + +import lombok.AllArgsConstructor; +import lombok.Builder; +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 +@Builder +@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 ); + } + +} From c2cd54180e5e960c4dd212cb6c1729c4d2d44c5a Mon Sep 17 00:00:00 2001 From: Parker Hawke Date: Wed, 6 Dec 2023 19:31:54 -0500 Subject: [PATCH 2/5] Add ComponentStyleSerializer, ComponentStyleBuilder, and more --- .../md_5/bungee/api/chat/BaseComponent.java | 60 ++++++++- .../md_5/bungee/api/chat/ComponentStyle.java | 2 - .../api/chat/ComponentStyleBuilder.java | 126 ++++++++++++++++++ .../bungee/chat/BaseComponentSerializer.java | 65 +-------- .../md_5/bungee/chat/ComponentSerializer.java | 2 + .../bungee/chat/ComponentStyleSerializer.java | 94 +++++++++++++ 6 files changed, 288 insertions(+), 61 deletions(-) create mode 100644 chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyleBuilder.java create mode 100644 chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java 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 6fd9a68304..8512843a10 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 @@ -238,6 +238,24 @@ public static String toPlainText(BaseComponent... components) 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 ); @@ -274,6 +292,11 @@ public ChatColor getColorRaw() 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 ); @@ -309,6 +332,11 @@ public String getFontRaw() 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 ); @@ -341,6 +369,11 @@ public Boolean isBoldRaw() 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 ); @@ -373,6 +406,11 @@ public Boolean isItalicRaw() 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 ); @@ -405,6 +443,11 @@ public Boolean isUnderlinedRaw() 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 ); @@ -437,6 +480,11 @@ public Boolean isStrikethroughRaw() 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 ); @@ -546,6 +594,16 @@ public void addExtra(BaseComponent component) 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 * @@ -553,7 +611,7 @@ public void addExtra(BaseComponent component) */ public boolean hasFormatting() { - return !style.isEmpty() || insertion != null + return hasStyle() || insertion != null || hoverEvent != null || clickEvent != null; } 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 index 1013ed6410..b6bb98bedc 100644 --- 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 @@ -1,7 +1,6 @@ package net.md_5.bungee.api.chat; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.Setter; @@ -11,7 +10,6 @@ * Represents a style that may be applied to a {@link BaseComponent}. */ @Setter -@Builder @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode 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 0000000000..a837928727 --- /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 b213498e13..ca7486730e 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 @@ -12,16 +12,16 @@ 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) + static boolean getAsBoolean(JsonElement el) { if ( el.isJsonPrimitive() ) { @@ -47,30 +47,8 @@ private static boolean getAsBoolean(JsonElement el) 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() ); @@ -139,10 +117,6 @@ protected void deserialize(JsonObject object, BaseComponent component, JsonDeser } } - 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 ) ) ); @@ -161,30 +135,9 @@ protected void serialize(JsonObject object, BaseComponent component, JsonSeriali { 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() ); @@ -213,10 +166,6 @@ protected void serialize(JsonObject object, BaseComponent component, JsonSeriali 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 d4de81c36b..3cfeab1b70 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 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() ). 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 0000000000..f8e307184a --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java @@ -0,0 +1,94 @@ +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.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 +{ + + 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( BaseComponentSerializer.getAsBoolean( object.get( "bold" ) ) ); + } + if ( object.has( "italic" ) ) + { + builder.italic( BaseComponentSerializer.getAsBoolean( object.get( "italic" ) ) ); + } + if ( object.has( "underlined" ) ) + { + builder.underlined( BaseComponentSerializer.getAsBoolean( object.get( "underlined" ) ) ); + } + if ( object.has( "strikethrough" ) ) + { + builder.strikethrough( BaseComponentSerializer.getAsBoolean( object.get( "strikethrough" ) ) ); + } + if ( object.has( "obfuscated" ) ) + { + builder.obfuscated( BaseComponentSerializer.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; + } + +} From 2fe9d2b02abaa247e2c9bc38abf176a7ecaa11d8 Mon Sep 17 00:00:00 2001 From: Parker Hawke Date: Wed, 6 Dec 2023 20:20:34 -0500 Subject: [PATCH 3/5] Move getAsBoolean() into ComponentStyleSerializer --- .../bungee/chat/BaseComponentSerializer.java | 25 ------------- .../bungee/chat/ComponentStyleSerializer.java | 35 ++++++++++++++++--- 2 files changed, 30 insertions(+), 30 deletions(-) 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 ca7486730e..e01681d7ee 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 @@ -5,7 +5,6 @@ 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 java.util.ArrayList; import java.util.Arrays; @@ -21,30 +20,6 @@ public class BaseComponentSerializer { - 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) { component.applyStyle( context.deserialize( object, ComponentStyle.class ) ); 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 index f8e307184a..93c145d9b1 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java @@ -5,6 +5,7 @@ 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; @@ -15,6 +16,30 @@ 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 ) @@ -54,23 +79,23 @@ public ComponentStyle deserialize(JsonElement json, Type typeOfT, JsonDeserializ JsonObject object = json.getAsJsonObject(); if ( object.has( "bold" ) ) { - builder.bold( BaseComponentSerializer.getAsBoolean( object.get( "bold" ) ) ); + builder.bold( getAsBoolean( object.get( "bold" ) ) ); } if ( object.has( "italic" ) ) { - builder.italic( BaseComponentSerializer.getAsBoolean( object.get( "italic" ) ) ); + builder.italic( getAsBoolean( object.get( "italic" ) ) ); } if ( object.has( "underlined" ) ) { - builder.underlined( BaseComponentSerializer.getAsBoolean( object.get( "underlined" ) ) ); + builder.underlined( getAsBoolean( object.get( "underlined" ) ) ); } if ( object.has( "strikethrough" ) ) { - builder.strikethrough( BaseComponentSerializer.getAsBoolean( object.get( "strikethrough" ) ) ); + builder.strikethrough( getAsBoolean( object.get( "strikethrough" ) ) ); } if ( object.has( "obfuscated" ) ) { - builder.obfuscated( BaseComponentSerializer.getAsBoolean( object.get( "obfuscated" ) ) ); + builder.obfuscated( getAsBoolean( object.get( "obfuscated" ) ) ); } if ( object.has( "color" ) ) { From 2035f8f9db46987e304c4e4a5839cc4670d6fbd2 Mon Sep 17 00:00:00 2001 From: Parker Hawke Date: Tue, 12 Dec 2023 10:14:43 -0500 Subject: [PATCH 4/5] Add methods to ComponentSerializer to parse styles, and test it --- .../md_5/bungee/chat/ComponentSerializer.java | 38 +++++++++++++++++++ .../md_5/bungee/api/chat/ComponentsTest.java | 10 +++++ 2 files changed, 48 insertions(+) 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 3cfeab1b70..1164804f7c 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 @@ -120,11 +120,44 @@ public static BaseComponent deserialize(JsonElement jsonElement) 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 ); @@ -146,6 +179,11 @@ public static String toString(BaseComponent... components) } } + 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/test/java/net/md_5/bungee/api/chat/ComponentsTest.java b/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java index 5319ecf31c..f9239ab2c7 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 @@ -421,6 +421,16 @@ public void testScore() 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() { From f3d995cce7288a8504e1693718beb664f2a54ecd Mon Sep 17 00:00:00 2001 From: Parker Hawke Date: Wed, 27 Dec 2023 15:28:07 -0500 Subject: [PATCH 5/5] Remove unused imports as a result of previous merge --- .../main/java/net/md_5/bungee/chat/BaseComponentSerializer.java | 2 -- 1 file changed, 2 deletions(-) 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 c87f9697b5..86bb0cb5a1 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,8 +4,6 @@ import com.google.gson.JsonDeserializationContext; 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 java.util.ArrayList; import java.util.Arrays;