#2363: Chat Component API Improvements
- duplicateWithoutFormatting deprecated and now works to include extra. Less maintenance required for any component implementations. - Improved copyFormatting API to allow for retention copying. - API to append a single BaseComponent in a ComponentBuilder, previously had to wrap a component in its own array to do this. - BaseComponent retain API that functions the same as from ComponentBuilder.
This commit is contained in:
parent
74e077e0fb
commit
7653a5f0f8
@ -9,6 +9,7 @@ import net.md_5.bungee.api.ChatColor;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder.FormatRetention;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@ToString(exclude = "parent")
|
@ToString(exclude = "parent")
|
||||||
@ -62,13 +63,13 @@ public abstract class BaseComponent
|
|||||||
private List<BaseComponent> extra;
|
private List<BaseComponent> extra;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The action to preform when this component (and child components) are
|
* The action to perform when this component (and child components) are
|
||||||
* clicked
|
* clicked
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
private ClickEvent clickEvent;
|
private ClickEvent clickEvent;
|
||||||
/**
|
/**
|
||||||
* The action to preform when this component (and child components) are
|
* The action to perform when this component (and child components) are
|
||||||
* hovered over
|
* hovered over
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@ -76,29 +77,118 @@ public abstract class BaseComponent
|
|||||||
|
|
||||||
BaseComponent(BaseComponent old)
|
BaseComponent(BaseComponent old)
|
||||||
{
|
{
|
||||||
copyFormatting( old );
|
copyFormatting( old, FormatRetention.ALL, true );
|
||||||
}
|
|
||||||
|
|
||||||
public void copyFormatting(BaseComponent component)
|
if ( old.getExtra() != null )
|
||||||
{
|
|
||||||
setColor( component.getColorRaw() );
|
|
||||||
setBold( component.isBoldRaw() );
|
|
||||||
setItalic( component.isItalicRaw() );
|
|
||||||
setUnderlined( component.isUnderlinedRaw() );
|
|
||||||
setStrikethrough( component.isStrikethroughRaw() );
|
|
||||||
setObfuscated( component.isObfuscatedRaw() );
|
|
||||||
setInsertion( component.getInsertion() );
|
|
||||||
setClickEvent( component.getClickEvent() );
|
|
||||||
setHoverEvent( component.getHoverEvent() );
|
|
||||||
if ( component.getExtra() != null )
|
|
||||||
{
|
{
|
||||||
for ( BaseComponent extra : component.getExtra() )
|
for ( BaseComponent extra : old.getExtra() )
|
||||||
{
|
{
|
||||||
addExtra( extra.duplicate() );
|
addExtra( extra.duplicate() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the events and formatting of a BaseComponent. Already set
|
||||||
|
* formatting will be replaced.
|
||||||
|
*
|
||||||
|
* @param component the component to copy from
|
||||||
|
*/
|
||||||
|
public void copyFormatting(BaseComponent component)
|
||||||
|
{
|
||||||
|
copyFormatting( component, FormatRetention.ALL, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the events and formatting of a BaseComponent.
|
||||||
|
*
|
||||||
|
* @param component the component to copy from
|
||||||
|
* @param replace if already set formatting should be replaced by the new
|
||||||
|
* component
|
||||||
|
*/
|
||||||
|
public void copyFormatting(BaseComponent component, boolean replace)
|
||||||
|
{
|
||||||
|
copyFormatting( component, FormatRetention.ALL, replace );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the specified formatting of a BaseComponent.
|
||||||
|
*
|
||||||
|
* @param component the component to copy from
|
||||||
|
* @param retention the formatting to copy
|
||||||
|
* @param replace if already set formatting should be replaced by the new
|
||||||
|
* component
|
||||||
|
*/
|
||||||
|
public void copyFormatting(BaseComponent component, FormatRetention retention, boolean replace)
|
||||||
|
{
|
||||||
|
if ( retention == FormatRetention.EVENTS || retention == FormatRetention.ALL )
|
||||||
|
{
|
||||||
|
if ( replace || clickEvent == null )
|
||||||
|
{
|
||||||
|
setClickEvent( component.getClickEvent() );
|
||||||
|
}
|
||||||
|
if ( replace || hoverEvent == null )
|
||||||
|
{
|
||||||
|
setHoverEvent( component.getHoverEvent() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( retention == FormatRetention.FORMATTING || retention == FormatRetention.ALL )
|
||||||
|
{
|
||||||
|
if ( replace || color == null )
|
||||||
|
{
|
||||||
|
setColor( component.getColorRaw() );
|
||||||
|
}
|
||||||
|
if ( replace || bold == null )
|
||||||
|
{
|
||||||
|
setBold( component.isBoldRaw() );
|
||||||
|
}
|
||||||
|
if ( replace || italic == null )
|
||||||
|
{
|
||||||
|
setItalic( component.isItalicRaw() );
|
||||||
|
}
|
||||||
|
if ( replace || underlined == null )
|
||||||
|
{
|
||||||
|
setUnderlined( component.isUnderlinedRaw() );
|
||||||
|
}
|
||||||
|
if ( replace || strikethrough == null )
|
||||||
|
{
|
||||||
|
setStrikethrough( component.isStrikethroughRaw() );
|
||||||
|
}
|
||||||
|
if ( replace || obfuscated == null )
|
||||||
|
{
|
||||||
|
setObfuscated( component.isObfuscatedRaw() );
|
||||||
|
}
|
||||||
|
if ( replace || insertion == null )
|
||||||
|
{
|
||||||
|
setInsertion( component.getInsertion() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retains only the specified formatting.
|
||||||
|
*
|
||||||
|
* @param retention the formatting to retain
|
||||||
|
*/
|
||||||
|
public void retain(FormatRetention retention)
|
||||||
|
{
|
||||||
|
if ( retention == FormatRetention.FORMATTING || retention == FormatRetention.NONE )
|
||||||
|
{
|
||||||
|
setClickEvent( null );
|
||||||
|
setHoverEvent( null );
|
||||||
|
}
|
||||||
|
if ( retention == FormatRetention.EVENTS || retention == FormatRetention.NONE )
|
||||||
|
{
|
||||||
|
setColor( null );
|
||||||
|
setBold( null );
|
||||||
|
setItalic( null );
|
||||||
|
setUnderlined( null );
|
||||||
|
setStrikethrough( null );
|
||||||
|
setObfuscated( null );
|
||||||
|
setInsertion( null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clones the BaseComponent and returns the clone.
|
* Clones the BaseComponent and returns the clone.
|
||||||
*
|
*
|
||||||
@ -110,8 +200,15 @@ public abstract class BaseComponent
|
|||||||
* Clones the BaseComponent without formatting and returns the clone.
|
* Clones the BaseComponent without formatting and returns the clone.
|
||||||
*
|
*
|
||||||
* @return The duplicate of this BaseComponent
|
* @return The duplicate of this BaseComponent
|
||||||
|
* @deprecated API use discouraged, use traditional duplicate
|
||||||
*/
|
*/
|
||||||
public abstract BaseComponent duplicateWithoutFormatting();
|
@Deprecated
|
||||||
|
public BaseComponent duplicateWithoutFormatting()
|
||||||
|
{
|
||||||
|
BaseComponent component = duplicate();
|
||||||
|
component.retain( FormatRetention.NONE );
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the components to a string that uses the old formatting codes
|
* Converts the components to a string that uses the old formatting codes
|
||||||
|
@ -55,8 +55,51 @@ public final class ComponentBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the components to the builder and makes it the current target for
|
* Creates a ComponentBuilder with the given component as the first part.
|
||||||
* formatting. The text will have all the formatting from the previous part.
|
*
|
||||||
|
* @param component the first component element
|
||||||
|
*/
|
||||||
|
public ComponentBuilder(BaseComponent component)
|
||||||
|
{
|
||||||
|
current = component.duplicate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a component to the builder and makes it the current target for
|
||||||
|
* formatting. The component will have all the formatting from previous
|
||||||
|
* part.
|
||||||
|
*
|
||||||
|
* @param component the component to append
|
||||||
|
* @return this ComponentBuilder for chaining
|
||||||
|
*/
|
||||||
|
public ComponentBuilder append(BaseComponent component)
|
||||||
|
{
|
||||||
|
return append( component, FormatRetention.ALL );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a component to the builder and makes it the current target for
|
||||||
|
* formatting. You can specify the amount of formatting retained from
|
||||||
|
* previous part.
|
||||||
|
*
|
||||||
|
* @param component the component to append
|
||||||
|
* @param retention the formatting to retain
|
||||||
|
* @return this ComponentBuilder for chaining
|
||||||
|
*/
|
||||||
|
public ComponentBuilder append(BaseComponent component, FormatRetention retention)
|
||||||
|
{
|
||||||
|
parts.add( current );
|
||||||
|
|
||||||
|
BaseComponent previous = current;
|
||||||
|
current = component.duplicate();
|
||||||
|
current.copyFormatting( previous, retention, false );
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the components to the builder and makes the last element the
|
||||||
|
* current target for formatting. The components will have all the
|
||||||
|
* formatting from previous part.
|
||||||
*
|
*
|
||||||
* @param components the components to append
|
* @param components the components to append
|
||||||
* @return this ComponentBuilder for chaining
|
* @return this ComponentBuilder for chaining
|
||||||
@ -67,8 +110,9 @@ public final class ComponentBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the components to the builder and makes it the current target for
|
* Appends the components to the builder and makes the last element the
|
||||||
* formatting. You can specify the amount of formatting retained.
|
* current target for formatting. You can specify the amount of formatting
|
||||||
|
* retained from previous part.
|
||||||
*
|
*
|
||||||
* @param components the components to append
|
* @param components the components to append
|
||||||
* @param retention the formatting to retain
|
* @param retention the formatting to retain
|
||||||
@ -82,8 +126,9 @@ public final class ComponentBuilder
|
|||||||
{
|
{
|
||||||
parts.add( current );
|
parts.add( current );
|
||||||
|
|
||||||
|
BaseComponent previous = current;
|
||||||
current = component.duplicate();
|
current = component.duplicate();
|
||||||
retain( retention );
|
current.copyFormatting( previous, retention, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@ -91,7 +136,7 @@ public final class ComponentBuilder
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the text to the builder and makes it the current target for
|
* Appends the text to the builder and makes it the current target for
|
||||||
* formatting. The text will have all the formatting from the previous part.
|
* formatting. The text will have all the formatting from previous part.
|
||||||
*
|
*
|
||||||
* @param text the text to append
|
* @param text the text to append
|
||||||
* @return this ComponentBuilder for chaining
|
* @return this ComponentBuilder for chaining
|
||||||
@ -103,7 +148,8 @@ public final class ComponentBuilder
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the text to the builder and makes it the current target for
|
* Appends the text to the builder and makes it the current target for
|
||||||
* formatting. You can specify the amount of formatting retained.
|
* formatting. You can specify the amount of formatting retained from
|
||||||
|
* previous part.
|
||||||
*
|
*
|
||||||
* @param text the text to append
|
* @param text the text to append
|
||||||
* @param retention the formatting to retain
|
* @param retention the formatting to retain
|
||||||
@ -115,8 +161,7 @@ public final class ComponentBuilder
|
|||||||
|
|
||||||
BaseComponent old = current;
|
BaseComponent old = current;
|
||||||
current = new TextComponent( text );
|
current = new TextComponent( text );
|
||||||
current.copyFormatting( old );
|
current.copyFormatting( old, retention, false );
|
||||||
retain( retention );
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -247,27 +292,7 @@ public final class ComponentBuilder
|
|||||||
*/
|
*/
|
||||||
public ComponentBuilder retain(FormatRetention retention)
|
public ComponentBuilder retain(FormatRetention retention)
|
||||||
{
|
{
|
||||||
BaseComponent previous = current;
|
current.retain( retention );
|
||||||
|
|
||||||
switch ( retention )
|
|
||||||
{
|
|
||||||
case NONE:
|
|
||||||
current = current.duplicateWithoutFormatting();
|
|
||||||
break;
|
|
||||||
case ALL:
|
|
||||||
// No changes are required
|
|
||||||
break;
|
|
||||||
case EVENTS:
|
|
||||||
current = current.duplicateWithoutFormatting();
|
|
||||||
current.setInsertion( previous.getInsertion() );
|
|
||||||
current.setClickEvent( previous.getClickEvent() );
|
|
||||||
current.setHoverEvent( previous.getHoverEvent() );
|
|
||||||
break;
|
|
||||||
case FORMATTING:
|
|
||||||
current.setClickEvent( null );
|
|
||||||
current.setHoverEvent( null );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,12 +48,6 @@ public final class KeybindComponent extends BaseComponent
|
|||||||
return new KeybindComponent( this );
|
return new KeybindComponent( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseComponent duplicateWithoutFormatting()
|
|
||||||
{
|
|
||||||
return new KeybindComponent( keybind );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void toPlainText(StringBuilder builder)
|
protected void toPlainText(StringBuilder builder)
|
||||||
{
|
{
|
||||||
|
@ -82,12 +82,6 @@ public final class ScoreComponent extends BaseComponent
|
|||||||
return new ScoreComponent( this );
|
return new ScoreComponent( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ScoreComponent duplicateWithoutFormatting()
|
|
||||||
{
|
|
||||||
return new ScoreComponent( this.name, this.objective, this.value );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void toLegacyText(StringBuilder builder)
|
protected void toLegacyText(StringBuilder builder)
|
||||||
{
|
{
|
||||||
|
@ -48,12 +48,6 @@ public final class SelectorComponent extends BaseComponent
|
|||||||
return new SelectorComponent( this );
|
return new SelectorComponent( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public SelectorComponent duplicateWithoutFormatting()
|
|
||||||
{
|
|
||||||
return new SelectorComponent( this.selector );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void toLegacyText(StringBuilder builder)
|
protected void toLegacyText(StringBuilder builder)
|
||||||
{
|
{
|
||||||
|
@ -180,12 +180,6 @@ public final class TextComponent extends BaseComponent
|
|||||||
return new TextComponent( this );
|
return new TextComponent( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseComponent duplicateWithoutFormatting()
|
|
||||||
{
|
|
||||||
return new TextComponent( this.text );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void toPlainText(StringBuilder builder)
|
protected void toPlainText(StringBuilder builder)
|
||||||
{
|
{
|
||||||
|
@ -94,12 +94,6 @@ public final class TranslatableComponent extends BaseComponent
|
|||||||
return new TranslatableComponent( this );
|
return new TranslatableComponent( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseComponent duplicateWithoutFormatting()
|
|
||||||
{
|
|
||||||
return new TranslatableComponent( this.translate, this.with );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the translation substitutions to be used in this component. Removes
|
* Sets the translation substitutions to be used in this component. Removes
|
||||||
* any previously set substitutions
|
* any previously set substitutions
|
||||||
|
@ -7,6 +7,7 @@ import com.google.gson.JsonDeserializer;
|
|||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
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;
|
||||||
@ -20,6 +21,7 @@ import java.util.HashSet;
|
|||||||
public class ComponentSerializer implements JsonDeserializer<BaseComponent>
|
public class ComponentSerializer implements JsonDeserializer<BaseComponent>
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private final static JsonParser JSON_PARSER = new JsonParser();
|
||||||
private final static Gson gson = new GsonBuilder().
|
private final static Gson gson = new GsonBuilder().
|
||||||
registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ).
|
registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ).
|
||||||
registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ).
|
registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ).
|
||||||
@ -33,14 +35,18 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
|
|||||||
|
|
||||||
public static BaseComponent[] parse(String json)
|
public static BaseComponent[] parse(String json)
|
||||||
{
|
{
|
||||||
if ( json.startsWith( "[" ) )
|
JsonElement jsonElement = JSON_PARSER.parse( json );
|
||||||
{ //Array
|
|
||||||
return gson.fromJson( json, BaseComponent[].class );
|
if ( jsonElement.isJsonArray() )
|
||||||
}
|
|
||||||
return new BaseComponent[]
|
|
||||||
{
|
{
|
||||||
gson.fromJson( json, BaseComponent.class )
|
return gson.fromJson( jsonElement, BaseComponent[].class );
|
||||||
};
|
} else
|
||||||
|
{
|
||||||
|
return new BaseComponent[]
|
||||||
|
{
|
||||||
|
gson.fromJson( jsonElement, BaseComponent.class )
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toString(BaseComponent component)
|
public static String toString(BaseComponent component)
|
||||||
@ -50,7 +56,13 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
|
|||||||
|
|
||||||
public static String toString(BaseComponent... components)
|
public static String toString(BaseComponent... components)
|
||||||
{
|
{
|
||||||
return gson.toJson( new TextComponent( components ) );
|
if ( components.length == 1 )
|
||||||
|
{
|
||||||
|
return gson.toJson( components[0] );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
return gson.toJson( new TextComponent( components ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,6 +8,23 @@ import org.junit.Test;
|
|||||||
public class ComponentsTest
|
public class ComponentsTest
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testComponentFormatRetention()
|
||||||
|
{
|
||||||
|
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() ) );
|
||||||
|
|
||||||
|
TextComponent second = new TextComponent( " world" );
|
||||||
|
second.copyFormatting( first, ComponentBuilder.FormatRetention.ALL, true );
|
||||||
|
Assert.assertEquals( first.isBold(), second.isBold() );
|
||||||
|
Assert.assertEquals( first.getColor(), second.getColor() );
|
||||||
|
Assert.assertEquals( first.getClickEvent(), second.getClickEvent() );
|
||||||
|
Assert.assertEquals( first.getHoverEvent(), second.getHoverEvent() );
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBuilderClone()
|
public void testBuilderClone()
|
||||||
{
|
{
|
||||||
@ -17,6 +34,25 @@ public class ComponentsTest
|
|||||||
Assert.assertEquals( TextComponent.toLegacyText( builder.create() ), TextComponent.toLegacyText( cloned.create() ) );
|
Assert.assertEquals( TextComponent.toLegacyText( builder.create() ), TextComponent.toLegacyText( cloned.create() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuilderAppendMixedComponents()
|
||||||
|
{
|
||||||
|
ComponentBuilder builder = new ComponentBuilder( "Hello " );
|
||||||
|
TextComponent textComponent = new TextComponent( "world " );
|
||||||
|
TranslatableComponent translatableComponent = new TranslatableComponent( "item.swordGold.name" );
|
||||||
|
builder.append( new BaseComponent[] { // array based BaseComponent append
|
||||||
|
textComponent,
|
||||||
|
translatableComponent
|
||||||
|
} );
|
||||||
|
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() );
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBuilderAppend()
|
public void testBuilderAppend()
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user