blob: fe0ff8b9835304a5a99847872ea8e2bd5084ea37 [file] [log] [blame] [raw]
package net.glowstone.util;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* Simple container for chat message structures until more advanced chat
* formatting is implemented.
*/
public final class TextMessage {
private final JSONObject object;
/**
* Construct a new chat message from a simple text string. Handles style
* and colors in the original string, converting them to the new format.
* @param text The text of the message.
*/
public TextMessage(String text) {
object = convert(text);
}
/**
* Construct a chat message from a JSON structure. No validation occurs.
* @param object The JSON structure of the message.
*/
public TextMessage(JSONObject object) {
Validate.notNull(object, "object must not be null");
this.object = object;
}
/**
* Encode this chat message to its textual JSON representation.
* @return The encoded representation.
*/
public String encode() {
return object.toJSONString();
}
/**
* Attempt to convert the message to its plaintext representation.
* @return The plain text, or the empty string on failure.
*/
public String asPlaintext() {
if (object.containsKey("text")) {
Object obj = object.get("text");
if (obj instanceof String) {
return (String) obj;
}
}
return "";
}
@Override
public String toString() {
return "Message" + encode();
}
/**
* Decode a chat message from its textual JSON representation if possible.
* @param json The encoded representation.
* @return The decoded TextMessage.
*/
public static TextMessage decode(String json) {
JSONParser parser = new JSONParser();
try {
Object o = parser.parse(json);
if (o instanceof JSONObject) {
return new TextMessage((JSONObject) o);
} else {
return new TextMessage(o.toString());
}
} catch (ParseException e) {
return new TextMessage("parse error");
}
}
/**
* Convert from an old-style to a new-style chat message.
* @param text The The text of the message.
* @return The converted JSON structure.
*/
@SuppressWarnings("unchecked")
private static JSONObject convert(String text) {
// state
final List<JSONObject> items = new LinkedList<>();
final Set<ChatColor> formatting = EnumSet.noneOf(ChatColor.class);
final StringBuilder current = new StringBuilder();
ChatColor color = null;
// work way through text, converting colors
for (int i = 0; i < text.length(); ++i) {
char ch = text.charAt(i);
if (ch != ChatColor.COLOR_CHAR) {
// no special handling
current.append(ch);
continue;
}
if (i == text.length() - 1) {
// ignore color character at end
continue;
}
// handle colors
append(items, current, color, formatting);
ChatColor code = ChatColor.getByChar(text.charAt(++i));
if (code == ChatColor.RESET) {
color = null;
formatting.clear();
} else if (code.isFormat()) {
formatting.add(code);
} else {
color = code;
formatting.clear();
}
}
append(items, current, color, formatting);
// convert list of items into structure
if (items.isEmpty()) {
// no items, return a blank message
JSONObject object = new JSONObject();
object.put("text", "");
return object;
} else if (items.size() == 1) {
// only one item, return it as-is
return items.get(0);
} else {
JSONObject object = items.get(0);
if (object.size() == 1) {
// only contains "text", no formatting, can reuse
object.put("extra", items.subList(1, items.size()));
} else {
// must put everything in the "extra" list
object = new JSONObject();
object.put("text", "");
object.put("extra", items);
}
return object;
}
}
@SuppressWarnings("unchecked")
private static void append(List<JSONObject> items, StringBuilder current, ChatColor color, Set<ChatColor> formatting) {
if (current.length() == 0) {
return;
}
JSONObject object = new JSONObject();
object.put("text", current.toString());
if (color != null) {
object.put("color", color.name().toLowerCase());
}
for (ChatColor format : formatting) {
if (format == ChatColor.MAGIC) {
object.put("obfuscated", true);
} else {
object.put(format.name().toLowerCase(), true);
}
}
current.setLength(0);
items.add(object);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TextMessage that = (TextMessage) o;
return object.equals(that.object);
}
@Override
public int hashCode() {
return object.hashCode();
}
}