From d3aaf3c14ac5d227861820b8ae769db89b6501e6 Mon Sep 17 00:00:00 2001 From: snowleo Date: Thu, 13 Oct 2011 01:39:52 +0200 Subject: New storage system for settings (WIP) --- Essentials/nbproject/project.properties | 9 +- .../com/earth2me/essentials/settings/Backup.java | 17 ++ .../com/earth2me/essentials/settings/General.java | 37 ++++ .../com/earth2me/essentials/settings/Location.java | 28 +++ .../com/earth2me/essentials/settings/Settings.java | 40 ++++ .../com/earth2me/essentials/storage/Comment.java | 16 ++ .../com/earth2me/essentials/storage/ListType.java | 14 ++ .../com/earth2me/essentials/storage/MapType.java | 14 ++ .../earth2me/essentials/storage/StorageObject.java | 244 +++++++++++++++++++++ .../test/com/earth2me/essentials/StorageTest.java | 34 +++ lib/lombok-0.10.1.jar | Bin 0 -> 1668835 bytes 11 files changed, 450 insertions(+), 3 deletions(-) create mode 100644 Essentials/src/com/earth2me/essentials/settings/Backup.java create mode 100644 Essentials/src/com/earth2me/essentials/settings/General.java create mode 100644 Essentials/src/com/earth2me/essentials/settings/Location.java create mode 100644 Essentials/src/com/earth2me/essentials/settings/Settings.java create mode 100644 Essentials/src/com/earth2me/essentials/storage/Comment.java create mode 100644 Essentials/src/com/earth2me/essentials/storage/ListType.java create mode 100644 Essentials/src/com/earth2me/essentials/storage/MapType.java create mode 100644 Essentials/src/com/earth2me/essentials/storage/StorageObject.java create mode 100644 Essentials/test/com/earth2me/essentials/StorageTest.java create mode 100644 lib/lombok-0.10.1.jar diff --git a/Essentials/nbproject/project.properties b/Essentials/nbproject/project.properties index db78855e6..17af87b61 100644 --- a/Essentials/nbproject/project.properties +++ b/Essentials/nbproject/project.properties @@ -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 diff --git a/Essentials/src/com/earth2me/essentials/settings/Backup.java b/Essentials/src/com/earth2me/essentials/settings/Backup.java new file mode 100644 index 000000000..1b59260db --- /dev/null +++ b/Essentials/src/com/earth2me/essentials/settings/Backup.java @@ -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; +} diff --git a/Essentials/src/com/earth2me/essentials/settings/General.java b/Essentials/src/com/earth2me/essentials/settings/General.java new file mode 100644 index 000000000..b88242cd6 --- /dev/null +++ b/Essentials/src/com/earth2me/essentials/settings/General.java @@ -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 locations = new HashMap(); +} diff --git a/Essentials/src/com/earth2me/essentials/settings/Location.java b/Essentials/src/com/earth2me/essentials/settings/Location.java new file mode 100644 index 000000000..0535fdf52 --- /dev/null +++ b/Essentials/src/com/earth2me/essentials/settings/Location.java @@ -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); + } +} diff --git a/Essentials/src/com/earth2me/essentials/settings/Settings.java b/Essentials/src/com/earth2me/essentials/settings/Settings.java new file mode 100644 index 000000000..1d3256c84 --- /dev/null +++ b/Essentials/src/com/earth2me/essentials/settings/Settings.java @@ -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 locations = new HashMap(); + @ListType + private List m_o_t_d = new ArrayList(); +} diff --git a/Essentials/src/com/earth2me/essentials/storage/Comment.java b/Essentials/src/com/earth2me/essentials/storage/Comment.java new file mode 100644 index 000000000..b43cec980 --- /dev/null +++ b/Essentials/src/com/earth2me/essentials/storage/Comment.java @@ -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 ""; +} \ No newline at end of file diff --git a/Essentials/src/com/earth2me/essentials/storage/ListType.java b/Essentials/src/com/earth2me/essentials/storage/ListType.java new file mode 100644 index 000000000..9bf6e2e64 --- /dev/null +++ b/Essentials/src/com/earth2me/essentials/storage/ListType.java @@ -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; +} diff --git a/Essentials/src/com/earth2me/essentials/storage/MapType.java b/Essentials/src/com/earth2me/essentials/storage/MapType.java new file mode 100644 index 000000000..dc5636315 --- /dev/null +++ b/Essentials/src/com/earth2me/essentials/storage/MapType.java @@ -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; +} \ No newline at end of file diff --git a/Essentials/src/com/earth2me/essentials/storage/StorageObject.java b/Essentials/src/com/earth2me/essentials/storage/StorageObject.java new file mode 100644 index 000000000..5840e33a7 --- /dev/null +++ b/Essentials/src/com/earth2me/essentials/storage/StorageObject.java @@ -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 clazz; + + protected StorageObject() + { + } + private static Map constructors = new HashMap(); + + public static T load(Class 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 classes = new HashSet(); + + prepareConstructor(constructor, classes, clazz); + return constructor; + } + + private static void prepareConstructor(final Constructor constructor, final Set 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 entry : ((Map)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)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(" "); + } + } +} diff --git a/Essentials/test/com/earth2me/essentials/StorageTest.java b/Essentials/test/com/earth2me/essentials/StorageTest.java new file mode 100644 index 000000000..6ab5980c1 --- /dev/null +++ b/Essentials/test/com/earth2me/essentials/StorageTest.java @@ -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); + } +} diff --git a/lib/lombok-0.10.1.jar b/lib/lombok-0.10.1.jar new file mode 100644 index 000000000..993ebc3c2 Binary files /dev/null and b/lib/lombok-0.10.1.jar differ -- cgit v1.2.3