Added new Configuration classes

By: Dinnerbone <dinnerbone@dinnerbone.com>
This commit is contained in:
Bukkit/Spigot
2011-09-19 20:36:44 +01:00
parent e99e39ee62
commit 6c7412d365
31 changed files with 3454 additions and 9 deletions

View File

@@ -0,0 +1,24 @@
package org.bukkit.configuration.serialization;
import java.util.Map;
/**
* Represents an object that may be serialized.
* <p>
* These objects MUST implement one of the following, in addition to the methods
* as defined by this interface:
* - A static method "deserialize" that accepts a single {@link Map<String, Object>} and returns the class.
* - A static method "valueOf" that accepts a single {@link Map<String, Object>} and returns the class.
* - A constructor that accepts a single {@link Map<String, Object>}.
*/
public interface ConfigurationSerializable {
/**
* Creates a Map representation of this class.
* <p>
* This class must provide a method to restore this class, as defined in the
* {@link ConfigurationSerializable} interface javadocs.
*
* @return Map containing the current state of this class
*/
public Map<String, Object> serialize();
}

View File

@@ -0,0 +1,251 @@
package org.bukkit.configuration.serialization;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
/**
* Utility class for storing and retrieving classes for {@link Configuration}.
*/
public class ConfigurationSerialization {
public static final String SERIALIZED_TYPE_KEY = "==";
private final Class<? extends ConfigurationSerializable> clazz;
private static Map<String, Class<? extends ConfigurationSerializable>> aliases = new HashMap<String, Class<? extends ConfigurationSerializable>>();
static {
registerClass(Vector.class);
registerClass(BlockVector.class);
}
protected ConfigurationSerialization(Class<? extends ConfigurationSerializable> clazz) {
this.clazz = clazz;
}
protected Method getMethod(String name, boolean isStatic) {
try {
Method method = clazz.getDeclaredMethod(name, Map.class);
if (!ConfigurationSerializable.class.isAssignableFrom(method.getReturnType())) {
return null;
}
if (Modifier.isStatic(method.getModifiers()) != isStatic) {
return null;
}
return method;
} catch (NoSuchMethodException ex) {
return null;
} catch (SecurityException ex) {
return null;
}
}
protected Constructor<? extends ConfigurationSerializable> getConstructor() {
try {
return clazz.getConstructor(Map.class);
} catch (NoSuchMethodException ex) {
return null;
} catch (SecurityException ex) {
return null;
}
}
protected ConfigurationSerializable deserializeViaMethod(Method method, Map<String, Object> args) {
try {
ConfigurationSerializable result = (ConfigurationSerializable)method.invoke(null, args);
if (result == null) {
Logger.getLogger(ConfigurationSerialization.class.getName()).log(Level.SEVERE, "Could not call method '" + method.toString() + "' of " + clazz + " for deserialization: method returned null");
} else {
return result;
}
} catch (Throwable ex) {
Logger.getLogger(ConfigurationSerialization.class.getName()).log(Level.SEVERE, "Could not call method '" + method.toString() + "' of " + clazz + " for deserialization", ex);
}
return null;
}
protected ConfigurationSerializable deserializeViaCtor(Constructor<? extends ConfigurationSerializable> ctor, Map<String, Object> args) {
try {
return ctor.newInstance(args);
} catch (Throwable ex) {
Logger.getLogger(ConfigurationSerialization.class.getName()).log(Level.SEVERE, "Could not call constructor '" + ctor.toString() + "' of " + clazz + " for deserialization", ex);
}
return null;
}
public ConfigurationSerializable deserialize(Map<String, Object> args) {
if (args == null) {
throw new IllegalArgumentException("Args must not be null");
}
ConfigurationSerializable result = null;
Method method = null;
if (result == null) {
method = getMethod("deserialize", true);
if (method != null) {
result = deserializeViaMethod(method, args);
}
}
if (result == null) {
method = getMethod("valueOf", true);
if (method != null) {
result = deserializeViaMethod(method, args);
}
}
if (result == null) {
Constructor<? extends ConfigurationSerializable> constructor = getConstructor();
if (constructor != null) {
result = deserializeViaCtor(constructor, args);
}
}
return result;
}
/**
* Attempts to deserialize the given arguments into a new instance of the given class.
* <p>
* The class must implement {@link ConfigurationSerializable}, including the extra methods
* as specified in the javadoc of ConfigurationSerializable.
* <p>
* If a new instance could not be made, an example being the class not fully implementing
* the interface, null will be returned.
*
* @param args Arguments for deserialization
* @param clazz Class to deserialize into
* @return New instance of the specified class
*/
public static ConfigurationSerializable deserializeObject(Map<String, Object> args, Class<? extends ConfigurationSerializable> clazz) {
return new ConfigurationSerialization(clazz).deserialize(args);
}
/**
* Attempts to deserialize the given arguments into a new instance of the given class.
* <p>
* The class must implement {@link ConfigurationSerializable}, including the extra methods
* as specified in the javadoc of ConfigurationSerializable.
* <p>
* If a new instance could not be made, an example being the class not fully implementing
* the interface, null will be returned.
*
* @param args Arguments for deserialization
* @return New instance of the specified class
*/
public static ConfigurationSerializable deserializeObject(Map<String, Object> args) {
Class<? extends ConfigurationSerializable> clazz = null;
if (args.containsKey(SERIALIZED_TYPE_KEY)) {
try {
String alias = (String)args.get(SERIALIZED_TYPE_KEY);
if (alias == null) {
throw new IllegalArgumentException("Specified class does not exist ('" + alias + ")'");
} else {
clazz = getClassByAlias(alias);
}
} catch (ClassCastException ex) {
ex.fillInStackTrace();
throw ex;
}
} else {
throw new IllegalArgumentException("Args doesn't contain type key ('" + SERIALIZED_TYPE_KEY + "')");
}
return new ConfigurationSerialization(clazz).deserialize(args);
}
/**
* Registers the given {@link ConfigurationSerializable} class by its alias
*
* @param clazz Class to register
*/
public static void registerClass(Class<? extends ConfigurationSerializable> clazz) {
DelegateDeserialization delegate = clazz.getAnnotation(DelegateDeserialization.class);
if (delegate == null ) {
registerClass(clazz, getAlias(clazz));
registerClass(clazz, clazz.getName());
}
}
/**
* Registers the given alias to the specified {@link ConfigurationSerializable} class
*
* @param clazz Class to register
* @param alias Alias to register as
*/
public static void registerClass(Class<? extends ConfigurationSerializable> clazz, String alias) {
aliases.put(alias, clazz);
}
/**
* Unregisters the specified alias to a {@link ConfigurationSerializable}
*
* @param alias Alias to unregister
*/
public static void unregisterClass(String alias) {
aliases.remove(alias);
}
/**
* Unregisters any aliases for the specified {@link ConfigurationSerializable} class
*
* @param clazz Class to unregister
*/
public static void unregisterClass(Class<? extends ConfigurationSerializable> clazz) {
while (aliases.values().remove(clazz));
}
/**
* Attempts to get a registered {@link ConfigurationSerializable} class by its alias
*
* @param alias Alias of the serializable
* @return Registered class, or null if not found
*/
public static Class<? extends ConfigurationSerializable> getClassByAlias(String alias) {
return aliases.get(alias);
}
/**
* Gets the correct alias for the given {@link ConfigurationSerializable} class
*
* @param clazz Class to get alias for
* @return Alias to use for the class
*/
public static String getAlias(Class<? extends ConfigurationSerializable> clazz) {
DelegateDeserialization delegate = clazz.getAnnotation(DelegateDeserialization.class);
if (delegate != null) {
if ((delegate.value() == null) || (delegate.value() == clazz)) {
delegate = null;
} else {
return getAlias(delegate.value());
}
}
if (delegate == null) {
SerializableAs alias = clazz.getAnnotation(SerializableAs.class);
if ((alias != null) && (alias.value() != null)) {
return alias.value();
}
}
return clazz.getName();
}
}

View File

@@ -0,0 +1,21 @@
package org.bukkit.configuration.serialization;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Represents a {@link ConfigurationSerializable} that will delegate all deserialization to another
* {@link ConfigurationSerializable}
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DelegateDeserialization {
/**
* Which class should be used as a delegate for this classes deserialization
*
* @return Delegate class
*/
public Class<? extends ConfigurationSerializable> value();
}

View File

@@ -0,0 +1,28 @@
package org.bukkit.configuration.serialization;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Represents an "alias" that a {@link ConfigurationSerializable} may be stored as.
* If this is not present on a {@link ConfigurationSerializable} class, it will use the
* fully qualified name of the class.
* <p>
* Using this annotation on any other class than a {@link ConfigurationSerializable} will
* have no effect.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SerializableAs {
/**
* This is the name your class will be stored and retrieved as.
* <p>
* This name MUST be unique. We recommend using names such as "MyPluginThing" instead of
* "Thing".
*
* @return Name to serialize the class as.
*/
public String value();
}