mirror of
https://github.com/IntellectualSites/PlotSquared.git
synced 2025-07-05 23:24:43 +02:00
cleanup
This commit is contained in:
@ -16,61 +16,62 @@ import org.apache.commons.lang.Validate;
|
||||
* @param <E> The type of elements in the array.
|
||||
* @see Arrays
|
||||
*/
|
||||
public final class ArrayWrapper<E> {
|
||||
public final class ArrayWrapper<E>
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates an array wrapper with some elements.
|
||||
* @param elements The elements of the array.
|
||||
*/
|
||||
public ArrayWrapper(E... elements){
|
||||
setArray(elements);
|
||||
}
|
||||
|
||||
private E[] _array;
|
||||
|
||||
/**
|
||||
* Retrieves a reference to the wrapped array instance.
|
||||
* @return The array wrapped by this instance.
|
||||
*/
|
||||
public E[] getArray(){
|
||||
return _array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this wrapper to wrap a new array instance.
|
||||
* @param array The new wrapped array.
|
||||
*/
|
||||
public void setArray(E[] array){
|
||||
Validate.notNull(array, "The array must not be null.");
|
||||
_array = array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this object has a value equivalent to another object.
|
||||
* @see Arrays#equals(Object[], Object[])
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public boolean equals(Object other)
|
||||
/**
|
||||
* Creates an array wrapper with some elements.
|
||||
* @param elements The elements of the array.
|
||||
*/
|
||||
public ArrayWrapper(final E... elements)
|
||||
{
|
||||
if (!(other instanceof ArrayWrapper))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(_array, ((ArrayWrapper)other)._array);
|
||||
setArray(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hash code represented by this objects value.
|
||||
* @see Arrays#hashCode(Object[])
|
||||
* @return This object's hash code.
|
||||
*/
|
||||
private E[] _array;
|
||||
|
||||
/**
|
||||
* Retrieves a reference to the wrapped array instance.
|
||||
* @return The array wrapped by this instance.
|
||||
*/
|
||||
public E[] getArray()
|
||||
{
|
||||
return _array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this wrapper to wrap a new array instance.
|
||||
* @param array The new wrapped array.
|
||||
*/
|
||||
public void setArray(final E[] array)
|
||||
{
|
||||
Validate.notNull(array, "The array must not be null.");
|
||||
_array = array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this object has a value equivalent to another object.
|
||||
* @see Arrays#equals(Object[], Object[])
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public boolean equals(final Object other)
|
||||
{
|
||||
if (!(other instanceof ArrayWrapper)) { return false; }
|
||||
return Arrays.equals(_array, ((ArrayWrapper) other)._array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hash code represented by this objects value.
|
||||
* @see Arrays#hashCode(Object[])
|
||||
* @return This object's hash code.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Arrays.hashCode(_array);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts an iterable element collection to an array of elements.
|
||||
* The iteration order of the specified object will be used as the array element order.
|
||||
@ -78,29 +79,34 @@ public final class ArrayWrapper<E> {
|
||||
* @param c The type of the elements of the array.
|
||||
* @return An array of elements in the specified iterable.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T[] toArray(Iterable<? extends T> list, Class<T> c) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T[] toArray(final Iterable<? extends T> list, final Class<T> c)
|
||||
{
|
||||
int size = -1;
|
||||
if(list instanceof Collection<?>){
|
||||
@SuppressWarnings("rawtypes")
|
||||
Collection coll = (Collection)list;
|
||||
size = coll.size();
|
||||
if (list instanceof Collection<?>)
|
||||
{
|
||||
@SuppressWarnings("rawtypes")
|
||||
final Collection coll = (Collection) list;
|
||||
size = coll.size();
|
||||
}
|
||||
|
||||
|
||||
if(size < 0){
|
||||
size = 0;
|
||||
// Ugly hack: Count it ourselves
|
||||
for(@SuppressWarnings("unused") T element : list){
|
||||
size++;
|
||||
}
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
size = 0;
|
||||
// Ugly hack: Count it ourselves
|
||||
for (@SuppressWarnings("unused")
|
||||
final T element : list)
|
||||
{
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
T[] result = (T[]) Array.newInstance(c, size);
|
||||
|
||||
final T[] result = (T[]) Array.newInstance(c, size);
|
||||
int i = 0;
|
||||
for(T element : list){ // Assumes iteration order is consistent
|
||||
result[i++] = element; // Assign array element at index THEN increment counter
|
||||
}
|
||||
for (final T element : list)
|
||||
{ // Assumes iteration order is consistent
|
||||
result[i++] = element; // Assign array element at index THEN increment counter
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,13 +7,14 @@ import com.google.gson.stream.JsonWriter;
|
||||
/**
|
||||
* Represents an object that can be serialized to a JSON writer instance.
|
||||
*/
|
||||
interface JsonRepresentedObject {
|
||||
interface JsonRepresentedObject
|
||||
{
|
||||
|
||||
/**
|
||||
* Writes the JSON representation of this object to the specified writer.
|
||||
* @param writer The JSON writer which will receive the object.
|
||||
* @throws IOException If an error occurs writing to the stream.
|
||||
*/
|
||||
void writeJson(final JsonWriter writer) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes the JSON representation of this object to the specified writer.
|
||||
* @param writer The JSON writer which will receive the object.
|
||||
* @throws IOException If an error occurs writing to the stream.
|
||||
*/
|
||||
void writeJson(JsonWriter writer) throws IOException;
|
||||
|
||||
}
|
||||
|
@ -12,35 +12,43 @@ import com.intellectualcrafters.configuration.serialization.ConfigurationSeriali
|
||||
* Writes by this object will not write name values nor begin/end objects in the JSON stream.
|
||||
* All writes merely write the represented string value.
|
||||
*/
|
||||
final class JsonString implements JsonRepresentedObject, ConfigurationSerializable {
|
||||
final class JsonString implements JsonRepresentedObject, ConfigurationSerializable
|
||||
{
|
||||
|
||||
private String _value;
|
||||
|
||||
public JsonString(CharSequence value){
|
||||
_value = value == null ? null : value.toString();
|
||||
}
|
||||
private final String _value;
|
||||
|
||||
@Override
|
||||
public void writeJson(JsonWriter writer) throws IOException {
|
||||
writer.value(getValue());
|
||||
}
|
||||
|
||||
public String getValue(){
|
||||
return _value;
|
||||
}
|
||||
public JsonString(final CharSequence value)
|
||||
{
|
||||
_value = value == null ? null : value.toString();
|
||||
}
|
||||
|
||||
public Map<String, Object> serialize() {
|
||||
HashMap<String, Object> theSingleValue = new HashMap<String, Object>();
|
||||
theSingleValue.put("stringValue", _value);
|
||||
return theSingleValue;
|
||||
}
|
||||
|
||||
public static JsonString deserialize(Map<String, Object> map){
|
||||
return new JsonString(map.get("stringValue").toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return _value;
|
||||
}
|
||||
@Override
|
||||
public void writeJson(final JsonWriter writer) throws IOException
|
||||
{
|
||||
writer.value(getValue());
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize()
|
||||
{
|
||||
final HashMap<String, Object> theSingleValue = new HashMap<String, Object>();
|
||||
theSingleValue.put("stringValue", _value);
|
||||
return theSingleValue;
|
||||
}
|
||||
|
||||
public static JsonString deserialize(final Map<String, Object> map)
|
||||
{
|
||||
return new JsonString(map.get("stringValue").toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
@ -18,137 +18,167 @@ import com.intellectualcrafters.configuration.serialization.ConfigurationSeriali
|
||||
/**
|
||||
* Internal class: Represents a component of a JSON-serializable {@link FancyMessage}.
|
||||
*/
|
||||
final class MessagePart implements JsonRepresentedObject, ConfigurationSerializable, Cloneable {
|
||||
final class MessagePart implements JsonRepresentedObject, ConfigurationSerializable, Cloneable
|
||||
{
|
||||
|
||||
ChatColor color = ChatColor.WHITE;
|
||||
ArrayList<ChatColor> styles = new ArrayList<ChatColor>();
|
||||
String clickActionName = null, clickActionData = null,
|
||||
hoverActionName = null;
|
||||
JsonRepresentedObject hoverActionData = null;
|
||||
TextualComponent text = null;
|
||||
String insertionData = null;
|
||||
ArrayList<JsonRepresentedObject> translationReplacements = new ArrayList<JsonRepresentedObject>();
|
||||
ChatColor color = ChatColor.WHITE;
|
||||
ArrayList<ChatColor> styles = new ArrayList<ChatColor>();
|
||||
String clickActionName = null, clickActionData = null,
|
||||
hoverActionName = null;
|
||||
JsonRepresentedObject hoverActionData = null;
|
||||
TextualComponent text = null;
|
||||
String insertionData = null;
|
||||
ArrayList<JsonRepresentedObject> translationReplacements = new ArrayList<JsonRepresentedObject>();
|
||||
|
||||
MessagePart(final TextualComponent text){
|
||||
this.text = text;
|
||||
}
|
||||
MessagePart(final TextualComponent text)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
MessagePart() {
|
||||
this.text = null;
|
||||
}
|
||||
MessagePart()
|
||||
{
|
||||
text = null;
|
||||
}
|
||||
|
||||
boolean hasText() {
|
||||
return text != null;
|
||||
}
|
||||
boolean hasText()
|
||||
{
|
||||
return text != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public MessagePart clone() throws CloneNotSupportedException{
|
||||
MessagePart obj = (MessagePart)super.clone();
|
||||
obj.styles = (ArrayList<ChatColor>)styles.clone();
|
||||
if(hoverActionData instanceof JsonString){
|
||||
obj.hoverActionData = new JsonString(((JsonString)hoverActionData).getValue());
|
||||
}else if(hoverActionData instanceof FancyMessage){
|
||||
obj.hoverActionData = ((FancyMessage)hoverActionData).clone();
|
||||
}
|
||||
obj.translationReplacements = (ArrayList<JsonRepresentedObject>)translationReplacements.clone();
|
||||
return obj;
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public MessagePart clone() throws CloneNotSupportedException
|
||||
{
|
||||
final MessagePart obj = (MessagePart) super.clone();
|
||||
obj.styles = (ArrayList<ChatColor>) styles.clone();
|
||||
if (hoverActionData instanceof JsonString)
|
||||
{
|
||||
obj.hoverActionData = new JsonString(((JsonString) hoverActionData).getValue());
|
||||
}
|
||||
else if (hoverActionData instanceof FancyMessage)
|
||||
{
|
||||
obj.hoverActionData = ((FancyMessage) hoverActionData).clone();
|
||||
}
|
||||
obj.translationReplacements = (ArrayList<JsonRepresentedObject>) translationReplacements.clone();
|
||||
return obj;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static final BiMap<ChatColor, String> stylesToNames;
|
||||
static final BiMap<ChatColor, String> stylesToNames;
|
||||
|
||||
static{
|
||||
ImmutableBiMap.Builder<ChatColor, String> builder = ImmutableBiMap.builder();
|
||||
for (final ChatColor style : ChatColor.values()){
|
||||
if(!style.isFormat()){
|
||||
continue;
|
||||
}
|
||||
static
|
||||
{
|
||||
final ImmutableBiMap.Builder<ChatColor, String> builder = ImmutableBiMap.builder();
|
||||
for (final ChatColor style : ChatColor.values())
|
||||
{
|
||||
if (!style.isFormat())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String styleName;
|
||||
switch (style) {
|
||||
case MAGIC:
|
||||
styleName = "obfuscated"; break;
|
||||
case UNDERLINE:
|
||||
styleName = "underlined"; break;
|
||||
default:
|
||||
styleName = style.name().toLowerCase(); break;
|
||||
}
|
||||
|
||||
builder.put(style, styleName);
|
||||
}
|
||||
stylesToNames = builder.build();
|
||||
}
|
||||
String styleName;
|
||||
switch (style)
|
||||
{
|
||||
case MAGIC:
|
||||
styleName = "obfuscated";
|
||||
break;
|
||||
case UNDERLINE:
|
||||
styleName = "underlined";
|
||||
break;
|
||||
default:
|
||||
styleName = style.name().toLowerCase();
|
||||
break;
|
||||
}
|
||||
|
||||
public void writeJson(JsonWriter json) {
|
||||
try {
|
||||
json.beginObject();
|
||||
text.writeJson(json);
|
||||
json.name("color").value(color.name().toLowerCase());
|
||||
for (final ChatColor style : styles) {
|
||||
json.name(stylesToNames.get(style)).value(true);
|
||||
}
|
||||
if (clickActionName != null && clickActionData != null) {
|
||||
json.name("clickEvent")
|
||||
.beginObject()
|
||||
.name("action").value(clickActionName)
|
||||
.name("value").value(clickActionData)
|
||||
.endObject();
|
||||
}
|
||||
if (hoverActionName != null && hoverActionData != null) {
|
||||
json.name("hoverEvent")
|
||||
.beginObject()
|
||||
.name("action").value(hoverActionName)
|
||||
.name("value");
|
||||
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);
|
||||
}
|
||||
}
|
||||
builder.put(style, styleName);
|
||||
}
|
||||
stylesToNames = builder.build();
|
||||
}
|
||||
|
||||
public Map<String, Object> serialize() {
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("text", text);
|
||||
map.put("styles", styles);
|
||||
map.put("color", color.getChar());
|
||||
map.put("hoverActionName", hoverActionName);
|
||||
map.put("hoverActionData", hoverActionData);
|
||||
map.put("clickActionName", clickActionName);
|
||||
map.put("clickActionData", clickActionData);
|
||||
map.put("insertion", insertionData);
|
||||
map.put("translationReplacements", translationReplacements);
|
||||
return map;
|
||||
}
|
||||
@Override
|
||||
public void writeJson(final JsonWriter json)
|
||||
{
|
||||
try
|
||||
{
|
||||
json.beginObject();
|
||||
text.writeJson(json);
|
||||
json.name("color").value(color.name().toLowerCase());
|
||||
for (final ChatColor style : styles)
|
||||
{
|
||||
json.name(stylesToNames.get(style)).value(true);
|
||||
}
|
||||
if ((clickActionName != null) && (clickActionData != null))
|
||||
{
|
||||
json.name("clickEvent")
|
||||
.beginObject()
|
||||
.name("action").value(clickActionName)
|
||||
.name("value").value(clickActionData)
|
||||
.endObject();
|
||||
}
|
||||
if ((hoverActionName != null) && (hoverActionData != null))
|
||||
{
|
||||
json.name("hoverEvent")
|
||||
.beginObject()
|
||||
.name("action").value(hoverActionName)
|
||||
.name("value");
|
||||
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 (final JsonRepresentedObject obj : translationReplacements)
|
||||
{
|
||||
obj.writeJson(json);
|
||||
}
|
||||
json.endArray();
|
||||
}
|
||||
json.endObject();
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
Bukkit.getLogger().log(Level.WARNING, "A problem occured during writing of JSON string", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static MessagePart deserialize(Map<String, Object> serialized){
|
||||
MessagePart part = new MessagePart((TextualComponent)serialized.get("text"));
|
||||
part.styles = (ArrayList<ChatColor>)serialized.get("styles");
|
||||
part.color = ChatColor.getByChar(serialized.get("color").toString());
|
||||
part.hoverActionName = (String)serialized.get("hoverActionName");
|
||||
part.hoverActionData = (JsonRepresentedObject)serialized.get("hoverActionData");
|
||||
part.clickActionName = (String)serialized.get("clickActionName");
|
||||
part.clickActionData = (String)serialized.get("clickActionData");
|
||||
part.insertionData = (String)serialized.get("insertion");
|
||||
part.translationReplacements = (ArrayList<JsonRepresentedObject>)serialized.get("translationReplacements");
|
||||
return part;
|
||||
}
|
||||
@Override
|
||||
public Map<String, Object> serialize()
|
||||
{
|
||||
final HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("text", text);
|
||||
map.put("styles", styles);
|
||||
map.put("color", color.getChar());
|
||||
map.put("hoverActionName", hoverActionName);
|
||||
map.put("hoverActionData", hoverActionData);
|
||||
map.put("clickActionName", clickActionName);
|
||||
map.put("clickActionData", clickActionData);
|
||||
map.put("insertion", insertionData);
|
||||
map.put("translationReplacements", translationReplacements);
|
||||
return map;
|
||||
}
|
||||
|
||||
static{
|
||||
ConfigurationSerialization.registerClass(MessagePart.class);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public static MessagePart deserialize(final Map<String, Object> serialized)
|
||||
{
|
||||
final MessagePart part = new MessagePart((TextualComponent) serialized.get("text"));
|
||||
part.styles = (ArrayList<ChatColor>) serialized.get("styles");
|
||||
part.color = ChatColor.getByChar(serialized.get("color").toString());
|
||||
part.hoverActionName = (String) serialized.get("hoverActionName");
|
||||
part.hoverActionData = (JsonRepresentedObject) serialized.get("hoverActionData");
|
||||
part.clickActionName = (String) serialized.get("clickActionName");
|
||||
part.clickActionData = (String) serialized.get("clickActionData");
|
||||
part.insertionData = (String) serialized.get("insertion");
|
||||
part.translationReplacements = (ArrayList<JsonRepresentedObject>) serialized.get("translationReplacements");
|
||||
return part;
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
ConfigurationSerialization.registerClass(MessagePart.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,202 +12,227 @@ import org.bukkit.Bukkit;
|
||||
* A class containing static utility methods and caches which are intended as reflective conveniences.
|
||||
* Unless otherwise noted, upon failure methods will return {@code null}.
|
||||
*/
|
||||
public final class Reflection {
|
||||
public final class Reflection
|
||||
{
|
||||
|
||||
private static String _versionString;
|
||||
|
||||
private Reflection(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the version string from the package name of the CraftBukkit server implementation.
|
||||
* This is needed to bypass the JAR package name changing on each update.
|
||||
* @return The version string of the OBC and NMS packages, <em>including the trailing dot</em>.
|
||||
*/
|
||||
public synchronized static String getVersion() {
|
||||
if(_versionString == null){
|
||||
if(Bukkit.getServer() == null){
|
||||
// The server hasn't started, static initializer call?
|
||||
return null;
|
||||
}
|
||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
_versionString = name.substring(name.lastIndexOf('.') + 1) + ".";
|
||||
}
|
||||
|
||||
return _versionString;
|
||||
}
|
||||
private static String _versionString;
|
||||
|
||||
/**
|
||||
* Stores loaded classes from the {@code net.minecraft.server} package.
|
||||
*/
|
||||
private static final Map<String, Class<?>> _loadedNMSClasses = new HashMap<String, Class<?>>();
|
||||
/**
|
||||
* Stores loaded classes from the {@code org.bukkit.craftbukkit} package (and subpackages).
|
||||
*/
|
||||
private static final Map<String, Class<?>> _loadedOBCClasses = new HashMap<String, Class<?>>();
|
||||
|
||||
/**
|
||||
* Gets a {@link Class} object representing a type contained within the {@code net.minecraft.server} versioned package.
|
||||
* The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously).
|
||||
* @param className The name of the class, excluding the package, within NMS.
|
||||
* @return The class instance representing the specified NMS class, or {@code null} if it could not be loaded.
|
||||
*/
|
||||
public synchronized static Class<?> getNMSClass(String className) {
|
||||
if(_loadedNMSClasses.containsKey(className)){
|
||||
return _loadedNMSClasses.get(className);
|
||||
}
|
||||
|
||||
String fullName = "net.minecraft.server." + getVersion() + className;
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
clazz = Class.forName(fullName);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
_loadedNMSClasses.put(className, null);
|
||||
return null;
|
||||
}
|
||||
_loadedNMSClasses.put(className, clazz);
|
||||
return clazz;
|
||||
}
|
||||
private Reflection()
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets a {@link Class} object representing a type contained within the {@code org.bukkit.craftbukkit} versioned package.
|
||||
* The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously).
|
||||
* @param className The name of the class, excluding the package, within OBC. This name may contain a subpackage name, such as {@code inventory.CraftItemStack}.
|
||||
* @return The class instance representing the specified OBC class, or {@code null} if it could not be loaded.
|
||||
*/
|
||||
public synchronized static Class<?> getOBCClass(String className) {
|
||||
if(_loadedOBCClasses.containsKey(className)){
|
||||
return _loadedOBCClasses.get(className);
|
||||
}
|
||||
|
||||
String fullName = "org.bukkit.craftbukkit." + getVersion() + className;
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
clazz = Class.forName(fullName);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
_loadedOBCClasses.put(className, null);
|
||||
return null;
|
||||
}
|
||||
_loadedOBCClasses.put(className, clazz);
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to get the NMS handle of a CraftBukkit object.
|
||||
* <p>
|
||||
* The only match currently attempted by this method is a retrieval by using a parameterless {@code getHandle()} method implemented by the runtime type of the specified object.
|
||||
* </p>
|
||||
* @param obj The object for which to retrieve an NMS handle.
|
||||
* @return The NMS handle of the specified object, or {@code null} if it could not be retrieved using {@code getHandle()}.
|
||||
*/
|
||||
public synchronized static Object getHandle(Object obj) {
|
||||
try {
|
||||
return getMethod(obj.getClass(), "getHandle").invoke(obj);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Gets the version string from the package name of the CraftBukkit server implementation.
|
||||
* This is needed to bypass the JAR package name changing on each update.
|
||||
* @return The version string of the OBC and NMS packages, <em>including the trailing dot</em>.
|
||||
*/
|
||||
public synchronized static String getVersion()
|
||||
{
|
||||
if (_versionString == null)
|
||||
{
|
||||
if (Bukkit.getServer() == null)
|
||||
{
|
||||
// The server hasn't started, static initializer call?
|
||||
return null;
|
||||
}
|
||||
final String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
_versionString = name.substring(name.lastIndexOf('.') + 1) + ".";
|
||||
}
|
||||
|
||||
private static final Map<Class<?>, Map<String, Field>> _loadedFields = new HashMap<Class<?>, Map<String, Field>>();
|
||||
|
||||
/**
|
||||
* Retrieves a {@link Field} instance declared by the specified class with the specified name.
|
||||
* Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field
|
||||
* returned will be an instance or static field.
|
||||
* <p>
|
||||
* A global caching mechanism within this class is used to store fields. Combined with synchronization, this guarantees that
|
||||
* no field will be reflectively looked up twice.
|
||||
* </p>
|
||||
* <p>
|
||||
* If a field is deemed suitable for return, {@link Field#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned.
|
||||
* This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance.
|
||||
* </p>
|
||||
* @param clazz The class which contains the field to retrieve.
|
||||
* @param name The declared name of the field in the class.
|
||||
* @return A field object with the specified name declared by the specified class.
|
||||
* @see Class#getDeclaredField(String)
|
||||
*/
|
||||
public synchronized static Field getField(Class<?> clazz, String name) {
|
||||
Map<String, Field> loaded;
|
||||
if(!_loadedFields.containsKey(clazz)){
|
||||
loaded = new HashMap<String, Field>();
|
||||
_loadedFields.put(clazz, loaded);
|
||||
}else{
|
||||
loaded = _loadedFields.get(clazz);
|
||||
}
|
||||
if(loaded.containsKey(name)){
|
||||
// If the field is loaded (or cached as not existing), return the relevant value, which might be null
|
||||
return loaded.get(name);
|
||||
}
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
loaded.put(name, field);
|
||||
return field;
|
||||
} catch (Exception e) {
|
||||
// Error loading
|
||||
e.printStackTrace();
|
||||
// Cache field as not existing
|
||||
loaded.put(name, null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return _versionString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains loaded methods in a cache.
|
||||
* The map maps [types to maps of [method names to maps of [parameter types to method instances]]].
|
||||
*/
|
||||
private static final Map<Class<?>, Map<String, Map<ArrayWrapper<Class<?>>, Method>>> _loadedMethods = new HashMap<Class<?>, Map<String, Map<ArrayWrapper<Class<?>>, Method>>>();
|
||||
|
||||
/**
|
||||
* Retrieves a {@link Method} instance declared by the specified class with the specified name and argument types.
|
||||
* Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field
|
||||
* returned will be an instance or static field.
|
||||
* <p>
|
||||
* A global caching mechanism within this class is used to store method. Combined with synchronization, this guarantees that
|
||||
* no method will be reflectively looked up twice.
|
||||
* </p>
|
||||
* <p>
|
||||
* If a method is deemed suitable for return, {@link Method#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned.
|
||||
* This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance.
|
||||
* </p>
|
||||
* <p>
|
||||
* This method does <em>not</em> search superclasses of the specified type for methods with the specified signature.
|
||||
* Callers wishing this behavior should use {@link Class#getDeclaredMethod(String, Class...)}.
|
||||
* @param clazz The class which contains the method to retrieve.
|
||||
* @param name The declared name of the method in the class.
|
||||
* @param args The formal argument types of the method.
|
||||
* @return A method object with the specified name declared by the specified class.
|
||||
*/
|
||||
public synchronized static Method getMethod(Class<?> clazz, String name,
|
||||
Class<?>... args) {
|
||||
if(!_loadedMethods.containsKey(clazz)){
|
||||
_loadedMethods.put(clazz, new HashMap<String, Map<ArrayWrapper<Class<?>>, Method>>());
|
||||
}
|
||||
|
||||
Map<String, Map<ArrayWrapper<Class<?>>, Method>> loadedMethodNames = _loadedMethods.get(clazz);
|
||||
if(!loadedMethodNames.containsKey(name)){
|
||||
loadedMethodNames.put(name, new HashMap<ArrayWrapper<Class<?>>, Method>());
|
||||
}
|
||||
|
||||
Map<ArrayWrapper<Class<?>>, Method> loadedSignatures = loadedMethodNames.get(name);
|
||||
ArrayWrapper<Class<?>> wrappedArg = new ArrayWrapper<Class<?>>(args);
|
||||
if(loadedSignatures.containsKey(wrappedArg)){
|
||||
return loadedSignatures.get(wrappedArg);
|
||||
}
|
||||
|
||||
for (Method m : clazz.getMethods())
|
||||
if (m.getName().equals(name) && Arrays.equals(args, m.getParameterTypes())) {
|
||||
m.setAccessible(true);
|
||||
loadedSignatures.put(wrappedArg, m);
|
||||
return m;
|
||||
}
|
||||
loadedSignatures.put(wrappedArg, null);
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Stores loaded classes from the {@code net.minecraft.server} package.
|
||||
*/
|
||||
private static final Map<String, Class<?>> _loadedNMSClasses = new HashMap<String, Class<?>>();
|
||||
/**
|
||||
* Stores loaded classes from the {@code org.bukkit.craftbukkit} package (and subpackages).
|
||||
*/
|
||||
private static final Map<String, Class<?>> _loadedOBCClasses = new HashMap<String, Class<?>>();
|
||||
|
||||
/**
|
||||
* Gets a {@link Class} object representing a type contained within the {@code net.minecraft.server} versioned package.
|
||||
* The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously).
|
||||
* @param className The name of the class, excluding the package, within NMS.
|
||||
* @return The class instance representing the specified NMS class, or {@code null} if it could not be loaded.
|
||||
*/
|
||||
public synchronized static Class<?> getNMSClass(final String className)
|
||||
{
|
||||
if (_loadedNMSClasses.containsKey(className)) { return _loadedNMSClasses.get(className); }
|
||||
|
||||
final String fullName = "net.minecraft.server." + getVersion() + className;
|
||||
Class<?> clazz = null;
|
||||
try
|
||||
{
|
||||
clazz = Class.forName(fullName);
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
_loadedNMSClasses.put(className, null);
|
||||
return null;
|
||||
}
|
||||
_loadedNMSClasses.put(className, clazz);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link Class} object representing a type contained within the {@code org.bukkit.craftbukkit} versioned package.
|
||||
* The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously).
|
||||
* @param className The name of the class, excluding the package, within OBC. This name may contain a subpackage name, such as {@code inventory.CraftItemStack}.
|
||||
* @return The class instance representing the specified OBC class, or {@code null} if it could not be loaded.
|
||||
*/
|
||||
public synchronized static Class<?> getOBCClass(final String className)
|
||||
{
|
||||
if (_loadedOBCClasses.containsKey(className)) { return _loadedOBCClasses.get(className); }
|
||||
|
||||
final String fullName = "org.bukkit.craftbukkit." + getVersion() + className;
|
||||
Class<?> clazz = null;
|
||||
try
|
||||
{
|
||||
clazz = Class.forName(fullName);
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
_loadedOBCClasses.put(className, null);
|
||||
return null;
|
||||
}
|
||||
_loadedOBCClasses.put(className, clazz);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to get the NMS handle of a CraftBukkit object.
|
||||
* <p>
|
||||
* The only match currently attempted by this method is a retrieval by using a parameterless {@code getHandle()} method implemented by the runtime type of the specified object.
|
||||
* </p>
|
||||
* @param obj The object for which to retrieve an NMS handle.
|
||||
* @return The NMS handle of the specified object, or {@code null} if it could not be retrieved using {@code getHandle()}.
|
||||
*/
|
||||
public synchronized static Object getHandle(final Object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
return getMethod(obj.getClass(), "getHandle").invoke(obj);
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<Class<?>, Map<String, Field>> _loadedFields = new HashMap<Class<?>, Map<String, Field>>();
|
||||
|
||||
/**
|
||||
* Retrieves a {@link Field} instance declared by the specified class with the specified name.
|
||||
* Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field
|
||||
* returned will be an instance or static field.
|
||||
* <p>
|
||||
* A global caching mechanism within this class is used to store fields. Combined with synchronization, this guarantees that
|
||||
* no field will be reflectively looked up twice.
|
||||
* </p>
|
||||
* <p>
|
||||
* If a field is deemed suitable for return, {@link Field#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned.
|
||||
* This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance.
|
||||
* </p>
|
||||
* @param clazz The class which contains the field to retrieve.
|
||||
* @param name The declared name of the field in the class.
|
||||
* @return A field object with the specified name declared by the specified class.
|
||||
* @see Class#getDeclaredField(String)
|
||||
*/
|
||||
public synchronized static Field getField(final Class<?> clazz, final String name)
|
||||
{
|
||||
Map<String, Field> loaded;
|
||||
if (!_loadedFields.containsKey(clazz))
|
||||
{
|
||||
loaded = new HashMap<String, Field>();
|
||||
_loadedFields.put(clazz, loaded);
|
||||
}
|
||||
else
|
||||
{
|
||||
loaded = _loadedFields.get(clazz);
|
||||
}
|
||||
if (loaded.containsKey(name))
|
||||
{
|
||||
// If the field is loaded (or cached as not existing), return the relevant value, which might be null
|
||||
return loaded.get(name);
|
||||
}
|
||||
try
|
||||
{
|
||||
final Field field = clazz.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
loaded.put(name, field);
|
||||
return field;
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
// Error loading
|
||||
e.printStackTrace();
|
||||
// Cache field as not existing
|
||||
loaded.put(name, null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains loaded methods in a cache.
|
||||
* The map maps [types to maps of [method names to maps of [parameter types to method instances]]].
|
||||
*/
|
||||
private static final Map<Class<?>, Map<String, Map<ArrayWrapper<Class<?>>, Method>>> _loadedMethods = new HashMap<Class<?>, Map<String, Map<ArrayWrapper<Class<?>>, Method>>>();
|
||||
|
||||
/**
|
||||
* Retrieves a {@link Method} instance declared by the specified class with the specified name and argument types.
|
||||
* Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field
|
||||
* returned will be an instance or static field.
|
||||
* <p>
|
||||
* A global caching mechanism within this class is used to store method. Combined with synchronization, this guarantees that
|
||||
* no method will be reflectively looked up twice.
|
||||
* </p>
|
||||
* <p>
|
||||
* If a method is deemed suitable for return, {@link Method#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned.
|
||||
* This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance.
|
||||
* </p>
|
||||
* <p>
|
||||
* This method does <em>not</em> search superclasses of the specified type for methods with the specified signature.
|
||||
* Callers wishing this behavior should use {@link Class#getDeclaredMethod(String, Class...)}.
|
||||
* @param clazz The class which contains the method to retrieve.
|
||||
* @param name The declared name of the method in the class.
|
||||
* @param args The formal argument types of the method.
|
||||
* @return A method object with the specified name declared by the specified class.
|
||||
*/
|
||||
public synchronized static Method getMethod(final Class<?> clazz, final String name,
|
||||
final Class<?>... args)
|
||||
{
|
||||
if (!_loadedMethods.containsKey(clazz))
|
||||
{
|
||||
_loadedMethods.put(clazz, new HashMap<String, Map<ArrayWrapper<Class<?>>, Method>>());
|
||||
}
|
||||
|
||||
final Map<String, Map<ArrayWrapper<Class<?>>, Method>> loadedMethodNames = _loadedMethods.get(clazz);
|
||||
if (!loadedMethodNames.containsKey(name))
|
||||
{
|
||||
loadedMethodNames.put(name, new HashMap<ArrayWrapper<Class<?>>, Method>());
|
||||
}
|
||||
|
||||
final Map<ArrayWrapper<Class<?>>, Method> loadedSignatures = loadedMethodNames.get(name);
|
||||
final ArrayWrapper<Class<?>> wrappedArg = new ArrayWrapper<Class<?>>(args);
|
||||
if (loadedSignatures.containsKey(wrappedArg)) { return loadedSignatures.get(wrappedArg); }
|
||||
|
||||
for (final Method m : clazz.getMethods())
|
||||
{
|
||||
if (m.getName().equals(name) && Arrays.equals(args, m.getParameterTypes()))
|
||||
{
|
||||
m.setAccessible(true);
|
||||
loadedSignatures.put(wrappedArg, m);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
loadedSignatures.put(wrappedArg, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,276 +16,326 @@ import com.intellectualcrafters.configuration.serialization.ConfigurationSeriali
|
||||
* but also to represent localized strings and other text values.
|
||||
* <p>Different instances of this class can be created with static constructor methods.</p>
|
||||
*/
|
||||
public abstract class TextualComponent implements Cloneable {
|
||||
public abstract class TextualComponent implements Cloneable
|
||||
{
|
||||
|
||||
static{
|
||||
ConfigurationSerialization.registerClass(TextualComponent.ArbitraryTextTypeComponent.class);
|
||||
ConfigurationSerialization.registerClass(TextualComponent.ComplexTextTypeComponent.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getReadableString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The JSON key used to represent text components of this type.
|
||||
*/
|
||||
public abstract String getKey();
|
||||
|
||||
/**
|
||||
* @return A readable String
|
||||
*/
|
||||
public abstract String getReadableString();
|
||||
|
||||
/**
|
||||
* Clones a textual component instance.
|
||||
* The returned object should not reference this textual component instance, but should maintain the same key and value.
|
||||
*/
|
||||
@Override
|
||||
public abstract TextualComponent clone() throws CloneNotSupportedException;
|
||||
|
||||
/**
|
||||
* Writes the text data represented by this textual component to the specified JSON writer object.
|
||||
* A new object within the writer is not started.
|
||||
* @param writer The object to which to write the JSON data.
|
||||
* @throws IOException If an error occurs while writing to the stream.
|
||||
*/
|
||||
public abstract void writeJson(JsonWriter writer) throws IOException;
|
||||
|
||||
static TextualComponent deserialize(Map<String, Object> map){
|
||||
if(map.containsKey("key") && map.size() == 2 && map.containsKey("value")){
|
||||
// Arbitrary text component
|
||||
return ArbitraryTextTypeComponent.deserialize(map);
|
||||
}else if(map.size() >= 2 && map.containsKey("key") && !map.containsKey("value") /* It contains keys that START WITH value */){
|
||||
// Complex JSON object
|
||||
return ComplexTextTypeComponent.deserialize(map);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
private static final class ArbitraryTextTypeComponent extends TextualComponent implements ConfigurationSerializable {
|
||||
static
|
||||
{
|
||||
ConfigurationSerialization.registerClass(TextualComponent.ArbitraryTextTypeComponent.class);
|
||||
ConfigurationSerialization.registerClass(TextualComponent.ComplexTextTypeComponent.class);
|
||||
}
|
||||
|
||||
public ArbitraryTextTypeComponent(String key, String value){
|
||||
setKey(key);
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return _key;
|
||||
}
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return getReadableString();
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
Preconditions.checkArgument(key != null && !key.isEmpty(), "The key must be specified.");
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return _value;
|
||||
}
|
||||
/**
|
||||
* @return The JSON key used to represent text components of this type.
|
||||
*/
|
||||
public abstract String getKey();
|
||||
|
||||
public void setValue(String value) {
|
||||
Preconditions.checkArgument(value != null, "The value must be specified.");
|
||||
_value = value;
|
||||
}
|
||||
/**
|
||||
* @return A readable String
|
||||
*/
|
||||
public abstract String getReadableString();
|
||||
|
||||
private String _key;
|
||||
private String _value;
|
||||
|
||||
@Override
|
||||
public TextualComponent clone() throws CloneNotSupportedException {
|
||||
// Since this is a private and final class, we can just reinstantiate this class instead of casting super.clone
|
||||
return new ArbitraryTextTypeComponent(getKey(), getValue());
|
||||
}
|
||||
/**
|
||||
* Clones a textual component instance.
|
||||
* The returned object should not reference this textual component instance, but should maintain the same key and value.
|
||||
*/
|
||||
@Override
|
||||
public abstract TextualComponent clone() throws CloneNotSupportedException;
|
||||
|
||||
@Override
|
||||
public void writeJson(JsonWriter writer) throws IOException {
|
||||
writer.name(getKey()).value(getValue());
|
||||
}
|
||||
/**
|
||||
* Writes the text data represented by this textual component to the specified JSON writer object.
|
||||
* A new object within the writer is not started.
|
||||
* @param writer The object to which to write the JSON data.
|
||||
* @throws IOException If an error occurs while writing to the stream.
|
||||
*/
|
||||
public abstract void writeJson(final JsonWriter writer) throws IOException;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public Map<String, Object> serialize() {
|
||||
return new HashMap<String, Object>(){{
|
||||
put("key", getKey());
|
||||
put("value", getValue());
|
||||
}};
|
||||
}
|
||||
|
||||
public static ArbitraryTextTypeComponent deserialize(Map<String, Object> map){
|
||||
return new ArbitraryTextTypeComponent(map.get("key").toString(), map.get("value").toString());
|
||||
}
|
||||
static TextualComponent deserialize(final Map<String, Object> map)
|
||||
{
|
||||
if (map.containsKey("key") && (map.size() == 2) && map.containsKey("value"))
|
||||
{
|
||||
// Arbitrary text component
|
||||
return ArbitraryTextTypeComponent.deserialize(map);
|
||||
}
|
||||
else if ((map.size() >= 2) && map.containsKey("key") && !map.containsKey("value") /* It contains keys that START WITH value */)
|
||||
{
|
||||
// Complex JSON object
|
||||
return ComplexTextTypeComponent.deserialize(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReadableString() {
|
||||
return getValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class used to represent a text component with a nested JSON value.
|
||||
* Exception validating done is on keys and values.
|
||||
*/
|
||||
private static final class ComplexTextTypeComponent extends TextualComponent implements ConfigurationSerializable{
|
||||
return null;
|
||||
}
|
||||
|
||||
public ComplexTextTypeComponent(String key, Map<String, String> values){
|
||||
setKey(key);
|
||||
setValue(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return _key;
|
||||
}
|
||||
static boolean isTextKey(final String key)
|
||||
{
|
||||
return key.equals("translate") || key.equals("text") || key.equals("score") || key.equals("selector");
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
Preconditions.checkArgument(key != null && !key.isEmpty(), "The key must be specified.");
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public Map<String, String> getValue() {
|
||||
return _value;
|
||||
}
|
||||
static boolean isTranslatableText(final TextualComponent component)
|
||||
{
|
||||
return (component instanceof ComplexTextTypeComponent) && ((ComplexTextTypeComponent) component).getKey().equals("translate");
|
||||
}
|
||||
|
||||
public void setValue(Map<String, String> value) {
|
||||
Preconditions.checkArgument(value != null, "The value must be specified.");
|
||||
_value = value;
|
||||
}
|
||||
/**
|
||||
* Internal class used to represent all types of text components.
|
||||
* Exception validating done is on keys and values.
|
||||
*/
|
||||
private static final class ArbitraryTextTypeComponent extends TextualComponent implements ConfigurationSerializable
|
||||
{
|
||||
|
||||
private String _key;
|
||||
private Map<String, String> _value;
|
||||
|
||||
@Override
|
||||
public TextualComponent clone() throws CloneNotSupportedException {
|
||||
// Since this is a private and final class, we can just reinstantiate this class instead of casting super.clone
|
||||
return new ComplexTextTypeComponent(getKey(), getValue());
|
||||
}
|
||||
public ArbitraryTextTypeComponent(final String key, final String value)
|
||||
{
|
||||
setKey(key);
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeJson(JsonWriter writer) throws IOException {
|
||||
writer.name(getKey());
|
||||
writer.beginObject();
|
||||
for(Map.Entry<String, String> jsonPair : _value.entrySet()){
|
||||
writer.name(jsonPair.getKey()).value(jsonPair.getValue());
|
||||
}
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public Map<String, Object> serialize() {
|
||||
return new java.util.HashMap<String, Object>(){{
|
||||
put("key", getKey());
|
||||
for(Map.Entry<String, String> valEntry : getValue().entrySet()){
|
||||
put("value." + valEntry.getKey(), valEntry.getValue());
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
public static ComplexTextTypeComponent deserialize(Map<String, Object> map){
|
||||
String key = null;
|
||||
Map<String, String> value = new HashMap<String, String>();
|
||||
for(Map.Entry<String, Object> valEntry : map.entrySet()){
|
||||
if(valEntry.getKey().equals("key")){
|
||||
key = (String) valEntry.getValue();
|
||||
}else if(valEntry.getKey().startsWith("value.")){
|
||||
value.put(((String) valEntry.getKey()).substring(6) /* Strips out the value prefix */, valEntry.getValue().toString());
|
||||
}
|
||||
}
|
||||
return new ComplexTextTypeComponent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReadableString() {
|
||||
return getKey();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textual component representing a string literal.
|
||||
* This is the default type of textual component when a single string literal is given to a method.
|
||||
* @param textValue The text which will be represented.
|
||||
* @return The text component representing the specified literal text.
|
||||
*/
|
||||
public static TextualComponent rawText(String textValue){
|
||||
return new ArbitraryTextTypeComponent("text", textValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey()
|
||||
{
|
||||
return _key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textual component representing a localized string.
|
||||
* The client will see this text component as their localized version of the specified string <em>key</em>, which can be overridden by a resource pack.
|
||||
* <p>
|
||||
* If the specified translation key is not present on the client resource pack, the translation key will be displayed as a string literal to the client.
|
||||
* </p>
|
||||
* @param translateKey The string key which maps to localized text.
|
||||
* @return The text component representing the specified localized text.
|
||||
*/
|
||||
public static TextualComponent localizedText(String translateKey){
|
||||
return new ArbitraryTextTypeComponent("translate", translateKey);
|
||||
}
|
||||
|
||||
private static void throwUnsupportedSnapshot(){
|
||||
throw new UnsupportedOperationException("This feature is only supported in snapshot releases.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textual component representing a scoreboard value.
|
||||
* The client will see their own score for the specified objective as the text represented by this component.
|
||||
* <p>
|
||||
* <b>This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients.</b>
|
||||
* </p>
|
||||
* @param scoreboardObjective The name of the objective for which to display the score.
|
||||
* @return The text component representing the specified scoreboard score (for the viewing player), or {@code null} if an error occurs during JSON serialization.
|
||||
*/
|
||||
public static TextualComponent objectiveScore(String scoreboardObjective){
|
||||
return objectiveScore("*", scoreboardObjective);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textual component representing a scoreboard value.
|
||||
* The client will see the score of the specified player for the specified objective as the text represented by this component.
|
||||
* <p>
|
||||
* <b>This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients.</b>
|
||||
* </p>
|
||||
* @param playerName The name of the player whos score will be shown. If this string represents the single-character sequence "*", the viewing player's score will be displayed.
|
||||
* Standard minecraft selectors (@a, @p, etc) are <em>not</em> supported.
|
||||
* @param scoreboardObjective The name of the objective for which to display the score.
|
||||
* @return The text component representing the specified scoreboard score for the specified player, or {@code null} if an error occurs during JSON serialization.
|
||||
*/
|
||||
public static TextualComponent objectiveScore(String playerName, String scoreboardObjective){
|
||||
throwUnsupportedSnapshot(); // Remove this line when the feature is released to non-snapshot versions, in addition to updating ALL THE OVERLOADS documentation accordingly
|
||||
|
||||
return new ComplexTextTypeComponent("score", ImmutableMap.<String, String>builder()
|
||||
.put("name", playerName)
|
||||
.put("objective", scoreboardObjective)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textual component representing a player name, retrievable by using a standard minecraft selector.
|
||||
* The client will see the players or entities captured by the specified selector as the text represented by this component.
|
||||
* <p>
|
||||
* <b>This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients.</b>
|
||||
* </p>
|
||||
* @param selector The minecraft player or entity selector which will capture the entities whose string representations will be displayed in the place of this text component.
|
||||
* @return The text component representing the name of the entities captured by the selector.
|
||||
*/
|
||||
public static TextualComponent selector(String selector){
|
||||
throwUnsupportedSnapshot(); // Remove this line when the feature is released to non-snapshot versions, in addition to updating ALL THE OVERLOADS documentation accordingly
|
||||
|
||||
return new ArbitraryTextTypeComponent("selector", selector);
|
||||
}
|
||||
public void setKey(final String key)
|
||||
{
|
||||
Preconditions.checkArgument((key != null) && !key.isEmpty(), "The key must be specified.");
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
public void setValue(final String value)
|
||||
{
|
||||
Preconditions.checkArgument(value != null, "The value must be specified.");
|
||||
_value = value;
|
||||
}
|
||||
|
||||
private String _key;
|
||||
private String _value;
|
||||
|
||||
@Override
|
||||
public TextualComponent clone() throws CloneNotSupportedException
|
||||
{
|
||||
// Since this is a private and final class, we can just reinstantiate this class instead of casting super.clone
|
||||
return new ArbitraryTextTypeComponent(getKey(), getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeJson(final JsonWriter writer) throws IOException
|
||||
{
|
||||
writer.name(getKey()).value(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("serial")
|
||||
public Map<String, Object> serialize()
|
||||
{
|
||||
return new HashMap<String, Object>()
|
||||
{
|
||||
{
|
||||
put("key", getKey());
|
||||
put("value", getValue());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ArbitraryTextTypeComponent deserialize(final Map<String, Object> map)
|
||||
{
|
||||
return new ArbitraryTextTypeComponent(map.get("key").toString(), map.get("value").toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReadableString()
|
||||
{
|
||||
return getValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class used to represent a text component with a nested JSON value.
|
||||
* Exception validating done is on keys and values.
|
||||
*/
|
||||
private static final class ComplexTextTypeComponent extends TextualComponent implements ConfigurationSerializable
|
||||
{
|
||||
|
||||
public ComplexTextTypeComponent(final String key, final Map<String, String> values)
|
||||
{
|
||||
setKey(key);
|
||||
setValue(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey()
|
||||
{
|
||||
return _key;
|
||||
}
|
||||
|
||||
public void setKey(final String key)
|
||||
{
|
||||
Preconditions.checkArgument((key != null) && !key.isEmpty(), "The key must be specified.");
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public Map<String, String> getValue()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
public void setValue(final Map<String, String> value)
|
||||
{
|
||||
Preconditions.checkArgument(value != null, "The value must be specified.");
|
||||
_value = value;
|
||||
}
|
||||
|
||||
private String _key;
|
||||
private Map<String, String> _value;
|
||||
|
||||
@Override
|
||||
public TextualComponent clone() throws CloneNotSupportedException
|
||||
{
|
||||
// Since this is a private and final class, we can just reinstantiate this class instead of casting super.clone
|
||||
return new ComplexTextTypeComponent(getKey(), getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeJson(final JsonWriter writer) throws IOException
|
||||
{
|
||||
writer.name(getKey());
|
||||
writer.beginObject();
|
||||
for (final Map.Entry<String, String> jsonPair : _value.entrySet())
|
||||
{
|
||||
writer.name(jsonPair.getKey()).value(jsonPair.getValue());
|
||||
}
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("serial")
|
||||
public Map<String, Object> serialize()
|
||||
{
|
||||
return new java.util.HashMap<String, Object>()
|
||||
{
|
||||
{
|
||||
put("key", getKey());
|
||||
for (final Map.Entry<String, String> valEntry : getValue().entrySet())
|
||||
{
|
||||
put("value." + valEntry.getKey(), valEntry.getValue());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ComplexTextTypeComponent deserialize(final Map<String, Object> map)
|
||||
{
|
||||
String key = null;
|
||||
final Map<String, String> value = new HashMap<String, String>();
|
||||
for (final Map.Entry<String, Object> valEntry : map.entrySet())
|
||||
{
|
||||
if (valEntry.getKey().equals("key"))
|
||||
{
|
||||
key = (String) valEntry.getValue();
|
||||
}
|
||||
else if (valEntry.getKey().startsWith("value."))
|
||||
{
|
||||
value.put(valEntry.getKey().substring(6) /* Strips out the value prefix */, valEntry.getValue().toString());
|
||||
}
|
||||
}
|
||||
return new ComplexTextTypeComponent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReadableString()
|
||||
{
|
||||
return getKey();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textual component representing a string literal.
|
||||
* This is the default type of textual component when a single string literal is given to a method.
|
||||
* @param textValue The text which will be represented.
|
||||
* @return The text component representing the specified literal text.
|
||||
*/
|
||||
public static TextualComponent rawText(final String textValue)
|
||||
{
|
||||
return new ArbitraryTextTypeComponent("text", textValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textual component representing a localized string.
|
||||
* The client will see this text component as their localized version of the specified string <em>key</em>, which can be overridden by a resource pack.
|
||||
* <p>
|
||||
* If the specified translation key is not present on the client resource pack, the translation key will be displayed as a string literal to the client.
|
||||
* </p>
|
||||
* @param translateKey The string key which maps to localized text.
|
||||
* @return The text component representing the specified localized text.
|
||||
*/
|
||||
public static TextualComponent localizedText(final String translateKey)
|
||||
{
|
||||
return new ArbitraryTextTypeComponent("translate", translateKey);
|
||||
}
|
||||
|
||||
private static void throwUnsupportedSnapshot()
|
||||
{
|
||||
throw new UnsupportedOperationException("This feature is only supported in snapshot releases.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textual component representing a scoreboard value.
|
||||
* The client will see their own score for the specified objective as the text represented by this component.
|
||||
* <p>
|
||||
* <b>This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients.</b>
|
||||
* </p>
|
||||
* @param scoreboardObjective The name of the objective for which to display the score.
|
||||
* @return The text component representing the specified scoreboard score (for the viewing player), or {@code null} if an error occurs during JSON serialization.
|
||||
*/
|
||||
public static TextualComponent objectiveScore(final String scoreboardObjective)
|
||||
{
|
||||
return objectiveScore("*", scoreboardObjective);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textual component representing a scoreboard value.
|
||||
* The client will see the score of the specified player for the specified objective as the text represented by this component.
|
||||
* <p>
|
||||
* <b>This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients.</b>
|
||||
* </p>
|
||||
* @param playerName The name of the player whos score will be shown. If this string represents the single-character sequence "*", the viewing player's score will be displayed.
|
||||
* Standard minecraft selectors (@a, @p, etc) are <em>not</em> supported.
|
||||
* @param scoreboardObjective The name of the objective for which to display the score.
|
||||
* @return The text component representing the specified scoreboard score for the specified player, or {@code null} if an error occurs during JSON serialization.
|
||||
*/
|
||||
public static TextualComponent objectiveScore(final String playerName, final String scoreboardObjective)
|
||||
{
|
||||
throwUnsupportedSnapshot(); // Remove this line when the feature is released to non-snapshot versions, in addition to updating ALL THE OVERLOADS documentation accordingly
|
||||
|
||||
return new ComplexTextTypeComponent("score", ImmutableMap.<String, String> builder()
|
||||
.put("name", playerName)
|
||||
.put("objective", scoreboardObjective)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textual component representing a player name, retrievable by using a standard minecraft selector.
|
||||
* The client will see the players or entities captured by the specified selector as the text represented by this component.
|
||||
* <p>
|
||||
* <b>This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients.</b>
|
||||
* </p>
|
||||
* @param selector The minecraft player or entity selector which will capture the entities whose string representations will be displayed in the place of this text component.
|
||||
* @return The text component representing the name of the entities captured by the selector.
|
||||
*/
|
||||
public static TextualComponent selector(final String selector)
|
||||
{
|
||||
throwUnsupportedSnapshot(); // Remove this line when the feature is released to non-snapshot versions, in addition to updating ALL THE OVERLOADS documentation accordingly
|
||||
|
||||
return new ArbitraryTextTypeComponent("selector", selector);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user