New storage system for settings (WIP)

This commit is contained in:
snowleo 2011-10-13 01:39:52 +02:00
parent d732821e06
commit d3aaf3c14a
11 changed files with 450 additions and 3 deletions

View File

@ -1,6 +1,7 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.run.all.processors=true
annotation.processing.enabled.in.editor=true
annotation.processing.processors.list=lombok.core.AnnotationProcessor
annotation.processing.run.all.processors=false
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
application.title=Essentials
application.vendor=
@ -68,6 +69,7 @@ file.reference.iCo4.jar=../lib/iCo4.jar
file.reference.iCo5.jar=../lib/iCo5.jar
file.reference.iCo6.jar=../lib/iCo6.jar
file.reference.junit-4.5.jar=..\\lib\\junit_4\\junit-4.5.jar
file.reference.lombok-0.10.1.jar=../lib/lombok-0.10.1.jar
file.reference.MultiCurrency.jar=../lib/MultiCurrency.jar
file.reference.Permissions3.jar=../lib/Permissions3.jar
file.reference.PermissionsBukkit-1.2.jar=../lib/PermissionsBukkit-1.2.jar
@ -86,7 +88,8 @@ javac.classpath=\
${file.reference.BOSEconomy7.jar}:\
${file.reference.PermissionsEx.jar}:\
${file.reference.bPermissions.jar}:\
${file.reference.PermissionsBukkit-1.2.jar}
${file.reference.PermissionsBukkit-1.2.jar}:\
${file.reference.lombok-0.10.1.jar}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false

View File

@ -0,0 +1,17 @@
package com.earth2me.essentials.settings;
import com.earth2me.essentials.storage.Comment;
import com.earth2me.essentials.storage.StorageObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class Backup extends StorageObject
{
@Comment("Interval in minutes")
private long interval = 60;
@Comment("Add a command that backups your data, e.g. 'rdiff-backup World1 backups/World1'")
private String command;
}

View File

@ -0,0 +1,37 @@
package com.earth2me.essentials.settings;
import com.earth2me.essentials.storage.Comment;
import com.earth2me.essentials.storage.MapType;
import com.earth2me.essentials.storage.StorageObject;
import java.util.HashMap;
import java.util.Map;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class General extends StorageObject
{
public General()
{
super();
locations.put("Test", new Location());
locations.put("Test2", new Location());
}
private boolean debug = false;
private boolean signsDisabled = false;
private int test = 1;
private String test2 = "\tline1\nline2\nline3";
@Comment("Backup runs a command while saving is disabled")
private Backup backup = new Backup();
@Comment(
{
"Set the locale here, if you want to change the language of Essentials.",
"If this is not set, Essentials will use the language of your computer.",
"Available locales: da, de, en, fr, nl"
})
private String locale;
@MapType(Location.class)
private Map<String, Location> locations = new HashMap<String, Location>();
}

View File

@ -0,0 +1,28 @@
package com.earth2me.essentials.settings;
import com.earth2me.essentials.storage.StorageObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.bukkit.Server;
@Data
@EqualsAndHashCode(callSuper = false)
public class Location extends StorageObject
{
private String worldName = "Test";
private double x;
private double y;
private double z;
private Float yaw;
private Float pitch;
public org.bukkit.Location getBukkit(Server server)
{
if (yaw == null || pitch == null)
{
return new org.bukkit.Location(server.getWorld(worldName), x, y, z);
}
return new org.bukkit.Location(server.getWorld(worldName), x, y, z, yaw, pitch);
}
}

View File

@ -0,0 +1,40 @@
package com.earth2me.essentials.settings;
import com.earth2me.essentials.storage.Comment;
import com.earth2me.essentials.storage.ListType;
import com.earth2me.essentials.storage.MapType;
import com.earth2me.essentials.storage.StorageObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class Settings extends StorageObject
{
public Settings()
{
super();
locations.put("Test", new Location());
m_o_t_d.add("Welcome to the server!");
m_o_t_d.add("Have a nice day!\nwoooooo");
}
private boolean test;
private Boolean test2;
@Comment(
{
"Hello!",
"World"
})
private String yay = "null";
private String lol = "lol: 1";
private General general = new General();
@MapType(Location.class)
private Map<String, Location> locations = new HashMap<String, Location>();
@ListType
private List<String> m_o_t_d = new ArrayList<String>();
}

View File

@ -0,0 +1,16 @@
package com.earth2me.essentials.storage;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Comment
{
String[] value() default "";
}

View File

@ -0,0 +1,14 @@
package com.earth2me.essentials.storage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ListType
{
Class value() default String.class;
}

View File

@ -0,0 +1,14 @@
package com.earth2me.essentials.storage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MapType
{
Class value() default String.class;
}

View File

@ -0,0 +1,244 @@
package com.earth2me.essentials.storage;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
public class StorageObject
{
protected Class<? extends StorageObject> clazz;
protected StorageObject()
{
}
private static Map<Class, Constructor> constructors = new HashMap<Class, Constructor>();
public static <T extends StorageObject> T load(Class<? extends T> clazz, Reader reader)
{
Constructor constructor;
if (constructors.containsKey(clazz))
{
constructor = constructors.get(clazz);
}
else
{
constructor = prepareConstructor(clazz);
constructors.put(clazz, constructor);
}
final Yaml yaml = new Yaml(constructor);
T ret = (T)yaml.load(reader);
if (ret == null)
{
try
{
ret = (T)clazz.newInstance();
}
catch (InstantiationException ex)
{
Logger.getLogger(StorageObject.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IllegalAccessException ex)
{
Logger.getLogger(StorageObject.class.getName()).log(Level.SEVERE, null, ex);
}
}
ret.clazz = clazz;
return ret;
}
private static Constructor prepareConstructor(final Class<?> clazz)
{
final Constructor constructor = new Constructor(clazz);
final Set<Class> classes = new HashSet<Class>();
prepareConstructor(constructor, classes, clazz);
return constructor;
}
private static void prepareConstructor(final Constructor constructor, final Set<Class> classes, final Class clazz)
{
classes.add(clazz);
final TypeDescription description = new TypeDescription(clazz);
for (Field field : clazz.getDeclaredFields())
{
final ListType listType = field.getAnnotation(ListType.class);
if (listType != null)
{
description.putListPropertyType(field.getName(), listType.value());
if (StorageObject.class.isAssignableFrom(listType.value())
&& !classes.contains(listType.value()))
{
prepareConstructor(constructor, classes, listType.value());
}
}
final MapType mapType = field.getAnnotation(MapType.class);
if (mapType != null)
{
description.putMapPropertyType(field.getName(), String.class, mapType.value());
if (StorageObject.class.isAssignableFrom(mapType.value())
&& !classes.contains(mapType.value()))
{
prepareConstructor(constructor, classes, mapType.value());
}
}
if (StorageObject.class.isAssignableFrom(field.getType())
&& !classes.contains(field.getType()))
{
prepareConstructor(constructor, classes, field.getType());
}
}
constructor.addTypeDescription(description);
}
private transient Yaml yaml;
public void save(final PrintWriter writer)
{
final DumperOptions ops = new DumperOptions();
yaml = new Yaml(ops);
try
{
writeToFile(this, writer, 0, clazz);
}
catch (IllegalArgumentException ex)
{
Logger.getLogger(StorageObject.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IllegalAccessException ex)
{
Logger.getLogger(StorageObject.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void writeToFile(final Object object, final PrintWriter writer, final int depth, final Class clazz) throws IllegalArgumentException, IllegalAccessException
{
for (Field field : clazz.getDeclaredFields())
{
final int modifier = field.getModifiers();
if (Modifier.isPrivate(modifier) && !Modifier.isTransient(depth) && !Modifier.isStatic(depth))
{
field.setAccessible(true);
final boolean commentPresent = field.isAnnotationPresent(Comment.class);
final String name = field.getName();
if (commentPresent)
{
final Comment comments = field.getAnnotation(Comment.class);
for (String comment : comments.value())
{
final String trimmed = comment.trim();
if (trimmed.isEmpty())
{
continue;
}
writeIndention(writer, depth);
writer.print("# ");
writer.print(trimmed);
writer.println();
}
}
final Object data = field.get(object);
if (data == null && !commentPresent)
{
continue;
}
writeIndention(writer, depth);
if (data == null && commentPresent)
{
writer.print('#');
}
writer.print(name);
writer.print(": ");
if (data == null && commentPresent)
{
writer.println();
continue;
}
if (data instanceof StorageObject)
{
writer.println();
writeToFile(data, writer, depth + 1, data.getClass());
}
else if (data instanceof Map)
{
writer.println();
for (Entry<String, Object> entry : ((Map<String, Object>)data).entrySet())
{
final Object value = entry.getValue();
if (value != null)
{
writeIndention(writer, depth + 1);
writer.print(entry.getKey());
writer.print(": ");
if (value instanceof StorageObject)
{
writer.println();
writeToFile(value, writer, depth + 2, value.getClass());
}
else if (value instanceof String || value instanceof Boolean || value instanceof Number)
{
yaml.dumpAll(Collections.singletonList(value).iterator(), writer);
}
else
{
throw new UnsupportedOperationException();
}
}
}
}
else if (data instanceof Collection)
{
writer.println();
for (Object entry : (Collection<Object>)data)
{
if (entry != null)
{
writeIndention(writer, depth + 1);
writer.print("- ");
if (entry instanceof String || entry instanceof Boolean || entry instanceof Number)
{
yaml.dumpAll(Collections.singletonList(entry).iterator(), writer);
}
else
{
throw new UnsupportedOperationException();
}
}
}
}
else if (data instanceof String || data instanceof Boolean || data instanceof Number)
{
yaml.dumpAll(Collections.singletonList(data).iterator(), writer);
}
else
{
throw new UnsupportedOperationException();
}
}
}
}
private void writeIndention(final PrintWriter writer, final int depth)
{
for (int i = 0; i < depth; i++)
{
writer.print(" ");
}
}
}

View File

@ -0,0 +1,34 @@
package com.earth2me.essentials;
import junit.framework.TestCase;
import com.earth2me.essentials.settings.Settings;
import com.earth2me.essentials.storage.StorageObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import org.junit.Test;
public class StorageTest extends TestCase
{
@Test
public void testSettings()
{
assertTrue(StorageObject.class.isAssignableFrom(Settings.class));
final ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
final Reader reader = new InputStreamReader(bais);
final Settings settings = StorageObject.load(Settings.class, reader);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintWriter writer = new PrintWriter(baos);
settings.save(writer);
writer.close();
byte[] written = baos.toByteArray();
System.out.println(new String(written));
final ByteArrayInputStream bais2 = new ByteArrayInputStream(written);
final Reader reader2 = new InputStreamReader(bais2);
final Settings settings2 = StorageObject.load(Settings.class, reader2);
assertEquals("Default and rewritten config should be equal", settings, settings2);
}
}

BIN
lib/lombok-0.10.1.jar Normal file

Binary file not shown.