#3569: Separate component styling into a ComponentStyle class

This commit is contained in:
Parker Hawke 2024-01-06 16:35:30 +11:00 committed by md_5
parent b23a51825e
commit 737d545fb6
No known key found for this signature in database
GPG Key ID: E8E901AC7C617C11
8 changed files with 714 additions and 145 deletions

View File

@ -20,38 +20,10 @@ public abstract class BaseComponent
BaseComponent parent; BaseComponent parent;
/** /**
* The color of this component and any child components (unless overridden) * The component's style.
*/ */
private ChatColor color; @Getter
/** private ComponentStyle style = new ComponentStyle();
* 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;
/** /**
* The text to insert into the chat when this component (and child * The text to insert into the chat when this component (and child
* components) are clicked while pressing the shift key * components) are clicked while pressing the shift key
@ -153,31 +125,31 @@ public abstract class BaseComponent
} }
if ( retention == FormatRetention.FORMATTING || retention == FormatRetention.ALL ) if ( retention == FormatRetention.FORMATTING || retention == FormatRetention.ALL )
{ {
if ( replace || color == null ) if ( replace || !style.hasColor() )
{ {
setColor( component.getColorRaw() ); setColor( component.getColorRaw() );
} }
if ( replace || font == null ) if ( replace || !style.hasFont() )
{ {
setFont( component.getFontRaw() ); setFont( component.getFontRaw() );
} }
if ( replace || bold == null ) if ( replace || style.isBoldRaw() == null )
{ {
setBold( component.isBoldRaw() ); setBold( component.isBoldRaw() );
} }
if ( replace || italic == null ) if ( replace || style.isItalicRaw() == null )
{ {
setItalic( component.isItalicRaw() ); setItalic( component.isItalicRaw() );
} }
if ( replace || underlined == null ) if ( replace || style.isUnderlinedRaw() == null )
{ {
setUnderlined( component.isUnderlinedRaw() ); setUnderlined( component.isUnderlinedRaw() );
} }
if ( replace || strikethrough == null ) if ( replace || style.isStrikethroughRaw() == null )
{ {
setStrikethrough( component.isStrikethroughRaw() ); setStrikethrough( component.isStrikethroughRaw() );
} }
if ( replace || obfuscated == null ) if ( replace || style.isObfuscatedRaw() == null )
{ {
setObfuscated( component.isObfuscatedRaw() ); setObfuscated( component.isObfuscatedRaw() );
} }
@ -266,6 +238,29 @@ public abstract class BaseComponent
return builder.toString(); return builder.toString();
} }
/**
* Set the {@link ComponentStyle} for this component.
* <p>
* 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 * 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} * component doesn't have one. {@link net.md_5.bungee.api.ChatColor#WHITE}
@ -275,7 +270,7 @@ public abstract class BaseComponent
*/ */
public ChatColor getColor() public ChatColor getColor()
{ {
if ( color == null ) if ( !style.hasColor() )
{ {
if ( parent == null ) if ( parent == null )
{ {
@ -283,7 +278,7 @@ public abstract class BaseComponent
} }
return parent.getColor(); return parent.getColor();
} }
return color; return style.getColor();
} }
/** /**
@ -294,7 +289,17 @@ public abstract class BaseComponent
*/ */
public ChatColor getColorRaw() 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() public String getFont()
{ {
if ( font == null ) if ( !style.hasFont() )
{ {
if ( parent == null ) if ( parent == null )
{ {
@ -313,7 +318,7 @@ public abstract class BaseComponent
} }
return parent.getFont(); return parent.getFont();
} }
return font; return style.getFont();
} }
/** /**
@ -324,7 +329,17 @@ public abstract class BaseComponent
*/ */
public String getFontRaw() 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() public boolean isBold()
{ {
if ( bold == null ) if ( style.isBoldRaw() == null )
{ {
return parent != null && parent.isBold(); return parent != null && parent.isBold();
} }
return bold; return style.isBold();
} }
/** /**
@ -351,7 +366,17 @@ public abstract class BaseComponent
*/ */
public Boolean isBoldRaw() 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() public boolean isItalic()
{ {
if ( italic == null ) if ( style.isItalicRaw() == null )
{ {
return parent != null && parent.isItalic(); return parent != null && parent.isItalic();
} }
return italic; return style.isItalic();
} }
/** /**
@ -378,7 +403,17 @@ public abstract class BaseComponent
*/ */
public Boolean isItalicRaw() 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() public boolean isUnderlined()
{ {
if ( underlined == null ) if ( style.isUnderlinedRaw() == null )
{ {
return parent != null && parent.isUnderlined(); return parent != null && parent.isUnderlined();
} }
return underlined; return style.isUnderlined();
} }
/** /**
@ -405,7 +440,18 @@ public abstract class BaseComponent
*/ */
public Boolean isUnderlinedRaw() 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() public boolean isStrikethrough()
{ {
if ( strikethrough == null ) if ( style.isStrikethroughRaw() == null )
{ {
return parent != null && parent.isStrikethrough(); return parent != null && parent.isStrikethrough();
} }
return strikethrough; return style.isStrikethrough();
} }
/** /**
@ -432,7 +478,17 @@ public abstract class BaseComponent
*/ */
public Boolean isStrikethroughRaw() 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() public boolean isObfuscated()
{ {
if ( obfuscated == null ) if ( style.isObfuscatedRaw() == null )
{ {
return parent != null && parent.isObfuscated(); return parent != null && parent.isObfuscated();
} }
return obfuscated; return style.isObfuscated();
} }
/** /**
@ -459,7 +515,48 @@ public abstract class BaseComponent
*/ */
public Boolean isObfuscatedRaw() public Boolean isObfuscatedRaw()
{ {
return obfuscated; return style.isObfuscatedRaw();
}
/**
* Apply the style from the given {@link ComponentStyle} to this component.
* <p>
* 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<BaseComponent> components) public void setExtra(List<BaseComponent> components)
@ -498,6 +595,16 @@ public abstract class BaseComponent
extra.add( 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 * Returns whether the component has any formatting or events applied to it
* *
@ -505,10 +612,8 @@ public abstract class BaseComponent
*/ */
public boolean hasFormatting() public boolean hasFormatting()
{ {
return color != null || font != null || bold != null return hasStyle() || insertion != null
|| italic != null || underlined != null || hoverEvent != null || clickEvent != null;
|| strikethrough != null || obfuscated != null
|| insertion != null || hoverEvent != null || clickEvent != null;
} }
/** /**

View File

@ -423,6 +423,18 @@ public final class ComponentBuilder
return this; 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. * Sets the insertion text for the current part.
* *

View File

@ -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 );
}
}

View File

@ -0,0 +1,126 @@
package net.md_5.bungee.api.chat;
import net.md_5.bungee.api.ChatColor;
/**
* <p>
* ComponentStyleBuilder simplifies creating component styles by allowing the
* use of a chainable builder.
* </p>
* <pre>
* 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);
* </pre>
*
* @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 );
}
}

View File

@ -4,72 +4,25 @@ import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializationContext;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Locale; 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.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent; 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.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Content; import net.md_5.bungee.api.chat.hover.content.Content;
public class BaseComponentSerializer 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) protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context)
{ {
if ( object.has( "bold" ) ) component.applyStyle( context.deserialize( object, ComponentStyle.class ) );
{
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() ) );
}
if ( object.has( "insertion" ) ) if ( object.has( "insertion" ) )
{ {
component.setInsertion( object.get( "insertion" ).getAsString() ); 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" ) ) if ( object.has( "extra" ) )
{ {
component.setExtra( Arrays.asList( context.<BaseComponent[]>deserialize( object.get( "extra" ), BaseComponent[].class ) ) ); component.setExtra( Arrays.asList( context.<BaseComponent[]>deserialize( object.get( "extra" ), BaseComponent[].class ) ) );
@ -153,30 +102,9 @@ public class BaseComponentSerializer
{ {
Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" ); Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" );
ComponentSerializer.serializedComponents.get().add( component ); ComponentSerializer.serializedComponents.get().add( component );
if ( component.isBoldRaw() != null )
{ ComponentStyleSerializer.serializeTo( component.getStyle(), object );
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() );
}
if ( component.getInsertion() != null ) if ( component.getInsertion() != null )
{ {
object.addProperty( "insertion", component.getInsertion() ); object.addProperty( "insertion", component.getInsertion() );
@ -205,10 +133,6 @@ public class BaseComponentSerializer
object.add( "hoverEvent", hoverEvent ); object.add( "hoverEvent", hoverEvent );
} }
if ( component.getFontRaw() != null )
{
object.addProperty( "font", component.getFontRaw() );
}
if ( component.getExtra() != null ) if ( component.getExtra() != null )
{ {
object.add( "extra", context.serialize( component.getExtra() ) ); object.add( "extra", context.serialize( component.getExtra() ) );

View File

@ -13,6 +13,7 @@ import com.google.gson.JsonPrimitive;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Set; import java.util.Set;
import net.md_5.bungee.api.chat.BaseComponent; 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.ItemTag;
import net.md_5.bungee.api.chat.KeybindComponent; import net.md_5.bungee.api.chat.KeybindComponent;
import net.md_5.bungee.api.chat.ScoreComponent; import net.md_5.bungee.api.chat.ScoreComponent;
@ -36,6 +37,7 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer() ). registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer() ).
registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() ). registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() ).
registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() ). registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() ).
registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() ).
registerTypeAdapter( Entity.class, new EntitySerializer() ). registerTypeAdapter( Entity.class, new EntitySerializer() ).
registerTypeAdapter( Text.class, new TextSerializer() ). registerTypeAdapter( Text.class, new TextSerializer() ).
registerTypeAdapter( Item.class, new ItemSerializer() ). registerTypeAdapter( Item.class, new ItemSerializer() ).
@ -118,11 +120,44 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
return gson.fromJson( jsonElement, BaseComponent.class ); 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) public static JsonElement toJson(BaseComponent component)
{ {
return gson.toJsonTree( component ); return gson.toJsonTree( component );
} }
public static JsonElement toJson(ComponentStyle style)
{
return gson.toJsonTree( style );
}
public static String toString(Object object) public static String toString(Object object)
{ {
return gson.toJson( object ); return gson.toJson( object );
@ -144,6 +179,11 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
} }
} }
public static String toString(ComponentStyle style)
{
return gson.toJson( style );
}
@Override @Override
public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{ {

View File

@ -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<ComponentStyle>, JsonDeserializer<ComponentStyle>
{
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;
}
}

View File

@ -430,6 +430,16 @@ public class ComponentsTest
assertArrayEquals( component, reparsed ); 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 @Test
public void testBuilderAppendCreate() public void testBuilderAppendCreate()
{ {