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 bf990f1b..08325a01 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 @@ -454,9 +454,32 @@ public final class ComponentBuilder return this; } + /** + * Returns the component built by this builder. If this builder is + * empty, an empty text component will be returned. + * + * @return the component + */ + public BaseComponent build() + { + TextComponent base = new TextComponent(); + if ( !parts.isEmpty() ) + { + List cloned = new ArrayList<>( parts ); + cloned.replaceAll( BaseComponent::duplicate ); + base.setExtra( cloned ); + } + return base; + } + /** * Returns the components needed to display the message created by this * builder.git + *

+ * NOTE: {@link #build()} is preferred as it will + * consolidate all components into a single BaseComponent with extra + * contents as opposed to an array of components which is non-standard + * and may result in unexpected behavior. * * @return the created components */ diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/Text.java b/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/Text.java index 58a343aa..214558f7 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/Text.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/Text.java @@ -23,6 +23,12 @@ public class Text extends Content this.value = value; } + public Text(BaseComponent value) + { + // For legacy serialization reasons, this has to be an array of components + this( new BaseComponent[]{value} ); + } + public Text(String value) { this.value = value; 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 f1a38151..8b2dac9f 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 @@ -27,7 +27,6 @@ import net.md_5.bungee.api.chat.hover.content.TextSerializer; public class ComponentSerializer implements JsonDeserializer { - private static final JsonParser JSON_PARSER = new JsonParser(); private static final Gson gson = new GsonBuilder(). registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ). registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ). @@ -43,9 +42,25 @@ public class ComponentSerializer implements JsonDeserializer public static final ThreadLocal> serializedComponents = new ThreadLocal>(); + /** + * Parse a JSON-compliant String as an array of base components. The input + * can be one of either an array of components, or a single component object. + * If the input is an array, each component will be parsed individually and + * returned in the order that they were parsed. If the input is a single + * component object, a single-valued array with the component will be returned. + *

+ * NOTE: {@link #deserialize(String)} is preferred as it will + * parse only one component as opposed to an array of components which is non- + * standard behavior. This method is still appropriate for parsing multiple + * components at once, although such use case is rarely (if at all) exhibited + * in vanilla Minecraft. + * + * @param json the component json to parse + * @return an array of all parsed components + */ public static BaseComponent[] parse(String json) { - JsonElement jsonElement = JSON_PARSER.parse( json ); + JsonElement jsonElement = JsonParser.parseString( json ); if ( jsonElement.isJsonArray() ) { @@ -59,6 +74,26 @@ public class ComponentSerializer implements JsonDeserializer } } + /** + * Deserialize a JSON-compliant String as a single component. The input is + * expected to be a JSON object that represents only one component. + * + * @param json the component json to parse + * @return the deserialized component + * @throws IllegalArgumentException if anything other than a JSON object is + * passed as input + */ + public static BaseComponent deserialize(String json) + { + JsonElement jsonElement = JsonParser.parseString( json ); + if ( !jsonElement.isJsonObject() ) + { + throw new IllegalArgumentException( "Malformatted JSON. Expected object, got array for input \"" + json + "\"." ); + } + + return gson.fromJson( jsonElement, BaseComponent.class ); + } + public static String toString(Object object) { return gson.toJson( 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 9884f29b..95dd1620 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 @@ -1,6 +1,11 @@ package net.md_5.bungee.api.chat; import java.awt.Color; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.ObjIntConsumer; +import java.util.function.Supplier; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.hover.content.Item; import net.md_5.bungee.api.chat.hover.content.Text; @@ -18,10 +23,24 @@ public class ComponentsTest Assert.assertEquals( TextComponent.toLegacyText( parsed ), TextComponent.toLegacyText( components ) ); } - public static void testDissembleReassemble(String json) + public static void testDissembleReassemble(BaseComponent component) { + String json = ComponentSerializer.toString( component ); BaseComponent[] parsed = ComponentSerializer.parse( json ); - Assert.assertEquals( json, ComponentSerializer.toString( parsed ) ); + Assert.assertEquals( TextComponent.toLegacyText( parsed ), TextComponent.toLegacyText( component ) ); + } + + public static void testAssembleDissemble(String json, boolean modern) + { + if ( modern ) + { + BaseComponent deserialized = ComponentSerializer.deserialize( json ); + Assert.assertEquals( json, ComponentSerializer.toString( deserialized ) ); + } else + { + BaseComponent[] parsed = ComponentSerializer.parse( json ); + Assert.assertEquals( json, ComponentSerializer.toString( parsed ) ); + } } @Test @@ -41,8 +60,10 @@ public class ComponentsTest { textComponent } ); + testDissembleReassemble( textComponent ); json = "{\"hoverEvent\":{\"action\":\"show_item\",\"value\":[{\"text\":\"{id:\\\"minecraft:netherrack\\\",Count:47b}\"}]},\"text\":\"Test\"}"; - testDissembleReassemble( json ); + testAssembleDissemble( json, false ); + testAssembleDissemble( json, true ); ////////// String hoverVal = "{\"text\":\"{id:\\\"minecraft:dirt\\\",Count:1b}\"}"; json = "{\"extra\":[{\"text\":\"[\"},{\"extra\":[{\"translate\":\"block.minecraft.dirt\"}],\"text\":\"\"},{\"text\":\"]\"}],\"hoverEvent\":{\"action\":\"show_item\",\"value\":[" + hoverVal + "]},\"text\":\"\"}"; @@ -66,18 +87,37 @@ public class ComponentsTest } @Test - public void testEmptyComponentBuilder() + public void testEmptyComponentBuilderCreate() + { + this.testEmptyComponentBuilder( + ComponentBuilder::create, + (components) -> Assert.assertEquals( components.length, 0 ), + (components, size) -> Assert.assertEquals( size, components.length ) + ); + } + + @Test + public void testEmptyComponentBuilderBuild() + { + this.testEmptyComponentBuilder( + ComponentBuilder::build, + (component) -> Assert.assertNull( component.getExtra() ), + (component, size) -> Assert.assertEquals( component.getExtra().size(), size ) + ); + } + + private void testEmptyComponentBuilder(Function componentBuilder, Consumer emptyAssertion, ObjIntConsumer sizedAssertion) { ComponentBuilder builder = new ComponentBuilder(); - BaseComponent[] parts = builder.create(); - Assert.assertEquals( parts.length, 0 ); + T component = componentBuilder.apply( builder ); + emptyAssertion.accept( component ); for ( int i = 0; i < 3; i++ ) { builder.append( "part:" + i ); - parts = builder.create(); - Assert.assertEquals( parts.length, i + 1 ); + component = componentBuilder.apply( builder ); + sizedAssertion.accept( component, i + 1 ); } } @@ -213,7 +253,7 @@ public class ComponentsTest } @Test - public void testHoverEventContents() + public void testHoverEventContentsCreate() { // First do the text using the newer contents system HoverEvent hoverEvent = new HoverEvent( @@ -222,21 +262,53 @@ public class ComponentsTest new Text( new ComponentBuilder( "Second" ).create() ) ); + this.testHoverEventContents( + hoverEvent, + ComponentSerializer::parse, + (components) -> components[0].getHoverEvent(), + ComponentsTest::testDissembleReassemble // BaseComponent + ); + + // check the test still works with the value method + hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Sample text" ).create() ); + TextComponent component = new TextComponent( "Sample text" ); + component.setHoverEvent( hoverEvent ); + + Assert.assertEquals( hoverEvent.getContents().size(), 1 ); + Assert.assertTrue( hoverEvent.isLegacy() ); + String serialized = ComponentSerializer.toString( component ); + BaseComponent[] deserialized = ComponentSerializer.parse( serialized ); + Assert.assertEquals( component.getHoverEvent(), deserialized[0].getHoverEvent() ); + } + + @Test + public void testHoverEventContentsBuild() + { + // First do the text using the newer contents system + HoverEvent hoverEvent = new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new Text( new ComponentBuilder( "First" ).build() ), + new Text( new ComponentBuilder( "Second" ).build() ) + ); + + this.testHoverEventContents( + hoverEvent, + ComponentSerializer::deserialize, + BaseComponent::getHoverEvent, + ComponentsTest::testDissembleReassemble // BaseComponent + ); + } + + private void testHoverEventContents(HoverEvent hoverEvent, Function deserializer, Function hoverEventGetter, Consumer dissembleReassembleTest) + { TextComponent component = new TextComponent( "Sample text" ); component.setHoverEvent( hoverEvent ); Assert.assertEquals( hoverEvent.getContents().size(), 2 ); Assert.assertFalse( hoverEvent.isLegacy() ); - String serialized = ComponentSerializer.toString( component ); - BaseComponent[] deserialized = ComponentSerializer.parse( serialized ); - Assert.assertEquals( component.getHoverEvent(), deserialized[0].getHoverEvent() ); - // check the test still works with the value method - hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Sample text" ).create() ); - Assert.assertEquals( hoverEvent.getContents().size(), 1 ); - Assert.assertTrue( hoverEvent.isLegacy() ); - serialized = ComponentSerializer.toString( component ); - deserialized = ComponentSerializer.parse( serialized ); - Assert.assertEquals( component.getHoverEvent(), deserialized[0].getHoverEvent() ); + String serialized = ComponentSerializer.toString( component ); + T deserialized = deserializer.apply( serialized ); + Assert.assertEquals( component.getHoverEvent(), hoverEventGetter.apply( deserialized ) ); // Test single content: String json = "{\"italic\":true,\"color\":\"gray\",\"translate\":\"chat.type.admin\",\"with\":[{\"text\":\"@\"}" @@ -248,17 +320,28 @@ public class ComponentsTest + "\"/tell Name \"},\"hoverEvent\":{\"action\":\"show_entity\",\"contents\":" + "{\"type\":\"minecraft:player\",\"id\":\"00000000-0000-0000-0000-00000000000000\",\"name\":" + "{\"text\":\"Name\"}}},\"text\":\"Name\"}]}]}"; - testDissembleReassemble( ComponentSerializer.parse( json ) ); + dissembleReassembleTest.accept( deserializer.apply( json ) ); } @Test - public void testFormatRetentionCopyFormatting() + public void testFormatRetentionCopyFormattingCreate() + { + this.testFormatRetentionCopyFormatting( () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Test" ).create() ) ); + } + + @Test + public void testFormatRetentionCopyFormattingBuild() + { + this.testFormatRetentionCopyFormatting( () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "Test" ).build() ) ) ); + } + + private void testFormatRetentionCopyFormatting(Supplier hoverEventSupplier) { TextComponent first = new TextComponent( "Hello" ); first.setBold( true ); first.setColor( ChatColor.RED ); first.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "test" ) ); - first.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Test" ).create() ) ); + first.setHoverEvent( hoverEventSupplier.get() ); TextComponent second = new TextComponent( " world" ); second.copyFormatting( first, ComponentBuilder.FormatRetention.ALL, true ); @@ -269,16 +352,44 @@ public class ComponentsTest } @Test - public void testBuilderClone() + public void testBuilderCloneCreate() + { + this.testBuilderClone( (builder) -> TextComponent.toLegacyText( builder.create() ) ); + } + + @Test + public void testBuilderCloneBuild() + { + this.testBuilderClone( (builder) -> TextComponent.toLegacyText( builder.build() ) ); + } + + private void testBuilderClone(Function legacyTextFunction) { ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.RED ).append( "world" ).color( ChatColor.DARK_RED ); ComponentBuilder cloned = new ComponentBuilder( builder ); - Assert.assertEquals( TextComponent.toLegacyText( builder.create() ), TextComponent.toLegacyText( cloned.create() ) ); + Assert.assertEquals( legacyTextFunction.apply( builder ), legacyTextFunction.apply( cloned ) ); } @Test - public void testBuilderAppendMixedComponents() + public void testBuilderAppendCreateMixedComponents() + { + this.testBuilderAppendMixedComponents( + ComponentBuilder::create, + (components, index) -> components[index] + ); + } + + @Test + public void testBuilderAppendBuildMixedComponents() + { + this.testBuilderAppendMixedComponents( + ComponentBuilder::build, + (component, index) -> component.getExtra().get( index ) + ); + } + + private void testBuilderAppendMixedComponents(Function componentBuilder, BiFunction extraGetter) { ComponentBuilder builder = new ComponentBuilder( "Hello " ); TextComponent textComponent = new TextComponent( "world " ); @@ -291,11 +402,11 @@ public class ComponentsTest } ); ScoreComponent scoreComponent = new ScoreComponent( "myscore", "myobjective" ); builder.append( scoreComponent ); // non array based BaseComponent append - BaseComponent[] components = builder.create(); - Assert.assertEquals( "Hello ", components[0].toPlainText() ); - Assert.assertEquals( textComponent.toPlainText(), components[1].toPlainText() ); - Assert.assertEquals( translatableComponent.toPlainText(), components[2].toPlainText() ); - Assert.assertEquals( scoreComponent.toPlainText(), components[3].toPlainText() ); + T component = componentBuilder.apply( builder ); + Assert.assertEquals( "Hello ", extraGetter.apply( component, 0 ).toPlainText() ); + Assert.assertEquals( textComponent.toPlainText(), extraGetter.apply( component, 1 ).toPlainText() ); + Assert.assertEquals( translatableComponent.toPlainText(), extraGetter.apply( component, 2 ).toPlainText() ); + Assert.assertEquals( scoreComponent.toPlainText(), extraGetter.apply( component, 3 ).toPlainText() ); } @Test @@ -309,32 +420,80 @@ public class ComponentsTest } @Test - public void testBuilderAppend() + public void testBuilderAppendCreate() { - ClickEvent clickEvent = new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/help " ); - HoverEvent hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Hello world" ).create() ); - - ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW ); - builder.append( new ComponentBuilder( "world!" ).color( ChatColor.GREEN ).event( hoverEvent ).event( clickEvent ).create() ); - - BaseComponent[] components = builder.create(); - - Assert.assertEquals( components[1].getHoverEvent(), hoverEvent ); - Assert.assertEquals( components[1].getClickEvent(), clickEvent ); - Assert.assertEquals( "Hello world!", BaseComponent.toPlainText( components ) ); - Assert.assertEquals( ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", BaseComponent.toLegacyText( components ) ); + this.testBuilderAppend( + () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Hello world" ).create() ), + ComponentBuilder::create, + (components, index) -> components[index], + BaseComponent::toPlainText, + ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", + BaseComponent::toLegacyText + ); } @Test - public void testBuilderAppendLegacy() + public void testBuilderAppendBuild() + { + this.testBuilderAppend( + () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "Hello world" ).build() ) ), + ComponentBuilder::build, + (component, index) -> component.getExtra().get( index ), + (component) -> BaseComponent.toPlainText( component ), + // An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component + ChatColor.WHITE.toString() + ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", + (component) -> BaseComponent.toLegacyText( component ) + ); + } + + private void testBuilderAppend(Supplier hoverEventSupplier, Function componentBuilder, BiFunction extraGetter, Function toPlainTextFunction, String expectedLegacyText, Function toLegacyTextFunction) + { + ClickEvent clickEvent = new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/help " ); + HoverEvent hoverEvent = hoverEventSupplier.get(); + + ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW ); + builder.append( new ComponentBuilder( "world!" ).color( ChatColor.GREEN ).event( hoverEvent ).event( clickEvent ).create() ); // Intentionally using create() to append multiple individual components + + T component = componentBuilder.apply( builder ); + + Assert.assertEquals( extraGetter.apply( component, 1 ).getHoverEvent(), hoverEvent ); + Assert.assertEquals( extraGetter.apply( component, 1 ).getClickEvent(), clickEvent ); + Assert.assertEquals( "Hello world!", toPlainTextFunction.apply( component ) ); + Assert.assertEquals( expectedLegacyText, toLegacyTextFunction.apply( component ) ); + } + + @Test + public void testBuilderAppendLegacyCreate() + { + this.testBuilderAppendLegacy( + ComponentBuilder::create, + BaseComponent::toPlainText, + ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", + BaseComponent::toLegacyText + ); + } + + @Test + public void testBuilderAppendLegacyBuild() + { + this.testBuilderAppendLegacy( + ComponentBuilder::build, + (component) -> BaseComponent.toPlainText( component ), + // An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component + ChatColor.WHITE.toString() + ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", + (component) -> BaseComponent.toLegacyText( component ) + ); + } + + private void testBuilderAppendLegacy(Function componentBuilder, Function toPlainTextFunction, String expectedLegacyString, Function toLegacyTextFunction) { ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW ); builder.appendLegacy( "§aworld!" ); - BaseComponent[] components = builder.create(); + T component = componentBuilder.apply( builder ); - Assert.assertEquals( "Hello world!", BaseComponent.toPlainText( components ) ); - Assert.assertEquals( ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", BaseComponent.toLegacyText( components ) ); + Assert.assertEquals( "Hello world!", toPlainTextFunction.apply( component ) ); + Assert.assertEquals( expectedLegacyString, toLegacyTextFunction.apply( component ) ); } @Test @@ -397,57 +556,114 @@ public class ComponentsTest } @Test - public void testBuilder() + public void testBuilderCreate() { - BaseComponent[] components = new ComponentBuilder( "Hello " ).color( ChatColor.RED ). + this.testBuilder( + ComponentBuilder::create, + BaseComponent::toPlainText, + ChatColor.RED + "Hello " + ChatColor.BLUE + ChatColor.BOLD + + "World" + ChatColor.YELLOW + ChatColor.BOLD + "!", + BaseComponent::toLegacyText + ); + } + + @Test + public void testBuilderBuild() + { + this.testBuilder( + ComponentBuilder::build, + (component) -> BaseComponent.toPlainText( component ), + // An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component + ChatColor.WHITE.toString() + ChatColor.RED + "Hello " + ChatColor.BLUE + ChatColor.BOLD + + "World" + ChatColor.YELLOW + ChatColor.BOLD + "!", + (component) -> BaseComponent.toLegacyText( component ) + ); + } + + private void testBuilder(Function componentBuilder, Function toPlainTextFunction, String expectedLegacyString, Function toLegacyTextFunction) + { + T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED ). append( "World" ).bold( true ).color( ChatColor.BLUE ). - append( "!" ).color( ChatColor.YELLOW ).create(); + append( "!" ).color( ChatColor.YELLOW ) ); - Assert.assertEquals( "Hello World!", BaseComponent.toPlainText( components ) ); - Assert.assertEquals( ChatColor.RED + "Hello " + ChatColor.BLUE + ChatColor.BOLD - + "World" + ChatColor.YELLOW + ChatColor.BOLD + "!", BaseComponent.toLegacyText( components ) ); + Assert.assertEquals( "Hello World!", toPlainTextFunction.apply( component ) ); + Assert.assertEquals( expectedLegacyString, toLegacyTextFunction.apply( component ) ); } @Test - public void testBuilderReset() + public void testBuilderCreateReset() { - BaseComponent[] components = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) - .append( "World" ).reset().create(); - - Assert.assertEquals( components[0].getColor(), ChatColor.RED ); - Assert.assertEquals( components[1].getColor(), ChatColor.WHITE ); + this.testBuilderReset( + ComponentBuilder::create, + (components, index) -> components[index] + ); } @Test - public void testBuilderFormatRetention() + public void testBuilderBuildReset() { - BaseComponent[] noneRetention = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) - .append( "World", ComponentBuilder.FormatRetention.NONE ).create(); + this.testBuilderReset( + ComponentBuilder::build, + (component, index) -> component.getExtra().get( index ) + ); + } - Assert.assertEquals( noneRetention[0].getColor(), ChatColor.RED ); - Assert.assertEquals( noneRetention[1].getColor(), ChatColor.WHITE ); + private void testBuilderReset(Function componentBuilder, BiFunction extraGetter) + { + T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED ) + .append( "World" ).reset() ); - HoverEvent testEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "test" ).create() ); + Assert.assertEquals( ChatColor.RED, extraGetter.apply( component, 0 ).getColor() ); + Assert.assertEquals( ChatColor.WHITE, extraGetter.apply( component, 1 ).getColor() ); + } - BaseComponent[] formattingRetention = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) - .event( testEvent ).append( "World", ComponentBuilder.FormatRetention.FORMATTING ).create(); + @Test + public void testBuilderCreateFormatRetention() + { + this.testBuilderFormatRetention( + ComponentBuilder::create, + (components, index) -> components[index] + ); + } - Assert.assertEquals( formattingRetention[0].getColor(), ChatColor.RED ); - Assert.assertEquals( formattingRetention[0].getHoverEvent(), testEvent ); - Assert.assertEquals( formattingRetention[1].getColor(), ChatColor.RED ); - Assert.assertNull( formattingRetention[1].getHoverEvent() ); + @Test + public void testBuilderBuildFormatRetention() + { + this.testBuilderFormatRetention( + ComponentBuilder::build, + (component, index) -> component.getExtra().get( index ) + ); + } + + private void testBuilderFormatRetention(Function componentBuilder, BiFunction extraGetter) + { + T noneRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED ) + .append( "World", ComponentBuilder.FormatRetention.NONE ) ); + + Assert.assertEquals( ChatColor.RED, extraGetter.apply( noneRetention, 0 ).getColor() ); + Assert.assertEquals( ChatColor.WHITE, extraGetter.apply( noneRetention, 1 ).getColor() ); + + HoverEvent testEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "test" ).build() ) ); + + T formattingRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED ) + .event( testEvent ).append( "World", ComponentBuilder.FormatRetention.FORMATTING ) ); + + Assert.assertEquals( ChatColor.RED, extraGetter.apply( formattingRetention, 0 ).getColor() ); + Assert.assertEquals( testEvent, extraGetter.apply( formattingRetention, 0 ).getHoverEvent() ); + Assert.assertEquals( ChatColor.RED, extraGetter.apply( formattingRetention, 1 ).getColor() ); + Assert.assertNull( extraGetter.apply( formattingRetention, 1 ).getHoverEvent() ); ClickEvent testClickEvent = new ClickEvent( ClickEvent.Action.OPEN_URL, "http://www.example.com" ); - BaseComponent[] eventRetention = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) - .event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ).create(); + T eventRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED ) + .event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ) ); - Assert.assertEquals( eventRetention[0].getColor(), ChatColor.RED ); - Assert.assertEquals( eventRetention[0].getHoverEvent(), testEvent ); - Assert.assertEquals( eventRetention[0].getClickEvent(), testClickEvent ); - Assert.assertEquals( eventRetention[1].getColor(), ChatColor.WHITE ); - Assert.assertEquals( eventRetention[1].getHoverEvent(), testEvent ); - Assert.assertEquals( eventRetention[1].getClickEvent(), testClickEvent ); + Assert.assertEquals( ChatColor.RED, extraGetter.apply( eventRetention, 0 ).getColor() ); + Assert.assertEquals( testEvent, extraGetter.apply( eventRetention, 0 ).getHoverEvent() ); + Assert.assertEquals( testClickEvent, extraGetter.apply( eventRetention, 0 ).getClickEvent() ); + Assert.assertEquals( ChatColor.WHITE, extraGetter.apply( eventRetention, 1 ).getColor() ); + Assert.assertEquals( testEvent, extraGetter.apply( eventRetention, 1 ).getHoverEvent() ); + Assert.assertEquals( testClickEvent, extraGetter.apply( eventRetention, 1 ).getClickEvent() ); } @Test(expected = IllegalArgumentException.class) @@ -572,12 +788,29 @@ public class ComponentsTest Assert.assertArrayEquals( hexColored, reColored ); } - /** + @Test + public void testLegacyResetInBuilderCreate() + { + this.testLegacyResetInBuilder( + ComponentBuilder::create, + ComponentSerializer::toString + ); + } + + @Test + public void testLegacyResetInBuilderBuild() + { + this.testLegacyResetInBuilder( + ComponentBuilder::build, + ComponentSerializer::toString + ); + } + + /* * In legacy chat, colors and reset both reset all formatting. * Make sure it works in combination with ComponentBuilder. */ - @Test - public void testLegacyResetInBuilder() + private void testLegacyResetInBuilder(Function componentBuilder, Function componentSerializer) { ComponentBuilder builder = new ComponentBuilder(); BaseComponent[] a = TextComponent.fromLegacyText( "§4§n44444§rdd§6§l6666" ); @@ -588,13 +821,13 @@ public class ComponentsTest builder.append( a ); - String test1 = ComponentSerializer.toString( builder.create() ); + String test1 = componentSerializer.apply( componentBuilder.apply( builder ) ); Assert.assertEquals( expected, test1 ); BaseComponent[] b = TextComponent.fromLegacyText( "§rrrrr" ); builder.append( b ); - String test2 = ComponentSerializer.toString( builder.create() ); + String test2 = componentSerializer.apply( componentBuilder.apply( builder ) ); Assert.assertEquals( "{\"extra\":[{\"underlined\":true,\"color\":\"dark_red\",\"text\":\"44444\"}," + "{\"color\":\"white\",\"text\":\"dd\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"6666\"},"