#3490: Add ComponentBuilder#build() and ComponentSerializer#deserialize()
Components traditionally use the extra data to represent components as a single BaseComponent object. While BaseComponent typically mirrors this behaviour, somewhere along the development of BungeeChat this practice was made unclear. Because ComponentBuilder#create() returns an array of BaseComponents, it has sort of been silently accepted that all components should be represented as arrays, which is incorrect. This heavily influenced the direction of Spigot's component API (with additions such as CommandSender#sendMessage(BaseComponent[])) which emphasizes this misconception of "all components are arrays". Adding new methods to ComponentBuilder and ComponentSerializer should steer use of the BungeeChat API to be more oriented towards single component instances, not arrays.
This commit is contained in:
parent
d68ebd1eaf
commit
cfe00fa47c
@ -454,9 +454,32 @@ public final class ComponentBuilder
|
|||||||
return this;
|
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<BaseComponent> cloned = new ArrayList<>( parts );
|
||||||
|
cloned.replaceAll( BaseComponent::duplicate );
|
||||||
|
base.setExtra( cloned );
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the components needed to display the message created by this
|
* Returns the components needed to display the message created by this
|
||||||
* builder.git
|
* builder.git
|
||||||
|
* <p>
|
||||||
|
* <strong>NOTE:</strong> {@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
|
* @return the created components
|
||||||
*/
|
*/
|
||||||
|
@ -23,6 +23,12 @@ public class Text extends Content
|
|||||||
this.value = value;
|
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)
|
public Text(String value)
|
||||||
{
|
{
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
@ -27,7 +27,6 @@ import net.md_5.bungee.api.chat.hover.content.TextSerializer;
|
|||||||
public class ComponentSerializer implements JsonDeserializer<BaseComponent>
|
public class ComponentSerializer implements JsonDeserializer<BaseComponent>
|
||||||
{
|
{
|
||||||
|
|
||||||
private static final JsonParser JSON_PARSER = new JsonParser();
|
|
||||||
private static final Gson gson = new GsonBuilder().
|
private static final Gson gson = new GsonBuilder().
|
||||||
registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ).
|
registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ).
|
||||||
registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ).
|
registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ).
|
||||||
@ -43,9 +42,25 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
|
|||||||
|
|
||||||
public static final ThreadLocal<Set<BaseComponent>> serializedComponents = new ThreadLocal<Set<BaseComponent>>();
|
public static final ThreadLocal<Set<BaseComponent>> serializedComponents = new ThreadLocal<Set<BaseComponent>>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* <strong>NOTE:</strong> {@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)
|
public static BaseComponent[] parse(String json)
|
||||||
{
|
{
|
||||||
JsonElement jsonElement = JSON_PARSER.parse( json );
|
JsonElement jsonElement = JsonParser.parseString( json );
|
||||||
|
|
||||||
if ( jsonElement.isJsonArray() )
|
if ( jsonElement.isJsonArray() )
|
||||||
{
|
{
|
||||||
@ -59,6 +74,26 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
public static String toString(Object object)
|
||||||
{
|
{
|
||||||
return gson.toJson( object );
|
return gson.toJson( object );
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
package net.md_5.bungee.api.chat;
|
package net.md_5.bungee.api.chat;
|
||||||
|
|
||||||
import java.awt.Color;
|
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.ChatColor;
|
||||||
import net.md_5.bungee.api.chat.hover.content.Item;
|
import net.md_5.bungee.api.chat.hover.content.Item;
|
||||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||||
@ -18,11 +23,25 @@ public class ComponentsTest
|
|||||||
Assert.assertEquals( TextComponent.toLegacyText( parsed ), TextComponent.toLegacyText( components ) );
|
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( 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 );
|
BaseComponent[] parsed = ComponentSerializer.parse( json );
|
||||||
Assert.assertEquals( json, ComponentSerializer.toString( parsed ) );
|
Assert.assertEquals( json, ComponentSerializer.toString( parsed ) );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testItemParse()
|
public void testItemParse()
|
||||||
@ -41,8 +60,10 @@ public class ComponentsTest
|
|||||||
{
|
{
|
||||||
textComponent
|
textComponent
|
||||||
} );
|
} );
|
||||||
|
testDissembleReassemble( textComponent );
|
||||||
json = "{\"hoverEvent\":{\"action\":\"show_item\",\"value\":[{\"text\":\"{id:\\\"minecraft:netherrack\\\",Count:47b}\"}]},\"text\":\"Test\"}";
|
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}\"}";
|
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\":\"\"}";
|
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
|
@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 <T> void testEmptyComponentBuilder(Function<ComponentBuilder, T> componentBuilder, Consumer<T> emptyAssertion, ObjIntConsumer<T> sizedAssertion)
|
||||||
{
|
{
|
||||||
ComponentBuilder builder = new ComponentBuilder();
|
ComponentBuilder builder = new ComponentBuilder();
|
||||||
|
|
||||||
BaseComponent[] parts = builder.create();
|
T component = componentBuilder.apply( builder );
|
||||||
Assert.assertEquals( parts.length, 0 );
|
emptyAssertion.accept( component );
|
||||||
|
|
||||||
for ( int i = 0; i < 3; i++ )
|
for ( int i = 0; i < 3; i++ )
|
||||||
{
|
{
|
||||||
builder.append( "part:" + i );
|
builder.append( "part:" + i );
|
||||||
parts = builder.create();
|
component = componentBuilder.apply( builder );
|
||||||
Assert.assertEquals( parts.length, i + 1 );
|
sizedAssertion.accept( component, i + 1 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +253,7 @@ public class ComponentsTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHoverEventContents()
|
public void testHoverEventContentsCreate()
|
||||||
{
|
{
|
||||||
// First do the text using the newer contents system
|
// First do the text using the newer contents system
|
||||||
HoverEvent hoverEvent = new HoverEvent(
|
HoverEvent hoverEvent = new HoverEvent(
|
||||||
@ -222,21 +262,53 @@ public class ComponentsTest
|
|||||||
new Text( new ComponentBuilder( "Second" ).create() )
|
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 <T> void testHoverEventContents(HoverEvent hoverEvent, Function<String, T> deserializer, Function<T, HoverEvent> hoverEventGetter, Consumer<T> dissembleReassembleTest)
|
||||||
|
{
|
||||||
TextComponent component = new TextComponent( "Sample text" );
|
TextComponent component = new TextComponent( "Sample text" );
|
||||||
component.setHoverEvent( hoverEvent );
|
component.setHoverEvent( hoverEvent );
|
||||||
Assert.assertEquals( hoverEvent.getContents().size(), 2 );
|
Assert.assertEquals( hoverEvent.getContents().size(), 2 );
|
||||||
Assert.assertFalse( hoverEvent.isLegacy() );
|
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
|
String serialized = ComponentSerializer.toString( component );
|
||||||
hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Sample text" ).create() );
|
T deserialized = deserializer.apply( serialized );
|
||||||
Assert.assertEquals( hoverEvent.getContents().size(), 1 );
|
Assert.assertEquals( component.getHoverEvent(), hoverEventGetter.apply( deserialized ) );
|
||||||
Assert.assertTrue( hoverEvent.isLegacy() );
|
|
||||||
serialized = ComponentSerializer.toString( component );
|
|
||||||
deserialized = ComponentSerializer.parse( serialized );
|
|
||||||
Assert.assertEquals( component.getHoverEvent(), deserialized[0].getHoverEvent() );
|
|
||||||
|
|
||||||
// Test single content:
|
// Test single content:
|
||||||
String json = "{\"italic\":true,\"color\":\"gray\",\"translate\":\"chat.type.admin\",\"with\":[{\"text\":\"@\"}"
|
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\":"
|
+ "\"/tell Name \"},\"hoverEvent\":{\"action\":\"show_entity\",\"contents\":"
|
||||||
+ "{\"type\":\"minecraft:player\",\"id\":\"00000000-0000-0000-0000-00000000000000\",\"name\":"
|
+ "{\"type\":\"minecraft:player\",\"id\":\"00000000-0000-0000-0000-00000000000000\",\"name\":"
|
||||||
+ "{\"text\":\"Name\"}}},\"text\":\"Name\"}]}]}";
|
+ "{\"text\":\"Name\"}}},\"text\":\"Name\"}]}]}";
|
||||||
testDissembleReassemble( ComponentSerializer.parse( json ) );
|
dissembleReassembleTest.accept( deserializer.apply( json ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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<HoverEvent> hoverEventSupplier)
|
||||||
{
|
{
|
||||||
TextComponent first = new TextComponent( "Hello" );
|
TextComponent first = new TextComponent( "Hello" );
|
||||||
first.setBold( true );
|
first.setBold( true );
|
||||||
first.setColor( ChatColor.RED );
|
first.setColor( ChatColor.RED );
|
||||||
first.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "test" ) );
|
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" );
|
TextComponent second = new TextComponent( " world" );
|
||||||
second.copyFormatting( first, ComponentBuilder.FormatRetention.ALL, true );
|
second.copyFormatting( first, ComponentBuilder.FormatRetention.ALL, true );
|
||||||
@ -269,16 +352,44 @@ public class ComponentsTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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<ComponentBuilder, String> legacyTextFunction)
|
||||||
{
|
{
|
||||||
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.RED ).append( "world" ).color( ChatColor.DARK_RED );
|
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.RED ).append( "world" ).color( ChatColor.DARK_RED );
|
||||||
ComponentBuilder cloned = new ComponentBuilder( builder );
|
ComponentBuilder cloned = new ComponentBuilder( builder );
|
||||||
|
|
||||||
Assert.assertEquals( TextComponent.toLegacyText( builder.create() ), TextComponent.toLegacyText( cloned.create() ) );
|
Assert.assertEquals( legacyTextFunction.apply( builder ), legacyTextFunction.apply( cloned ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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 <T> void testBuilderAppendMixedComponents(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter)
|
||||||
{
|
{
|
||||||
ComponentBuilder builder = new ComponentBuilder( "Hello " );
|
ComponentBuilder builder = new ComponentBuilder( "Hello " );
|
||||||
TextComponent textComponent = new TextComponent( "world " );
|
TextComponent textComponent = new TextComponent( "world " );
|
||||||
@ -291,11 +402,11 @@ public class ComponentsTest
|
|||||||
} );
|
} );
|
||||||
ScoreComponent scoreComponent = new ScoreComponent( "myscore", "myobjective" );
|
ScoreComponent scoreComponent = new ScoreComponent( "myscore", "myobjective" );
|
||||||
builder.append( scoreComponent ); // non array based BaseComponent append
|
builder.append( scoreComponent ); // non array based BaseComponent append
|
||||||
BaseComponent[] components = builder.create();
|
T component = componentBuilder.apply( builder );
|
||||||
Assert.assertEquals( "Hello ", components[0].toPlainText() );
|
Assert.assertEquals( "Hello ", extraGetter.apply( component, 0 ).toPlainText() );
|
||||||
Assert.assertEquals( textComponent.toPlainText(), components[1].toPlainText() );
|
Assert.assertEquals( textComponent.toPlainText(), extraGetter.apply( component, 1 ).toPlainText() );
|
||||||
Assert.assertEquals( translatableComponent.toPlainText(), components[2].toPlainText() );
|
Assert.assertEquals( translatableComponent.toPlainText(), extraGetter.apply( component, 2 ).toPlainText() );
|
||||||
Assert.assertEquals( scoreComponent.toPlainText(), components[3].toPlainText() );
|
Assert.assertEquals( scoreComponent.toPlainText(), extraGetter.apply( component, 3 ).toPlainText() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -309,32 +420,80 @@ public class ComponentsTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBuilderAppend()
|
public void testBuilderAppendCreate()
|
||||||
{
|
{
|
||||||
ClickEvent clickEvent = new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/help " );
|
this.testBuilderAppend(
|
||||||
HoverEvent hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Hello world" ).create() );
|
() -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Hello world" ).create() ),
|
||||||
|
ComponentBuilder::create,
|
||||||
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW );
|
(components, index) -> components[index],
|
||||||
builder.append( new ComponentBuilder( "world!" ).color( ChatColor.GREEN ).event( hoverEvent ).event( clickEvent ).create() );
|
BaseComponent::toPlainText,
|
||||||
|
ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!",
|
||||||
BaseComponent[] components = builder.create();
|
BaseComponent::toLegacyText
|
||||||
|
);
|
||||||
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 ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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 <T> void testBuilderAppend(Supplier<HoverEvent> hoverEventSupplier, Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter, Function<T, String> toPlainTextFunction, String expectedLegacyText, Function<T, String> 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 <T> void testBuilderAppendLegacy(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction)
|
||||||
{
|
{
|
||||||
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW );
|
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW );
|
||||||
builder.appendLegacy( "§aworld!" );
|
builder.appendLegacy( "§aworld!" );
|
||||||
|
|
||||||
BaseComponent[] components = builder.create();
|
T component = componentBuilder.apply( builder );
|
||||||
|
|
||||||
Assert.assertEquals( "Hello world!", BaseComponent.toPlainText( components ) );
|
Assert.assertEquals( "Hello world!", toPlainTextFunction.apply( component ) );
|
||||||
Assert.assertEquals( ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", BaseComponent.toLegacyText( components ) );
|
Assert.assertEquals( expectedLegacyString, toLegacyTextFunction.apply( component ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -397,57 +556,114 @@ public class ComponentsTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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 <T> void testBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction)
|
||||||
|
{
|
||||||
|
T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED ).
|
||||||
append( "World" ).bold( true ).color( ChatColor.BLUE ).
|
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( "Hello World!", toPlainTextFunction.apply( component ) );
|
||||||
Assert.assertEquals( ChatColor.RED + "Hello " + ChatColor.BLUE + ChatColor.BOLD
|
Assert.assertEquals( expectedLegacyString, toLegacyTextFunction.apply( component ) );
|
||||||
+ "World" + ChatColor.YELLOW + ChatColor.BOLD + "!", BaseComponent.toLegacyText( components ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBuilderReset()
|
public void testBuilderCreateReset()
|
||||||
{
|
{
|
||||||
BaseComponent[] components = new ComponentBuilder( "Hello " ).color( ChatColor.RED )
|
this.testBuilderReset(
|
||||||
.append( "World" ).reset().create();
|
ComponentBuilder::create,
|
||||||
|
(components, index) -> components[index]
|
||||||
Assert.assertEquals( components[0].getColor(), ChatColor.RED );
|
);
|
||||||
Assert.assertEquals( components[1].getColor(), ChatColor.WHITE );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBuilderFormatRetention()
|
public void testBuilderBuildReset()
|
||||||
{
|
{
|
||||||
BaseComponent[] noneRetention = new ComponentBuilder( "Hello " ).color( ChatColor.RED )
|
this.testBuilderReset(
|
||||||
.append( "World", ComponentBuilder.FormatRetention.NONE ).create();
|
ComponentBuilder::build,
|
||||||
|
(component, index) -> component.getExtra().get( index )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Assert.assertEquals( noneRetention[0].getColor(), ChatColor.RED );
|
private <T> void testBuilderReset(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter)
|
||||||
Assert.assertEquals( noneRetention[1].getColor(), ChatColor.WHITE );
|
{
|
||||||
|
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 )
|
@Test
|
||||||
.event( testEvent ).append( "World", ComponentBuilder.FormatRetention.FORMATTING ).create();
|
public void testBuilderCreateFormatRetention()
|
||||||
|
{
|
||||||
|
this.testBuilderFormatRetention(
|
||||||
|
ComponentBuilder::create,
|
||||||
|
(components, index) -> components[index]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Assert.assertEquals( formattingRetention[0].getColor(), ChatColor.RED );
|
@Test
|
||||||
Assert.assertEquals( formattingRetention[0].getHoverEvent(), testEvent );
|
public void testBuilderBuildFormatRetention()
|
||||||
Assert.assertEquals( formattingRetention[1].getColor(), ChatColor.RED );
|
{
|
||||||
Assert.assertNull( formattingRetention[1].getHoverEvent() );
|
this.testBuilderFormatRetention(
|
||||||
|
ComponentBuilder::build,
|
||||||
|
(component, index) -> component.getExtra().get( index )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void testBuilderFormatRetention(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> 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" );
|
ClickEvent testClickEvent = new ClickEvent( ClickEvent.Action.OPEN_URL, "http://www.example.com" );
|
||||||
|
|
||||||
BaseComponent[] eventRetention = new ComponentBuilder( "Hello " ).color( ChatColor.RED )
|
T eventRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED )
|
||||||
.event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ).create();
|
.event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ) );
|
||||||
|
|
||||||
Assert.assertEquals( eventRetention[0].getColor(), ChatColor.RED );
|
Assert.assertEquals( ChatColor.RED, extraGetter.apply( eventRetention, 0 ).getColor() );
|
||||||
Assert.assertEquals( eventRetention[0].getHoverEvent(), testEvent );
|
Assert.assertEquals( testEvent, extraGetter.apply( eventRetention, 0 ).getHoverEvent() );
|
||||||
Assert.assertEquals( eventRetention[0].getClickEvent(), testClickEvent );
|
Assert.assertEquals( testClickEvent, extraGetter.apply( eventRetention, 0 ).getClickEvent() );
|
||||||
Assert.assertEquals( eventRetention[1].getColor(), ChatColor.WHITE );
|
Assert.assertEquals( ChatColor.WHITE, extraGetter.apply( eventRetention, 1 ).getColor() );
|
||||||
Assert.assertEquals( eventRetention[1].getHoverEvent(), testEvent );
|
Assert.assertEquals( testEvent, extraGetter.apply( eventRetention, 1 ).getHoverEvent() );
|
||||||
Assert.assertEquals( eventRetention[1].getClickEvent(), testClickEvent );
|
Assert.assertEquals( testClickEvent, extraGetter.apply( eventRetention, 1 ).getClickEvent() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
@ -572,12 +788,29 @@ public class ComponentsTest
|
|||||||
Assert.assertArrayEquals( hexColored, reColored );
|
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.
|
* In legacy chat, colors and reset both reset all formatting.
|
||||||
* Make sure it works in combination with ComponentBuilder.
|
* Make sure it works in combination with ComponentBuilder.
|
||||||
*/
|
*/
|
||||||
@Test
|
private <T> void testLegacyResetInBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> componentSerializer)
|
||||||
public void testLegacyResetInBuilder()
|
|
||||||
{
|
{
|
||||||
ComponentBuilder builder = new ComponentBuilder();
|
ComponentBuilder builder = new ComponentBuilder();
|
||||||
BaseComponent[] a = TextComponent.fromLegacyText( "§4§n44444§rdd§6§l6666" );
|
BaseComponent[] a = TextComponent.fromLegacyText( "§4§n44444§rdd§6§l6666" );
|
||||||
@ -588,13 +821,13 @@ public class ComponentsTest
|
|||||||
|
|
||||||
builder.append( a );
|
builder.append( a );
|
||||||
|
|
||||||
String test1 = ComponentSerializer.toString( builder.create() );
|
String test1 = componentSerializer.apply( componentBuilder.apply( builder ) );
|
||||||
Assert.assertEquals( expected, test1 );
|
Assert.assertEquals( expected, test1 );
|
||||||
|
|
||||||
BaseComponent[] b = TextComponent.fromLegacyText( "§rrrrr" );
|
BaseComponent[] b = TextComponent.fromLegacyText( "§rrrrr" );
|
||||||
builder.append( b );
|
builder.append( b );
|
||||||
|
|
||||||
String test2 = ComponentSerializer.toString( builder.create() );
|
String test2 = componentSerializer.apply( componentBuilder.apply( builder ) );
|
||||||
Assert.assertEquals(
|
Assert.assertEquals(
|
||||||
"{\"extra\":[{\"underlined\":true,\"color\":\"dark_red\",\"text\":\"44444\"},"
|
"{\"extra\":[{\"underlined\":true,\"color\":\"dark_red\",\"text\":\"44444\"},"
|
||||||
+ "{\"color\":\"white\",\"text\":\"dd\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"6666\"},"
|
+ "{\"color\":\"white\",\"text\":\"dd\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"6666\"},"
|
||||||
|
Loading…
Reference in New Issue
Block a user