2015-07-31 03:24:01 +10:00
package com.plotsquared.bukkit.chat ;
2015-06-24 07:44:44 +10:00
2015-07-31 03:24:01 +10:00
import static com.plotsquared.bukkit.chat.TextualComponent.rawText ;
2015-07-31 00:25:16 +10:00
2016-01-11 07:00:56 +11:00
import com.google.gson.JsonArray ;
import com.google.gson.JsonElement ;
import com.google.gson.JsonObject ;
import com.google.gson.JsonParser ;
import com.google.gson.stream.JsonWriter ;
import com.intellectualcrafters.configuration.serialization.ConfigurationSerializable ;
import com.intellectualcrafters.configuration.serialization.ConfigurationSerialization ;
import org.bukkit.Achievement ;
import org.bukkit.Bukkit ;
import org.bukkit.ChatColor ;
import org.bukkit.Material ;
import org.bukkit.Statistic ;
import org.bukkit.Statistic.Type ;
import org.bukkit.command.CommandSender ;
import org.bukkit.entity.EntityType ;
import org.bukkit.entity.Player ;
import org.bukkit.inventory.ItemStack ;
2015-07-31 00:25:16 +10:00
import java.io.IOException ;
import java.io.StringWriter ;
import java.lang.reflect.Constructor ;
import java.lang.reflect.Field ;
import java.lang.reflect.InvocationTargetException ;
import java.lang.reflect.Method ;
import java.lang.reflect.Modifier ;
import java.util.ArrayList ;
import java.util.Arrays ;
2016-01-11 07:00:56 +11:00
import java.util.Collections ;
2015-07-31 00:25:16 +10:00
import java.util.HashMap ;
import java.util.Iterator ;
import java.util.List ;
import java.util.Map ;
import java.util.logging.Level ;
2015-06-24 07:44:44 +10:00
/ * *
* Represents a formattable message . Such messages can use elements such as colors , formatting codes , hover and click data , and other features provided by the vanilla Minecraft < a href = " http://minecraft.gamepedia.com/Tellraw#Raw_JSON_Text " > JSON message formatter < / a > .
* This class allows plugins to emulate the functionality of the vanilla Minecraft < a href = " http://minecraft.gamepedia.com/Commands#tellraw " > tellraw command < / a > .
* < p >
* This class follows the builder pattern , allowing for method chaining .
* It is set up such that invocations of property - setting methods will affect the current editing component ,
2015-12-19 12:23:32 -05:00
* and a call to { @link # then ( String ) } or { @link # text ( TextualComponent ) } will append a new editing component to the end of the message ,
2015-06-24 07:44:44 +10:00
* optionally initializing it with text . Further property - setting method calls will affect that editing component .
* < / p >
* /
2015-09-13 14:04:31 +10:00
public class FancyMessage implements JsonRepresentedObject , Cloneable , Iterable < MessagePart > , ConfigurationSerializable {
static {
2015-09-11 20:09:22 +10:00
ConfigurationSerialization . registerClass ( FancyMessage . class ) ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
private List < MessagePart > messageParts ;
private String jsonString ;
private boolean dirty ;
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
private static Constructor < ? > nmsPacketPlayOutChatConstructor ;
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
@Override
2015-09-13 14:04:31 +10:00
public FancyMessage clone ( ) throws CloneNotSupportedException {
2015-09-11 20:09:22 +10:00
final FancyMessage instance = ( FancyMessage ) super . clone ( ) ;
2016-01-11 07:00:56 +11:00
instance . messageParts = new ArrayList < > ( messageParts . size ( ) ) ;
2015-09-13 14:04:31 +10:00
for ( int i = 0 ; i < messageParts . size ( ) ; i + + ) {
2015-09-11 20:09:22 +10:00
instance . messageParts . add ( i , messageParts . get ( i ) . clone ( ) ) ;
}
instance . dirty = false ;
instance . jsonString = null ;
return instance ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Creates a JSON message with text .
* @param firstPartText The existing text in the message .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage ( final String firstPartText ) {
2015-09-11 20:09:22 +10:00
this ( rawText ( firstPartText ) ) ;
}
2015-09-13 14:04:31 +10:00
public FancyMessage ( final TextualComponent firstPartText ) {
2016-01-11 07:00:56 +11:00
messageParts = new ArrayList < > ( ) ;
2015-09-11 20:09:22 +10:00
messageParts . add ( new MessagePart ( firstPartText ) ) ;
jsonString = null ;
dirty = false ;
2015-09-13 14:04:31 +10:00
if ( nmsPacketPlayOutChatConstructor = = null ) {
try {
2015-09-11 20:09:22 +10:00
nmsPacketPlayOutChatConstructor = Reflection . getNMSClass ( " PacketPlayOutChat " ) . getDeclaredConstructor ( Reflection . getNMSClass ( " IChatBaseComponent " ) ) ;
nmsPacketPlayOutChatConstructor . setAccessible ( true ) ;
2015-09-13 14:04:31 +10:00
} catch ( final NoSuchMethodException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . SEVERE , " Could not find Minecraft method or constructor. " , e ) ;
2015-09-13 14:04:31 +10:00
} catch ( final SecurityException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Could not access constructor. " , e ) ;
}
}
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Creates a JSON message without text .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage ( ) {
2015-09-11 20:09:22 +10:00
this ( ( TextualComponent ) null ) ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Sets the text of the current editing component to a value .
* @param text The new text of the current editing component .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage text ( final String text ) {
2015-09-11 20:09:22 +10:00
final MessagePart latest = latest ( ) ;
latest . text = rawText ( text ) ;
dirty = true ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Sets the text of the current editing component to a value .
* @param text The new text of the current editing component .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage text ( final TextualComponent text ) {
2015-09-11 20:09:22 +10:00
final MessagePart latest = latest ( ) ;
latest . text = text ;
dirty = true ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Sets the color of the current editing component to a value .
* @param color The new color of the current editing component .
* @return This builder instance .
* @exception IllegalArgumentException If the specified { @code ChatColor } enumeration value is not a color ( but a format value ) .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage color ( final ChatColor color ) {
2015-09-11 20:09:22 +10:00
latest ( ) . color = color ;
dirty = true ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Sets the stylization of the current editing component .
* @param styles The array of styles to apply to the editing component .
* @return This builder instance .
* @exception IllegalArgumentException If any of the enumeration values in the array do not represent formatters .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage style ( final ChatColor . . . styles ) {
for ( final ChatColor style : styles ) {
if ( ! style . isFormat ( ) ) {
throw new IllegalArgumentException ( style . name ( ) + " is not a style " ) ;
}
2015-09-11 20:09:22 +10:00
}
latest ( ) . styles . addAll ( Arrays . asList ( styles ) ) ;
dirty = true ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to instruct the client to open a file on the client side filesystem when the currently edited part of the { @code FancyMessage } is clicked .
* @param path The path of the file on the client filesystem .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage file ( final String path ) {
2015-09-11 20:09:22 +10:00
onClick ( " open_file " , path ) ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to instruct the client to open a webpage in the client ' s web browser when the currently edited part of the { @code FancyMessage } is clicked .
* @param url The URL of the page to open when the link is clicked .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage link ( final String url ) {
2015-09-11 20:09:22 +10:00
onClick ( " open_url " , url ) ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to instruct the client to replace the chat input box content with the specified string when the currently edited part of the { @code FancyMessage } is clicked .
* The client will not immediately send the command to the server to be executed unless the client player submits the command / chat message , usually with the enter key .
* @param command The text to display in the chat bar of the client .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage suggest ( final String command ) {
2015-09-11 20:09:22 +10:00
onClick ( " suggest_command " , command ) ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to instruct the client to append the chat input box content with the specified string when the currently edited part of the { @code FancyMessage } is SHIFT - CLICKED .
* The client will not immediately send the command to the server to be executed unless the client player submits the command / chat message , usually with the enter key .
* @param command The text to append to the chat bar of the client .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage insert ( final String command ) {
2015-09-11 20:09:22 +10:00
latest ( ) . insertionData = command ;
dirty = true ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to instruct the client to send the specified string to the server as a chat message when the currently edited part of the { @code FancyMessage } is clicked .
* The client < b > will < / b > immediately send the command to the server to be executed when the editing component is clicked .
* @param command The text to display in the chat bar of the client .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage command ( final String command ) {
2015-09-11 20:09:22 +10:00
onClick ( " run_command " , command ) ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display information about an achievement when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param name The name of the achievement to display , excluding the " achievement. " prefix .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage achievementTooltip ( final String name ) {
2015-09-11 20:09:22 +10:00
onHover ( " show_achievement " , new JsonString ( " achievement. " + name ) ) ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display information about an achievement when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param which The achievement to display .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage achievementTooltip ( final Achievement which ) {
try {
2015-09-11 20:09:22 +10:00
final Object achievement = Reflection . getMethod ( Reflection . getOBCClass ( " CraftStatistic " ) , " getNMSAchievement " , Achievement . class ) . invoke ( null , which ) ;
return achievementTooltip ( ( String ) Reflection . getField ( Reflection . getNMSClass ( " Achievement " ) , " name " ) . get ( achievement ) ) ;
2015-09-13 14:04:31 +10:00
} catch ( final IllegalAccessException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Could not access method. " , e ) ;
return this ;
2015-09-13 14:04:31 +10:00
} catch ( final IllegalArgumentException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Argument could not be passed. " , e ) ;
return this ;
2015-09-13 14:04:31 +10:00
} catch ( final InvocationTargetException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " A error has occured durring invoking of method. " , e ) ;
return this ;
}
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display information about a parameterless statistic when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param which The statistic to display .
* @return This builder instance .
* @exception IllegalArgumentException If the statistic requires a parameter which was not supplied .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage statisticTooltip ( final Statistic which ) {
2015-09-11 20:09:22 +10:00
final Type type = which . getType ( ) ;
2015-09-13 14:04:31 +10:00
if ( type ! = Type . UNTYPED ) {
throw new IllegalArgumentException ( " That statistic requires an additional " + type + " parameter! " ) ;
}
try {
2015-09-11 20:09:22 +10:00
final Object statistic = Reflection . getMethod ( Reflection . getOBCClass ( " CraftStatistic " ) , " getNMSStatistic " , Statistic . class ) . invoke ( null , which ) ;
return achievementTooltip ( ( String ) Reflection . getField ( Reflection . getNMSClass ( " Statistic " ) , " name " ) . get ( statistic ) ) ;
2015-09-13 14:04:31 +10:00
} catch ( final IllegalAccessException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Could not access method. " , e ) ;
return this ;
2015-09-13 14:04:31 +10:00
} catch ( final IllegalArgumentException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Argument could not be passed. " , e ) ;
return this ;
2015-09-13 14:04:31 +10:00
} catch ( final InvocationTargetException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " A error has occured durring invoking of method. " , e ) ;
return this ;
}
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display information about a statistic parameter with a material when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param which The statistic to display .
* @param item The sole material parameter to the statistic .
* @return This builder instance .
* @exception IllegalArgumentException If the statistic requires a parameter which was not supplied , or was supplied a parameter that was not required .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage statisticTooltip ( final Statistic which , final Material item ) {
2015-09-11 20:09:22 +10:00
final Type type = which . getType ( ) ;
2015-09-13 14:04:31 +10:00
if ( type = = Type . UNTYPED ) {
throw new IllegalArgumentException ( " That statistic needs no additional parameter! " ) ;
}
if ( ( ( type = = Type . BLOCK ) & & item . isBlock ( ) ) | | ( type = = Type . ENTITY ) ) {
throw new IllegalArgumentException ( " Wrong parameter type for that statistic - needs " + type + " ! " ) ;
}
try {
2015-09-11 20:09:22 +10:00
final Object statistic = Reflection . getMethod ( Reflection . getOBCClass ( " CraftStatistic " ) , " getMaterialStatistic " , Statistic . class , Material . class ) . invoke ( null , which , item ) ;
return achievementTooltip ( ( String ) Reflection . getField ( Reflection . getNMSClass ( " Statistic " ) , " name " ) . get ( statistic ) ) ;
2015-09-13 14:04:31 +10:00
} catch ( final IllegalAccessException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Could not access method. " , e ) ;
return this ;
2015-09-13 14:04:31 +10:00
} catch ( final IllegalArgumentException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Argument could not be passed. " , e ) ;
return this ;
2015-09-13 14:04:31 +10:00
} catch ( final InvocationTargetException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " A error has occured durring invoking of method. " , e ) ;
return this ;
}
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display information about a statistic parameter with an entity type when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param which The statistic to display .
* @param entity The sole entity type parameter to the statistic .
* @return This builder instance .
* @exception IllegalArgumentException If the statistic requires a parameter which was not supplied , or was supplied a parameter that was not required .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage statisticTooltip ( final Statistic which , final EntityType entity ) {
2015-09-11 20:09:22 +10:00
final Type type = which . getType ( ) ;
2015-09-13 14:04:31 +10:00
if ( type = = Type . UNTYPED ) {
throw new IllegalArgumentException ( " That statistic needs no additional parameter! " ) ;
}
if ( type ! = Type . ENTITY ) {
throw new IllegalArgumentException ( " Wrong parameter type for that statistic - needs " + type + " ! " ) ;
}
try {
2015-09-11 20:09:22 +10:00
final Object statistic = Reflection . getMethod ( Reflection . getOBCClass ( " CraftStatistic " ) , " getEntityStatistic " , Statistic . class , EntityType . class ) . invoke ( null , which , entity ) ;
return achievementTooltip ( ( String ) Reflection . getField ( Reflection . getNMSClass ( " Statistic " ) , " name " ) . get ( statistic ) ) ;
2015-09-13 14:04:31 +10:00
} catch ( final IllegalAccessException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Could not access method. " , e ) ;
return this ;
2015-09-13 14:04:31 +10:00
} catch ( final IllegalArgumentException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Argument could not be passed. " , e ) ;
return this ;
2015-09-13 14:04:31 +10:00
} catch ( final InvocationTargetException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " A error has occured durring invoking of method. " , e ) ;
return this ;
}
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display information about an item when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param itemJSON A string representing the JSON - serialized NBT data tag of an { @link ItemStack } .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage itemTooltip ( final String itemJSON ) {
2015-09-11 20:09:22 +10:00
onHover ( " show_item " , new JsonString ( itemJSON ) ) ; // Seems a bit hacky, considering we have a JSON object as a parameter
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display information about an item when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param itemStack The stack for which to display information .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage itemTooltip ( final ItemStack itemStack ) {
try {
2015-09-11 20:09:22 +10:00
final Object nmsItem = Reflection . getMethod ( Reflection . getOBCClass ( " inventory.CraftItemStack " ) , " asNMSCopy " , ItemStack . class ) . invoke ( null , itemStack ) ;
return itemTooltip ( Reflection . getMethod ( Reflection . getNMSClass ( " ItemStack " ) , " save " , Reflection . getNMSClass ( " NBTTagCompound " ) )
. invoke ( nmsItem , Reflection . getNMSClass ( " NBTTagCompound " ) . newInstance ( ) ) . toString ( ) ) ;
2015-09-13 14:04:31 +10:00
} catch ( final Exception e ) {
2015-09-11 20:09:22 +10:00
e . printStackTrace ( ) ;
return this ;
}
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display raw text when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param text The text , which supports newlines , which will be displayed to the client upon hovering .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage tooltip ( final String text ) {
2015-09-11 20:09:22 +10:00
onHover ( " show_text " , new JsonString ( text ) ) ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display raw text when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param lines The lines of text which will be displayed to the client upon hovering . The iteration order of this object will be the order in which the lines of the tooltip are created .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage tooltip ( final Iterable < String > lines ) {
2015-09-11 20:09:22 +10:00
tooltip ( ArrayWrapper . toArray ( lines , String . class ) ) ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display raw text when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param lines The lines of text which will be displayed to the client upon hovering .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage tooltip ( final String . . . lines ) {
2015-09-11 20:09:22 +10:00
final StringBuilder builder = new StringBuilder ( ) ;
2015-09-13 14:04:31 +10:00
for ( int i = 0 ; i < lines . length ; i + + ) {
2015-09-11 20:09:22 +10:00
builder . append ( lines [ i ] ) ;
2015-09-13 14:04:31 +10:00
if ( i ! = ( lines . length - 1 ) ) {
2015-09-11 20:09:22 +10:00
builder . append ( '\n' ) ;
}
}
tooltip ( builder . toString ( ) ) ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display formatted text when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param text The formatted text which will be displayed to the client upon hovering .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage formattedTooltip ( final FancyMessage text ) {
for ( final MessagePart component : text . messageParts ) {
if ( ( component . clickActionData ! = null ) & & ( component . clickActionName ! = null ) ) {
2015-09-11 20:09:22 +10:00
throw new IllegalArgumentException ( " The tooltip text cannot have click data. " ) ;
2015-09-13 14:04:31 +10:00
} else if ( ( component . hoverActionData ! = null ) & & ( component . hoverActionName ! = null ) ) {
throw new IllegalArgumentException ( " The tooltip text cannot have a tooltip. " ) ;
2015-09-11 20:09:22 +10:00
}
}
onHover ( " show_text " , text ) ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display the specified lines of formatted text when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param lines The lines of formatted text which will be displayed to the client upon hovering .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage formattedTooltip ( final FancyMessage . . . lines ) {
if ( lines . length < 1 ) {
2015-09-11 20:09:22 +10:00
onHover ( null , null ) ; // Clear tooltip
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
final FancyMessage result = new FancyMessage ( ) ;
result . messageParts . clear ( ) ; // Remove the one existing text component that exists by default, which destabilizes the object
2015-09-13 14:04:31 +10:00
for ( int i = 0 ; i < lines . length ; i + + ) {
try {
for ( final MessagePart component : lines [ i ] ) {
if ( ( component . clickActionData ! = null ) & & ( component . clickActionName ! = null ) ) {
2015-09-11 20:09:22 +10:00
throw new IllegalArgumentException ( " The tooltip text cannot have click data. " ) ;
2015-09-13 14:04:31 +10:00
} else if ( ( component . hoverActionData ! = null ) & & ( component . hoverActionName ! = null ) ) {
throw new IllegalArgumentException ( " The tooltip text cannot have a tooltip. " ) ;
2015-09-11 20:09:22 +10:00
}
2015-09-13 14:04:31 +10:00
if ( component . hasText ( ) ) {
2015-09-11 20:09:22 +10:00
result . messageParts . add ( component . clone ( ) ) ;
}
2015-06-24 07:44:44 +10:00
}
2015-09-13 14:04:31 +10:00
if ( i ! = ( lines . length - 1 ) ) {
2015-09-11 20:09:22 +10:00
result . messageParts . add ( new MessagePart ( rawText ( " \ n " ) ) ) ;
2015-06-24 07:44:44 +10:00
}
2015-09-13 14:04:31 +10:00
} catch ( final CloneNotSupportedException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Failed to clone object " , e ) ;
return this ;
}
}
return formattedTooltip ( result . messageParts . isEmpty ( ) ? null : result ) ; // Throws NPE if size is 0, intended
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Set the behavior of the current editing component to display the specified lines of formatted text when the client hovers over the text .
* < p > Tooltips do not inherit display characteristics , such as color and styles , from the message component on which they are applied . < / p >
* @param lines The lines of text which will be displayed to the client upon hovering . The iteration order of this object will be the order in which the lines of the tooltip are created .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage formattedTooltip ( final Iterable < FancyMessage > lines ) {
2015-09-11 20:09:22 +10:00
return formattedTooltip ( ArrayWrapper . toArray ( lines , FancyMessage . class ) ) ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* If the text is a translatable key , and it has replaceable values , this function can be used to set the replacements that will be used in the message .
* @param replacements The replacements , in order , that will be used in the language - specific message .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage translationReplacements ( final String . . . replacements ) {
for ( final String str : replacements ) {
2015-09-11 20:09:22 +10:00
latest ( ) . translationReplacements . add ( new JsonString ( str ) ) ;
}
dirty = true ;
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ *
/ * *
* If the text is a translatable key , and it has replaceable values , this function can be used to set the replacements that will be used in the message .
* @param replacements The replacements , in order , that will be used in the language - specific message .
* @return This builder instance .
* //* ------------
public FancyMessage translationReplacements ( final Iterable < ? extends CharSequence > replacements ) {
for ( CharSequence str : replacements ) {
latest ( ) . translationReplacements . add ( new JsonString ( str ) ) ;
}
return this ;
}
2015-09-13 14:04:31 +10:00
* /
2015-09-11 20:09:22 +10:00
/ * *
* If the text is a translatable key , and it has replaceable values , this function can be used to set the replacements that will be used in the message .
* @param replacements The replacements , in order , that will be used in the language - specific message .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage translationReplacements ( final FancyMessage . . . replacements ) {
2016-01-11 07:00:56 +11:00
Collections . addAll ( latest ( ) . translationReplacements , replacements ) ;
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
dirty = true ;
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* If the text is a translatable key , and it has replaceable values , this function can be used to set the replacements that will be used in the message .
* @param replacements The replacements , in order , that will be used in the language - specific message .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage translationReplacements ( final Iterable < FancyMessage > replacements ) {
2015-09-11 20:09:22 +10:00
return translationReplacements ( ArrayWrapper . toArray ( replacements , FancyMessage . class ) ) ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Terminate construction of the current editing component , and begin construction of a new message component .
* After a successful call to this method , all setter methods will refer to a new message component , created as a result of the call to this method .
* @param text The text which will populate the new message component .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage then ( final String text ) {
2015-09-11 20:09:22 +10:00
return then ( rawText ( text ) ) ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Terminate construction of the current editing component , and begin construction of a new message component .
* After a successful call to this method , all setter methods will refer to a new message component , created as a result of the call to this method .
* @param text The text which will populate the new message component .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage then ( final TextualComponent text ) {
if ( ! latest ( ) . hasText ( ) ) {
throw new IllegalStateException ( " previous message part has no text " ) ;
}
2015-09-11 20:09:22 +10:00
messageParts . add ( new MessagePart ( text ) ) ;
dirty = true ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Terminate construction of the current editing component , and begin construction of a new message component .
* After a successful call to this method , all setter methods will refer to a new message component , created as a result of the call to this method .
* @return This builder instance .
* /
2015-09-13 14:04:31 +10:00
public FancyMessage then ( ) {
if ( ! latest ( ) . hasText ( ) ) {
throw new IllegalStateException ( " previous message part has no text " ) ;
}
2015-09-11 20:09:22 +10:00
messageParts . add ( new MessagePart ( ) ) ;
dirty = true ;
return this ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
@Override
2015-09-13 14:04:31 +10:00
public void writeJson ( final JsonWriter writer ) throws IOException {
if ( messageParts . size ( ) = = 1 ) {
2015-09-11 20:09:22 +10:00
latest ( ) . writeJson ( writer ) ;
2015-09-13 14:04:31 +10:00
} else {
2015-09-11 20:09:22 +10:00
writer . beginObject ( ) . name ( " text " ) . value ( " " ) . name ( " extra " ) . beginArray ( ) ;
2015-09-13 14:04:31 +10:00
for ( final MessagePart part : this ) {
2015-09-11 20:09:22 +10:00
part . writeJson ( writer ) ;
}
writer . endArray ( ) . endObject ( ) ;
}
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Serialize this fancy message , converting it into syntactically - valid JSON using a { @link JsonWriter } .
* This JSON should be compatible with vanilla formatter commands such as { @code / tellraw } .
* @return The JSON string representing this object .
* /
2015-09-13 14:04:31 +10:00
public String toJSONString ( ) {
if ( ! dirty & & ( jsonString ! = null ) ) {
return jsonString ;
}
2015-09-11 20:09:22 +10:00
final StringWriter string = new StringWriter ( ) ;
final JsonWriter json = new JsonWriter ( string ) ;
2015-09-13 14:04:31 +10:00
try {
2015-09-11 20:09:22 +10:00
writeJson ( json ) ;
json . close ( ) ;
2015-09-13 14:04:31 +10:00
} catch ( final IOException e ) {
2015-09-11 20:09:22 +10:00
throw new RuntimeException ( " invalid message " ) ;
}
jsonString = string . toString ( ) ;
dirty = false ;
return jsonString ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Sends this message to a player . The player will receive the fully - fledged formatted display of this message .
* @param player The player who will receive the message .
* /
2015-09-13 14:04:31 +10:00
public void send ( final Player player ) {
2015-09-11 20:09:22 +10:00
send ( player , toJSONString ( ) ) ;
}
2015-09-13 14:04:31 +10:00
private void send ( final CommandSender sender , final String jsonString ) {
if ( ! ( sender instanceof Player ) ) {
2015-09-11 20:09:22 +10:00
sender . sendMessage ( toOldMessageFormat ( ) ) ;
return ;
}
final Player player = ( Player ) sender ;
2015-09-13 14:04:31 +10:00
try {
2015-09-11 20:09:22 +10:00
final Object handle = Reflection . getHandle ( player ) ;
final Object connection = Reflection . getField ( handle . getClass ( ) , " playerConnection " ) . get ( handle ) ;
Reflection . getMethod ( connection . getClass ( ) , " sendPacket " , Reflection . getNMSClass ( " Packet " ) ) . invoke ( connection , createChatPacket ( jsonString ) ) ;
2015-09-13 14:04:31 +10:00
} catch ( final IllegalArgumentException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Argument could not be passed. " , e ) ;
2015-09-13 14:04:31 +10:00
} catch ( final IllegalAccessException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Could not access method. " , e ) ;
2015-09-13 14:04:31 +10:00
} catch ( final InstantiationException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Underlying class is abstract. " , e ) ;
2015-09-13 14:04:31 +10:00
} catch ( final InvocationTargetException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " A error has occured durring invoking of method. " , e ) ;
2015-09-13 14:04:31 +10:00
} catch ( final NoSuchMethodException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Could not find method. " , e ) ;
2015-09-13 14:04:31 +10:00
} catch ( final ClassNotFoundException e ) {
2015-09-11 20:09:22 +10:00
Bukkit . getLogger ( ) . log ( Level . WARNING , " Could not find class. " , e ) ;
}
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
// The ChatSerializer's instance of Gson
private static Object nmsChatSerializerGsonInstance ;
private static Method fromJsonMethod ;
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
private Object createChatPacket ( final String json ) throws IllegalArgumentException , IllegalAccessException , InstantiationException , InvocationTargetException , NoSuchMethodException ,
2015-09-13 14:04:31 +10:00
ClassNotFoundException {
if ( nmsChatSerializerGsonInstance = = null ) {
2015-09-11 20:09:22 +10:00
// Find the field and its value, completely bypassing obfuscation
Class < ? > chatSerializerClazz ;
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
final String version = Reflection . getVersion ( ) ;
final double majorVersion = Double . parseDouble ( version . replace ( '_' , '.' ) . substring ( 1 , 4 ) ) ;
final int lesserVersion = Integer . parseInt ( version . substring ( 6 , 7 ) ) ;
2015-09-13 14:04:31 +10:00
if ( ( majorVersion < 1 . 8 ) | | ( ( majorVersion = = 1 . 8 ) & & ( lesserVersion = = 1 ) ) ) {
2015-09-11 20:09:22 +10:00
chatSerializerClazz = Reflection . getNMSClass ( " ChatSerializer " ) ;
2015-09-13 14:04:31 +10:00
} else {
2015-09-11 20:09:22 +10:00
chatSerializerClazz = Reflection . getNMSClass ( " IChatBaseComponent$ChatSerializer " ) ;
}
2015-09-13 14:04:31 +10:00
if ( chatSerializerClazz = = null ) {
throw new ClassNotFoundException ( " Can't find the ChatSerializer class " ) ;
}
for ( final Field declaredField : chatSerializerClazz . getDeclaredFields ( ) ) {
if ( Modifier . isFinal ( declaredField . getModifiers ( ) ) & & Modifier . isStatic ( declaredField . getModifiers ( ) ) & & declaredField . getType ( ) . getName ( ) . endsWith ( " Gson " ) ) {
2015-09-11 20:09:22 +10:00
// We've found our field
declaredField . setAccessible ( true ) ;
nmsChatSerializerGsonInstance = declaredField . get ( null ) ;
fromJsonMethod = nmsChatSerializerGsonInstance . getClass ( ) . getMethod ( " fromJson " , String . class , Class . class ) ;
break ;
}
}
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
// Since the method is so simple, and all the obfuscated methods have the same name, it's easier to reimplement 'IChatBaseComponent a(String)' than to reflectively call it
// Of course, the implementation may change, but fuzzy matches might break with signature changes
final Object serializedChatComponent = fromJsonMethod . invoke ( nmsChatSerializerGsonInstance , json , Reflection . getNMSClass ( " IChatBaseComponent " ) ) ;
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
return nmsPacketPlayOutChatConstructor . newInstance ( serializedChatComponent ) ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Sends this message to a command sender .
* If the sender is a player , they will receive the fully - fledged formatted display of this message .
* Otherwise , they will receive a version of this message with less formatting .
* @param sender The command sender who will receive the message .
* @see # toOldMessageFormat ( )
* /
2015-09-13 14:04:31 +10:00
public void send ( final CommandSender sender ) {
2015-09-11 20:09:22 +10:00
send ( sender , toJSONString ( ) ) ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Sends this message to multiple command senders .
* @param senders The command senders who will receive the message .
* @see # send ( CommandSender )
* /
2015-09-13 14:04:31 +10:00
public void send ( final Iterable < ? extends CommandSender > senders ) {
2015-09-11 20:09:22 +10:00
final String string = toJSONString ( ) ;
2015-09-13 14:04:31 +10:00
for ( final CommandSender sender : senders ) {
2015-09-11 20:09:22 +10:00
send ( sender , string ) ;
}
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Convert this message to a human - readable string with limited formatting .
* This method is used to send this message to clients without JSON formatting support .
* < p >
* Serialization of this message by using this message will include ( in this order for each message part ) :
* < ol >
* < li > The color of each message part . < / li >
* < li > The applicable stylizations for each message part . < / li >
* < li > The core text of the message part . < / li >
* < / ol >
* The primary omissions are tooltips and clickable actions . Consequently , this method should be used only as a last resort .
* < / p >
* < p >
* Color and formatting can be removed from the returned string by using { @link ChatColor # stripColor ( String ) } . < / p >
* @return A human - readable string representing limited formatting in addition to the core text of this message .
* /
2015-09-13 14:04:31 +10:00
public String toOldMessageFormat ( ) {
2015-09-11 20:09:22 +10:00
final StringBuilder result = new StringBuilder ( ) ;
2015-09-13 14:04:31 +10:00
for ( final MessagePart part : this ) {
2015-09-11 20:09:22 +10:00
result . append ( part . color = = null ? " " : part . color ) ;
2015-09-13 14:04:31 +10:00
for ( final ChatColor formatSpecifier : part . styles ) {
2015-09-11 20:09:22 +10:00
result . append ( formatSpecifier ) ;
}
result . append ( part . text ) ;
}
return result . toString ( ) ;
}
2015-09-13 14:04:31 +10:00
private MessagePart latest ( ) {
2015-09-11 20:09:22 +10:00
return messageParts . get ( messageParts . size ( ) - 1 ) ;
}
2015-09-13 14:04:31 +10:00
private void onClick ( final String name , final String data ) {
2015-09-11 20:09:22 +10:00
final MessagePart latest = latest ( ) ;
latest . clickActionName = name ;
latest . clickActionData = data ;
dirty = true ;
}
2015-09-13 14:04:31 +10:00
private void onHover ( final String name , final JsonRepresentedObject data ) {
2015-09-11 20:09:22 +10:00
final MessagePart latest = latest ( ) ;
latest . hoverActionName = name ;
latest . hoverActionData = data ;
dirty = true ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
// Doc copied from interface
@Override
2015-09-13 14:04:31 +10:00
public Map < String , Object > serialize ( ) {
2016-01-11 07:00:56 +11:00
final HashMap < String , Object > map = new HashMap < > ( ) ;
2015-09-11 20:09:22 +10:00
map . put ( " messageParts " , messageParts ) ;
// map.put("JSON", toJSONString());
return map ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Deserializes a JSON - represented message from a mapping of key - value pairs .
* This is called by the Bukkit serialization API .
* It is not intended for direct public API consumption .
* @param serialized The key - value mapping which represents a fancy message .
* /
@SuppressWarnings ( " unchecked " )
2015-09-13 14:04:31 +10:00
public static FancyMessage deserialize ( final Map < String , Object > serialized ) {
2015-09-11 20:09:22 +10:00
final FancyMessage msg = new FancyMessage ( ) ;
msg . messageParts = ( List < MessagePart > ) serialized . get ( " messageParts " ) ;
msg . jsonString = serialized . containsKey ( " JSON " ) ? serialized . get ( " JSON " ) . toString ( ) : null ;
msg . dirty = ! serialized . containsKey ( " JSON " ) ;
return msg ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* < b > Internally called method . Not for API consumption . < / b >
* /
@Override
2015-09-13 14:04:31 +10:00
public Iterator < MessagePart > iterator ( ) {
2015-09-11 20:09:22 +10:00
return messageParts . iterator ( ) ;
}
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
private static JsonParser _stringParser = new JsonParser ( ) ;
2015-09-13 14:04:31 +10:00
2015-09-11 20:09:22 +10:00
/ * *
* Deserializes a fancy message from its JSON representation . This JSON representation is of the format of
* that returned by { @link # toJSONString ( ) } , and is compatible with vanilla inputs .
* @param json The JSON string which represents a fancy message .
* @return A { @code FancyMessage } representing the parameterized JSON message .
* /
2015-09-13 14:04:31 +10:00
public static FancyMessage deserialize ( final String json ) {
2015-09-11 20:09:22 +10:00
final JsonObject serialized = _stringParser . parse ( json ) . getAsJsonObject ( ) ;
final JsonArray extra = serialized . getAsJsonArray ( " extra " ) ; // Get the extra component
final FancyMessage returnVal = new FancyMessage ( ) ;
returnVal . messageParts . clear ( ) ;
2015-09-13 14:04:31 +10:00
for ( final JsonElement mPrt : extra ) {
2015-09-11 20:09:22 +10:00
final MessagePart component = new MessagePart ( ) ;
final JsonObject messagePart = mPrt . getAsJsonObject ( ) ;
2015-09-13 14:04:31 +10:00
for ( final Map . Entry < String , JsonElement > entry : messagePart . entrySet ( ) ) {
2015-09-11 20:09:22 +10:00
// Deserialize text
2015-09-13 14:04:31 +10:00
if ( TextualComponent . isTextKey ( entry . getKey ( ) ) ) {
2015-09-11 20:09:22 +10:00
// The map mimics the YAML serialization, which has a "key" field and one or more "value" fields
2016-01-11 07:00:56 +11:00
final Map < String , Object > serializedMapForm = new HashMap < > ( ) ; // Must be object due to Bukkit serializer API compliance
2015-09-11 20:09:22 +10:00
serializedMapForm . put ( " key " , entry . getKey ( ) ) ;
2015-09-13 14:04:31 +10:00
if ( entry . getValue ( ) . isJsonPrimitive ( ) ) {
2015-09-11 20:09:22 +10:00
// Assume string
serializedMapForm . put ( " value " , entry . getValue ( ) . getAsString ( ) ) ;
2015-09-13 14:04:31 +10:00
} else {
2015-09-11 20:09:22 +10:00
// Composite object, but we assume each element is a string
2015-09-13 14:04:31 +10:00
for ( final Map . Entry < String , JsonElement > compositeNestedElement : entry . getValue ( ) . getAsJsonObject ( ) . entrySet ( ) ) {
2015-09-11 20:09:22 +10:00
serializedMapForm . put ( " value. " + compositeNestedElement . getKey ( ) , compositeNestedElement . getValue ( ) . getAsString ( ) ) ;
}
}
component . text = TextualComponent . deserialize ( serializedMapForm ) ;
2015-09-13 14:04:31 +10:00
} else if ( MessagePart . stylesToNames . inverse ( ) . containsKey ( entry . getKey ( ) ) ) {
if ( entry . getValue ( ) . getAsBoolean ( ) ) {
2015-09-11 20:09:22 +10:00
component . styles . add ( MessagePart . stylesToNames . inverse ( ) . get ( entry . getKey ( ) ) ) ;
}
2015-09-13 14:04:31 +10:00
} else if ( entry . getKey ( ) . equals ( " color " ) ) {
2015-09-11 20:09:22 +10:00
component . color = ChatColor . valueOf ( entry . getValue ( ) . getAsString ( ) . toUpperCase ( ) ) ;
2015-09-13 14:04:31 +10:00
} else if ( entry . getKey ( ) . equals ( " clickEvent " ) ) {
2015-09-11 20:09:22 +10:00
final JsonObject object = entry . getValue ( ) . getAsJsonObject ( ) ;
component . clickActionName = object . get ( " action " ) . getAsString ( ) ;
component . clickActionData = object . get ( " value " ) . getAsString ( ) ;
2015-09-13 14:04:31 +10:00
} else if ( entry . getKey ( ) . equals ( " hoverEvent " ) ) {
2015-09-11 20:09:22 +10:00
final JsonObject object = entry . getValue ( ) . getAsJsonObject ( ) ;
component . hoverActionName = object . get ( " action " ) . getAsString ( ) ;
2015-09-13 14:04:31 +10:00
if ( object . get ( " value " ) . isJsonPrimitive ( ) ) {
2015-09-11 20:09:22 +10:00
// Assume string
component . hoverActionData = new JsonString ( object . get ( " value " ) . getAsString ( ) ) ;
2015-09-13 14:04:31 +10:00
} else {
2015-09-11 20:09:22 +10:00
// Assume composite type
// The only composite type we currently store is another FancyMessage
// Therefore, recursion time!
component . hoverActionData = deserialize ( object . get ( " value " ) . toString ( ) /* This should properly serialize the JSON object as a JSON string */ ) ;
}
2015-09-13 14:04:31 +10:00
} else if ( entry . getKey ( ) . equals ( " insertion " ) ) {
2015-09-11 20:09:22 +10:00
component . insertionData = entry . getValue ( ) . getAsString ( ) ;
2015-09-13 14:04:31 +10:00
} else if ( entry . getKey ( ) . equals ( " with " ) ) {
for ( final JsonElement object : entry . getValue ( ) . getAsJsonArray ( ) ) {
if ( object . isJsonPrimitive ( ) ) {
2015-09-11 20:09:22 +10:00
component . translationReplacements . add ( new JsonString ( object . getAsString ( ) ) ) ;
2015-09-13 14:04:31 +10:00
} else {
2015-09-11 20:09:22 +10:00
// Only composite type stored in this array is - again - FancyMessages
// Recurse within this function to parse this as a translation replacement
component . translationReplacements . add ( deserialize ( object . toString ( ) ) ) ;
}
}
}
}
returnVal . messageParts . add ( component ) ;
2015-06-24 07:44:44 +10:00
}
2015-09-11 20:09:22 +10:00
return returnVal ;
}
2015-06-24 07:44:44 +10:00
}