diff --git a/src/main/java/mkremins/fanciful/FancyMessage.java b/src/main/java/mkremins/fanciful/FancyMessage.java index f1a46d3..1e9ffe9 100644 --- a/src/main/java/mkremins/fanciful/FancyMessage.java +++ b/src/main/java/mkremins/fanciful/FancyMessage.java @@ -115,6 +115,11 @@ public FancyMessage text(String text) { return this; } + /** + * 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. + */ public FancyMessage text(TextualComponent text) { MessagePart latest = latest(); latest.text = text; @@ -126,6 +131,7 @@ public FancyMessage text(TextualComponent text) { * 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). */ public FancyMessage color(final ChatColor color) { if (!color.isColor()) { @@ -183,6 +189,18 @@ public FancyMessage suggest(final String command) { onClick("suggest_command", command); return this; } + + /** + * 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. + */ + public FancyMessage insert(final String command) { + latest().insertionData = command; + dirty = true; + return this; + } /** * 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. @@ -343,6 +361,7 @@ public FancyMessage itemTooltip(final ItemStack itemStack) { return this; } } + /** * Set the behavior of the current editing component to display raw text when the client hovers over the text. @@ -450,6 +469,60 @@ public FancyMessage formattedTooltip(final Iterable lines){ return formattedTooltip(ArrayWrapper.toArray(lines, FancyMessage.class)); } + /** + * 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 String... replacements){ + for(String str : replacements){ + latest().translationReplacements.add(new JsonString(str)); + } + dirty = true; + + return this; + } + /* + + /** + * 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 replacements){ + for(CharSequence str : replacements){ + latest().translationReplacements.add(new JsonString(str)); + } + + return this; + } + + */ + + /** + * 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 FancyMessage... replacements){ + for(FancyMessage str : replacements){ + latest().translationReplacements.add(str); + } + + dirty = true; + + return this; + } + + /** + * 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 replacements){ + return translationReplacements(ArrayWrapper.toArray(replacements, FancyMessage.class)); + } + /** * 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. @@ -733,6 +806,18 @@ public static FancyMessage deserialize(String json){ // Therefore, recursion time! component.hoverActionData = deserialize(object.get("value").toString() /* This should properly serialize the JSON object as a JSON string */); } + }else if(entry.getKey().equals("insertion")){ + component.insertionData = entry.getValue().getAsString(); + }else if(entry.getKey().equals("with")){ + for(JsonElement object : entry.getValue().getAsJsonArray()){ + if(object.isJsonPrimitive()){ + component.translationReplacements.add(new JsonString(object.getAsString())); + }else{ + // 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); diff --git a/src/main/java/mkremins/fanciful/JsonString.java b/src/main/java/mkremins/fanciful/JsonString.java index 8c5e7b7..f14a5cb 100644 --- a/src/main/java/mkremins/fanciful/JsonString.java +++ b/src/main/java/mkremins/fanciful/JsonString.java @@ -16,8 +16,8 @@ final class JsonString implements JsonRepresentedObject, ConfigurationSerializab private String _value; - public JsonString(String value){ - _value = value; + public JsonString(CharSequence value){ + _value = value == null ? null : value.toString(); } @Override diff --git a/src/main/java/mkremins/fanciful/MessagePart.java b/src/main/java/mkremins/fanciful/MessagePart.java index e837364..43818fb 100644 --- a/src/main/java/mkremins/fanciful/MessagePart.java +++ b/src/main/java/mkremins/fanciful/MessagePart.java @@ -26,6 +26,8 @@ final class MessagePart implements JsonRepresentedObject, ConfigurationSerializa hoverActionName = null; JsonRepresentedObject hoverActionData = null; TextualComponent text = null; + String insertionData = null; + ArrayList translationReplacements = new ArrayList(); MessagePart(final TextualComponent text){ this.text = text; @@ -49,6 +51,7 @@ public MessagePart clone() throws CloneNotSupportedException{ }else if(hoverActionData instanceof FancyMessage){ obj.hoverActionData = ((FancyMessage)hoverActionData).clone(); } + obj.translationReplacements = (ArrayList)translationReplacements.clone(); return obj; } @@ -100,6 +103,16 @@ public void writeJson(JsonWriter json) { hoverActionData.writeJson(json); json.endObject(); } + if(insertionData != null){ + json.name("insertion").value(insertionData); + } + if(translationReplacements.size() > 0 && text != null && TextualComponent.isTranslatableText(text)){ + json.name("with").beginArray(); + for(JsonRepresentedObject obj : translationReplacements){ + obj.writeJson(json); + } + json.endArray(); + } json.endObject(); } catch(IOException e){ Bukkit.getLogger().log(Level.WARNING, "A problem occured during writing of JSON string", e); @@ -115,6 +128,8 @@ public Map serialize() { map.put("hoverActionData", hoverActionData); map.put("clickActionName", clickActionName); map.put("clickActionData", clickActionData); + map.put("insertion", insertionData); + map.put("translationReplacements", translationReplacements); return map; } @@ -123,10 +138,12 @@ public static MessagePart deserialize(Map serialized){ MessagePart part = new MessagePart((TextualComponent)serialized.get("text")); part.styles = (ArrayList)serialized.get("styles"); part.color = ChatColor.getByChar(serialized.get("color").toString()); - part.hoverActionName = serialized.get("hoverActionName").toString(); + part.hoverActionName = (String)serialized.get("hoverActionName"); part.hoverActionData = (JsonRepresentedObject)serialized.get("hoverActionData"); - part.clickActionName = serialized.get("clickActionName").toString(); - part.clickActionData = serialized.get("clickActionData").toString(); + part.clickActionName = (String)serialized.get("clickActionName"); + part.clickActionData = (String)serialized.get("clickActionData"); + part.insertionData = (String)serialized.get("insertion"); + part.translationReplacements = (ArrayList)serialized.get("translationReplacements"); return part; } diff --git a/src/main/java/mkremins/fanciful/TextualComponent.java b/src/main/java/mkremins/fanciful/TextualComponent.java index daf45bc..d44d421 100644 --- a/src/main/java/mkremins/fanciful/TextualComponent.java +++ b/src/main/java/mkremins/fanciful/TextualComponent.java @@ -15,6 +15,7 @@ * Represents a textual component of a message part. * This can be used to not only represent string literals in a JSON message, * but also to represent localized strings and other text values. + *

Different instances of this class can be created with static constructor methods.

*/ public abstract class TextualComponent implements Cloneable { @@ -69,6 +70,10 @@ static boolean isTextKey(String key){ return key.equals("translate") || key.equals("text") || key.equals("score") || key.equals("selector"); } + static boolean isTranslatableText(TextualComponent component){ + return component instanceof ComplexTextTypeComponent && ((ComplexTextTypeComponent)component).getKey().equals("translate"); + } + /** * Internal class used to represent all types of text components. * Exception validating done is on keys and values.