From ac85eb6db84353d5b4857a18eb31dc7b0e567f9f Mon Sep 17 00:00:00 2001 From: boy0001 Date: Sun, 5 Jul 2015 20:51:34 +1000 Subject: [PATCH] configuration stuff --- .../configuration/Configuration.java | 84 ++ .../configuration/ConfigurationOptions.java | 90 +++ .../configuration/ConfigurationSection.java | 642 +++++++++++++++ .../InvalidConfigurationException.java | 45 ++ .../configuration/MemoryConfiguration.java | 76 ++ .../MemoryConfigurationOptions.java | 28 + .../configuration/MemorySection.java | 753 ++++++++++++++++++ .../configuration/file/FileConfiguration.java | 287 +++++++ .../file/FileConfigurationOptions.java | 118 +++ .../configuration/file/YamlConfiguration.java | 254 ++++++ .../file/YamlConfigurationOptions.java | 69 ++ .../configuration/file/YamlConstructor.java | 49 ++ .../configuration/file/YamlRepresenter.java | 38 + .../ConfigurationSerializable.java | 35 + .../ConfigurationSerialization.java | 266 +++++++ .../DelegateDeserialization.java | 22 + .../serialization/SerializableAs.java | 34 + .../com/intellectualcrafters/plot/PS.java | 4 +- .../plot/api/PlotAPI.java | 2 +- .../plot/commands/MusicSubcommand.java | 21 +- .../plot/commands/Template.java | 4 +- .../plot/commands/Trim.java | 16 +- .../intellectualcrafters/plot/config/C.java | 15 + .../database/plotme/APlotMeConnector.java | 2 +- .../plotme/ClassicPlotMeConnector.java | 2 +- .../database/plotme/LikePlotMeConverter.java | 4 +- .../plot/generator/ClassicPlotWorld.java | 2 +- .../plot/generator/HybridPlotWorld.java | 2 +- .../plot/generator/SquarePlotWorld.java | 2 +- .../plot/listeners/PlotListener.java | 42 +- .../plot/listeners/PlotPlusListener.java | 1 - .../plot/object/PlotWorld.java | 2 +- .../plot/util/bukkit/Metrics.java | 4 +- .../plot/util/bukkit/chat/FancyMessage.java | 4 +- .../plot/util/bukkit/chat/JsonString.java | 2 +- .../plot/util/bukkit/chat/MessagePart.java | 4 +- .../util/bukkit/chat/TextualComponent.java | 4 +- .../translation/YamlTranslationFile.java | 2 +- 38 files changed, 2984 insertions(+), 47 deletions(-) create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/Configuration.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/ConfigurationOptions.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/ConfigurationSection.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/InvalidConfigurationException.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemoryConfiguration.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemoryConfigurationOptions.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemorySection.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/FileConfiguration.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/FileConfigurationOptions.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConfiguration.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConfigurationOptions.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConstructor.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlRepresenter.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/ConfigurationSerializable.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/ConfigurationSerialization.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/DelegateDeserialization.java create mode 100644 PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/SerializableAs.java diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/Configuration.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/Configuration.java new file mode 100644 index 000000000..a912b5323 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/Configuration.java @@ -0,0 +1,84 @@ +package com.intellectualcrafters.configuration; + +import java.util.Map; + +/** + * Represents a source of configurable options and settings + */ +public interface Configuration extends ConfigurationSection { + /** + * Sets the default value of the given path as provided. + *

+ * If no source {@link Configuration} was provided as a default + * collection, then a new {@link MemoryConfiguration} will be created to + * hold the new default value. + *

+ * If value is null, the value will be removed from the default + * Configuration source. + * + * @param path Path of the value to set. + * @param value Value to set the default to. + * @throws IllegalArgumentException Thrown if path is null. + */ + public void addDefault(String path, Object value); + + /** + * Sets the default values of the given paths as provided. + *

+ * If no source {@link Configuration} was provided as a default + * collection, then a new {@link MemoryConfiguration} will be created to + * hold the new default values. + * + * @param defaults A map of Path->Values to add to defaults. + * @throws IllegalArgumentException Thrown if defaults is null. + */ + public void addDefaults(Map defaults); + + /** + * Sets the default values of the given paths as provided. + *

+ * If no source {@link Configuration} was provided as a default + * collection, then a new {@link MemoryConfiguration} will be created to + * hold the new default value. + *

+ * This method will not hold a reference to the specified Configuration, + * nor will it automatically update if that Configuration ever changes. If + * you require this, you should set the default source with {@link + * #setDefaults(com.intellectualcrafters.configuration.Configuration)}. + * + * @param defaults A configuration holding a list of defaults to copy. + * @throws IllegalArgumentException Thrown if defaults is null or this. + */ + public void addDefaults(Configuration defaults); + + /** + * Sets the source of all default values for this {@link Configuration}. + *

+ * If a previous source was set, or previous default values were defined, + * then they will not be copied to the new source. + * + * @param defaults New source of default values for this configuration. + * @throws IllegalArgumentException Thrown if defaults is null or this. + */ + public void setDefaults(Configuration defaults); + + /** + * Gets the source {@link Configuration} for this configuration. + *

+ * If no configuration source was set, but default values were added, then + * a {@link MemoryConfiguration} will be returned. If no source was set + * and no defaults were set, then this method will return null. + * + * @return Configuration source for default values, or null if none exist. + */ + public Configuration getDefaults(); + + /** + * Gets the {@link ConfigurationOptions} for this {@link Configuration}. + *

+ * All setters through this method are chainable. + * + * @return Options for this configuration + */ + public ConfigurationOptions options(); +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/ConfigurationOptions.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/ConfigurationOptions.java new file mode 100644 index 000000000..4f32c6545 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/ConfigurationOptions.java @@ -0,0 +1,90 @@ +package com.intellectualcrafters.configuration; + +/** + * Various settings for controlling the input and output of a {@link + * Configuration} + */ +public class ConfigurationOptions { + private char pathSeparator = '.'; + private boolean copyDefaults = false; + private final Configuration configuration; + + protected ConfigurationOptions(Configuration configuration) { + this.configuration = configuration; + } + + /** + * Returns the {@link Configuration} that this object is responsible for. + * + * @return Parent configuration + */ + public Configuration configuration() { + return configuration; + } + + /** + * Gets the char that will be used to separate {@link + * ConfigurationSection}s + *

+ * This value does not affect how the {@link Configuration} is stored, + * only in how you access the data. The default value is '.'. + * + * @return Path separator + */ + public char pathSeparator() { + return pathSeparator; + } + + /** + * Sets the char that will be used to separate {@link + * ConfigurationSection}s + *

+ * This value does not affect how the {@link Configuration} is stored, + * only in how you access the data. The default value is '.'. + * + * @param value Path separator + * @return This object, for chaining + */ + public ConfigurationOptions pathSeparator(char value) { + this.pathSeparator = value; + return this; + } + + /** + * Checks if the {@link Configuration} should copy values from its default + * {@link Configuration} directly. + *

+ * If this is true, all values in the default Configuration will be + * directly copied, making it impossible to distinguish between values + * that were set and values that are provided by default. As a result, + * {@link ConfigurationSection#contains(java.lang.String)} will always + * return the same value as {@link + * ConfigurationSection#isSet(java.lang.String)}. The default value is + * false. + * + * @return Whether or not defaults are directly copied + */ + public boolean copyDefaults() { + return copyDefaults; + } + + /** + * Sets if the {@link Configuration} should copy values from its default + * {@link Configuration} directly. + *

+ * If this is true, all values in the default Configuration will be + * directly copied, making it impossible to distinguish between values + * that were set and values that are provided by default. As a result, + * {@link ConfigurationSection#contains(java.lang.String)} will always + * return the same value as {@link + * ConfigurationSection#isSet(java.lang.String)}. The default value is + * false. + * + * @param value Whether or not defaults are directly copied + * @return This object, for chaining + */ + public ConfigurationOptions copyDefaults(boolean value) { + this.copyDefaults = value; + return this; + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/ConfigurationSection.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/ConfigurationSection.java new file mode 100644 index 000000000..8ca1b4d12 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/ConfigurationSection.java @@ -0,0 +1,642 @@ +package com.intellectualcrafters.configuration; + +import java.util.Map; +import java.util.Set; +import java.util.List; + +/** + * Represents a section of a {@link Configuration} + */ +public interface ConfigurationSection { + /** + * Gets a set containing all keys in this section. + *

+ * If deep is set to true, then this will contain all the keys within any + * child {@link ConfigurationSection}s (and their children, etc). These + * will be in a valid path notation for you to use. + *

+ * If deep is set to false, then this will contain only the keys of any + * direct children, and not their own children. + * + * @param deep Whether or not to get a deep list, as opposed to a shallow + * list. + * @return Set of keys contained within this ConfigurationSection. + */ + public Set getKeys(boolean deep); + + /** + * Gets a Map containing all keys and their values for this section. + *

+ * If deep is set to true, then this will contain all the keys and values + * within any child {@link ConfigurationSection}s (and their children, + * etc). These keys will be in a valid path notation for you to use. + *

+ * If deep is set to false, then this will contain only the keys and + * values of any direct children, and not their own children. + * + * @param deep Whether or not to get a deep list, as opposed to a shallow + * list. + * @return Map of keys and values of this section. + */ + public Map getValues(boolean deep); + + /** + * Checks if this {@link ConfigurationSection} contains the given path. + *

+ * If the value for the requested path does not exist but a default value + * has been specified, this will return true. + * + * @param path Path to check for existence. + * @return True if this section contains the requested path, either via + * default or being set. + * @throws IllegalArgumentException Thrown when path is null. + */ + public boolean contains(String path); + + /** + * Checks if this {@link ConfigurationSection} has a value set for the + * given path. + *

+ * If the value for the requested path does not exist but a default value + * has been specified, this will still return false. + * + * @param path Path to check for existence. + * @return True if this section contains the requested path, regardless of + * having a default. + * @throws IllegalArgumentException Thrown when path is null. + */ + public boolean isSet(String path); + + /** + * Gets the path of this {@link ConfigurationSection} from its root {@link + * Configuration} + *

+ * For any {@link Configuration} themselves, this will return an empty + * string. + *

+ * If the section is no longer contained within its root for any reason, + * such as being replaced with a different value, this may return null. + *

+ * To retrieve the single name of this section, that is, the final part of + * the path returned by this method, you may use {@link #getName()}. + * + * @return Path of this section relative to its root + */ + public String getCurrentPath(); + + /** + * Gets the name of this individual {@link ConfigurationSection}, in the + * path. + *

+ * This will always be the final part of {@link #getCurrentPath()}, unless + * the section is orphaned. + * + * @return Name of this section + */ + public String getName(); + + /** + * Gets the root {@link Configuration} that contains this {@link + * ConfigurationSection} + *

+ * For any {@link Configuration} themselves, this will return its own + * object. + *

+ * If the section is no longer contained within its root for any reason, + * such as being replaced with a different value, this may return null. + * + * @return Root configuration containing this section. + */ + public Configuration getRoot(); + + /** + * Gets the parent {@link ConfigurationSection} that directly contains + * this {@link ConfigurationSection}. + *

+ * For any {@link Configuration} themselves, this will return null. + *

+ * If the section is no longer contained within its parent for any reason, + * such as being replaced with a different value, this may return null. + * + * @return Parent section containing this section. + */ + public ConfigurationSection getParent(); + + /** + * Gets the requested Object by path. + *

+ * If the Object does not exist but a default value has been specified, + * this will return the default value. If the Object does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the Object to get. + * @return Requested Object. + */ + public Object get(String path); + + /** + * Gets the requested Object by path, returning a default value if not + * found. + *

+ * If the Object does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the Object to get. + * @param def The default value to return if the path is not found. + * @return Requested Object. + */ + public Object get(String path, Object def); + + /** + * Sets the specified path to the given value. + *

+ * If value is null, the entry will be removed. Any existing entry will be + * replaced, regardless of what the new value is. + *

+ * Some implementations may have limitations on what you may store. See + * their individual javadocs for details. No implementations should allow + * you to store {@link Configuration}s or {@link ConfigurationSection}s, + * please use {@link #createSection(java.lang.String)} for that. + * + * @param path Path of the object to set. + * @param value New value to set the path to. + */ + public void set(String path, Object value); + + /** + * Creates an empty {@link ConfigurationSection} at the specified path. + *

+ * Any value that was previously set at this path will be overwritten. If + * the previous value was itself a {@link ConfigurationSection}, it will + * be orphaned. + * + * @param path Path to create the section at. + * @return Newly created section + */ + public ConfigurationSection createSection(String path); + + /** + * Creates a {@link ConfigurationSection} at the specified path, with + * specified values. + *

+ * Any value that was previously set at this path will be overwritten. If + * the previous value was itself a {@link ConfigurationSection}, it will + * be orphaned. + * + * @param path Path to create the section at. + * @param map The values to used. + * @return Newly created section + */ + public ConfigurationSection createSection(String path, Map map); + + // Primitives + /** + * Gets the requested String by path. + *

+ * If the String does not exist but a default value has been specified, + * this will return the default value. If the String does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the String to get. + * @return Requested String. + */ + public String getString(String path); + + /** + * Gets the requested String by path, returning a default value if not + * found. + *

+ * If the String does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the String to get. + * @param def The default value to return if the path is not found or is + * not a String. + * @return Requested String. + */ + public String getString(String path, String def); + + /** + * Checks if the specified path is a String. + *

+ * If the path exists but is not a String, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a String and return appropriately. + * + * @param path Path of the String to check. + * @return Whether or not the specified path is a String. + */ + public boolean isString(String path); + + /** + * Gets the requested int by path. + *

+ * If the int does not exist but a default value has been specified, this + * will return the default value. If the int does not exist and no default + * value was specified, this will return 0. + * + * @param path Path of the int to get. + * @return Requested int. + */ + public int getInt(String path); + + /** + * Gets the requested int by path, returning a default value if not found. + *

+ * If the int does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the int to get. + * @param def The default value to return if the path is not found or is + * not an int. + * @return Requested int. + */ + public int getInt(String path, int def); + + /** + * Checks if the specified path is an int. + *

+ * If the path exists but is not a int, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a int and return appropriately. + * + * @param path Path of the int to check. + * @return Whether or not the specified path is an int. + */ + public boolean isInt(String path); + + /** + * Gets the requested boolean by path. + *

+ * If the boolean does not exist but a default value has been specified, + * this will return the default value. If the boolean does not exist and + * no default value was specified, this will return false. + * + * @param path Path of the boolean to get. + * @return Requested boolean. + */ + public boolean getBoolean(String path); + + /** + * Gets the requested boolean by path, returning a default value if not + * found. + *

+ * If the boolean does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the boolean to get. + * @param def The default value to return if the path is not found or is + * not a boolean. + * @return Requested boolean. + */ + public boolean getBoolean(String path, boolean def); + + /** + * Checks if the specified path is a boolean. + *

+ * If the path exists but is not a boolean, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a boolean and return appropriately. + * + * @param path Path of the boolean to check. + * @return Whether or not the specified path is a boolean. + */ + public boolean isBoolean(String path); + + /** + * Gets the requested double by path. + *

+ * If the double does not exist but a default value has been specified, + * this will return the default value. If the double does not exist and no + * default value was specified, this will return 0. + * + * @param path Path of the double to get. + * @return Requested double. + */ + public double getDouble(String path); + + /** + * Gets the requested double by path, returning a default value if not + * found. + *

+ * If the double does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the double to get. + * @param def The default value to return if the path is not found or is + * not a double. + * @return Requested double. + */ + public double getDouble(String path, double def); + + /** + * Checks if the specified path is a double. + *

+ * If the path exists but is not a double, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a double and return appropriately. + * + * @param path Path of the double to check. + * @return Whether or not the specified path is a double. + */ + public boolean isDouble(String path); + + /** + * Gets the requested long by path. + *

+ * If the long does not exist but a default value has been specified, this + * will return the default value. If the long does not exist and no + * default value was specified, this will return 0. + * + * @param path Path of the long to get. + * @return Requested long. + */ + public long getLong(String path); + + /** + * Gets the requested long by path, returning a default value if not + * found. + *

+ * If the long does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the long to get. + * @param def The default value to return if the path is not found or is + * not a long. + * @return Requested long. + */ + public long getLong(String path, long def); + + /** + * Checks if the specified path is a long. + *

+ * If the path exists but is not a long, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a long and return appropriately. + * + * @param path Path of the long to check. + * @return Whether or not the specified path is a long. + */ + public boolean isLong(String path); + + // Java + /** + * Gets the requested List by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the List to get. + * @return Requested List. + */ + public List getList(String path); + + /** + * Gets the requested List by path, returning a default value if not + * found. + *

+ * If the List does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the List to get. + * @param def The default value to return if the path is not found or is + * not a List. + * @return Requested List. + */ + public List getList(String path, List def); + + /** + * Checks if the specified path is a List. + *

+ * If the path exists but is not a List, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a List and return appropriately. + * + * @param path Path of the List to check. + * @return Whether or not the specified path is a List. + */ + public boolean isList(String path); + + /** + * Gets the requested List of String by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a String if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of String. + */ + public List getStringList(String path); + + /** + * Gets the requested List of Integer by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Integer if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Integer. + */ + public List getIntegerList(String path); + + /** + * Gets the requested List of Boolean by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Boolean if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Boolean. + */ + public List getBooleanList(String path); + + /** + * Gets the requested List of Double by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Double if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Double. + */ + public List getDoubleList(String path); + + /** + * Gets the requested List of Float by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Float if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Float. + */ + public List getFloatList(String path); + + /** + * Gets the requested List of Long by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Long if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Long. + */ + public List getLongList(String path); + + /** + * Gets the requested List of Byte by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Byte if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Byte. + */ + public List getByteList(String path); + + /** + * Gets the requested List of Character by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Character if + * possible, but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Character. + */ + public List getCharacterList(String path); + + /** + * Gets the requested List of Short by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Short if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Short. + */ + public List getShortList(String path); + + /** + * Gets the requested List of Maps by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Map if possible, but + * may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Maps. + */ + public List> getMapList(String path); + + /** + * Gets the requested ConfigurationSection by path. + *

+ * If the ConfigurationSection does not exist but a default value has been + * specified, this will return the default value. If the + * ConfigurationSection does not exist and no default value was specified, + * this will return null. + * + * @param path Path of the ConfigurationSection to get. + * @return Requested ConfigurationSection. + */ + public ConfigurationSection getConfigurationSection(String path); + + /** + * Checks if the specified path is a ConfigurationSection. + *

+ * If the path exists but is not a ConfigurationSection, this will return + * false. If the path does not exist, this will return false. If the path + * does not exist but a default value has been specified, this will check + * if that default value is a ConfigurationSection and return + * appropriately. + * + * @param path Path of the ConfigurationSection to check. + * @return Whether or not the specified path is a ConfigurationSection. + */ + public boolean isConfigurationSection(String path); + + /** + * Gets the equivalent {@link ConfigurationSection} from the default + * {@link Configuration} defined in {@link #getRoot()}. + *

+ * If the root contains no defaults, or the defaults doesn't contain a + * value for this path, or the value at this path is not a {@link + * ConfigurationSection} then this will return null. + * + * @return Equivalent section in root configuration + */ + public ConfigurationSection getDefaultSection(); + + /** + * Sets the default value in the root at the given path as provided. + *

+ * If no source {@link Configuration} was provided as a default + * collection, then a new {@link MemoryConfiguration} will be created to + * hold the new default value. + *

+ * If value is null, the value will be removed from the default + * Configuration source. + *

+ * If the value as returned by {@link #getDefaultSection()} is null, then + * this will create a new section at the path, replacing anything that may + * have existed there previously. + * + * @param path Path of the value to set. + * @param value Value to set the default to. + * @throws IllegalArgumentException Thrown if path is null. + */ + public void addDefault(String path, Object value); +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/InvalidConfigurationException.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/InvalidConfigurationException.java new file mode 100644 index 000000000..38e9b0cb3 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/InvalidConfigurationException.java @@ -0,0 +1,45 @@ +package com.intellectualcrafters.configuration; + +/** + * Exception thrown when attempting to load an invalid {@link Configuration} + */ +@SuppressWarnings("serial") +public class InvalidConfigurationException extends Exception { + + /** + * Creates a new instance of InvalidConfigurationException without a + * message or cause. + */ + public InvalidConfigurationException() {} + + /** + * Constructs an instance of InvalidConfigurationException with the + * specified message. + * + * @param msg The details of the exception. + */ + public InvalidConfigurationException(String msg) { + super(msg); + } + + /** + * Constructs an instance of InvalidConfigurationException with the + * specified cause. + * + * @param cause The cause of the exception. + */ + public InvalidConfigurationException(Throwable cause) { + super(cause); + } + + /** + * Constructs an instance of InvalidConfigurationException with the + * specified message and cause. + * + * @param cause The cause of the exception. + * @param msg The details of the exception. + */ + public InvalidConfigurationException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemoryConfiguration.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemoryConfiguration.java new file mode 100644 index 000000000..ad81ab8f8 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemoryConfiguration.java @@ -0,0 +1,76 @@ +package com.intellectualcrafters.configuration; + +import java.util.Map; + +/** + * This is a {@link Configuration} implementation that does not save or load + * from any source, and stores all values in memory only. + * This is useful for temporary Configurations for providing defaults. + */ +public class MemoryConfiguration extends MemorySection implements Configuration { + protected Configuration defaults; + protected MemoryConfigurationOptions options; + + /** + * Creates an empty {@link MemoryConfiguration} with no default values. + */ + public MemoryConfiguration() {} + + /** + * Creates an empty {@link MemoryConfiguration} using the specified {@link + * Configuration} as a source for all default values. + * + * @param defaults Default value provider + * @throws IllegalArgumentException Thrown if defaults is null + */ + public MemoryConfiguration(Configuration defaults) { + this.defaults = defaults; + } + + @Override + public void addDefault(String path, Object value) { + if (path == null) throw new NullPointerException("Path may not be null"); + if (defaults == null) { + defaults = new MemoryConfiguration(); + } + + defaults.set(path, value); + } + + public void addDefaults(Map defaults) { + if (defaults == null) throw new NullPointerException("Defaults may not be null"); + + for (Map.Entry entry : defaults.entrySet()) { + addDefault(entry.getKey(), entry.getValue()); + } + } + + public void addDefaults(Configuration defaults) { + if (defaults == null) throw new NullPointerException("Defaults may not be null"); + + addDefaults(defaults.getValues(true)); + } + + public void setDefaults(Configuration defaults) { + if (defaults == null) throw new NullPointerException("Defaults may not be null"); + + this.defaults = defaults; + } + + public Configuration getDefaults() { + return defaults; + } + + @Override + public ConfigurationSection getParent() { + return null; + } + + public MemoryConfigurationOptions options() { + if (options == null) { + options = new MemoryConfigurationOptions(this); + } + + return options; + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemoryConfigurationOptions.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemoryConfigurationOptions.java new file mode 100644 index 000000000..1720118c6 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemoryConfigurationOptions.java @@ -0,0 +1,28 @@ +package com.intellectualcrafters.configuration; + +/** + * Various settings for controlling the input and output of a {@link + * MemoryConfiguration} + */ +public class MemoryConfigurationOptions extends ConfigurationOptions { + protected MemoryConfigurationOptions(MemoryConfiguration configuration) { + super(configuration); + } + + @Override + public MemoryConfiguration configuration() { + return (MemoryConfiguration) super.configuration(); + } + + @Override + public MemoryConfigurationOptions copyDefaults(boolean value) { + super.copyDefaults(value); + return this; + } + + @Override + public MemoryConfigurationOptions pathSeparator(char value) { + super.pathSeparator(value); + return this; + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemorySection.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemorySection.java new file mode 100644 index 000000000..b0cd19dab --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/MemorySection.java @@ -0,0 +1,753 @@ +package com.intellectualcrafters.configuration; + +import static org.bukkit.util.NumberConversions.*; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A type of {@link ConfigurationSection} that is stored in memory. + */ +public class MemorySection implements ConfigurationSection { + protected final Map map = new LinkedHashMap(); + private final Configuration root; + private final ConfigurationSection parent; + private final String path; + private final String fullPath; + + /** + * Creates an empty MemorySection for use as a root {@link Configuration} + * section. + *

+ * Note that calling this without being yourself a {@link Configuration} + * will throw an exception! + * + * @throws IllegalStateException Thrown if this is not a {@link + * Configuration} root. + */ + protected MemorySection() { + if (!(this instanceof Configuration)) { + throw new IllegalStateException("Cannot construct a root MemorySection when not a Configuration"); + } + + this.path = ""; + this.fullPath = ""; + this.parent = null; + this.root = (Configuration) this; + } + + /** + * Creates an empty MemorySection with the specified parent and path. + * + * @param parent Parent section that contains this own section. + * @param path Path that you may access this section from via the root + * {@link Configuration}. + * @throws IllegalArgumentException Thrown is parent or path is null, or + * if parent contains no root Configuration. + */ + protected MemorySection(ConfigurationSection parent, String path) { + if (parent == null) throw new NullPointerException("Parent may not be null"); + if (path == null) throw new NullPointerException("Path may not be null"); + + this.path = path; + this.parent = parent; + this.root = parent.getRoot(); + + if (root == null) throw new NullPointerException("Path may not be orphaned"); + + this.fullPath = createPath(parent, path); + } + + public Set getKeys(boolean deep) { + Set result = new LinkedHashSet(); + + Configuration root = getRoot(); + if (root != null && root.options().copyDefaults()) { + ConfigurationSection defaults = getDefaultSection(); + + if (defaults != null) { + result.addAll(defaults.getKeys(deep)); + } + } + + mapChildrenKeys(result, this, deep); + + return result; + } + + public Map getValues(boolean deep) { + Map result = new LinkedHashMap(); + + Configuration root = getRoot(); + if (root != null && root.options().copyDefaults()) { + ConfigurationSection defaults = getDefaultSection(); + + if (defaults != null) { + result.putAll(defaults.getValues(deep)); + } + } + + mapChildrenValues(result, this, deep); + + return result; + } + + public boolean contains(String path) { + return get(path) != null; + } + + public boolean isSet(String path) { + Configuration root = getRoot(); + if (root == null) { + return false; + } + if (root.options().copyDefaults()) { + return contains(path); + } + return get(path, null) != null; + } + + public String getCurrentPath() { + return fullPath; + } + + public String getName() { + return path; + } + + public Configuration getRoot() { + return root; + } + + public ConfigurationSection getParent() { + return parent; + } + + public void addDefault(String path, Object value) { + if (path == null) throw new NullPointerException("Path cannot be null"); + + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot add default without root"); + } + if (root == this) { + throw new UnsupportedOperationException("Unsupported addDefault(String, Object) implementation"); + } + root.addDefault(createPath(this, path), value); + } + + public ConfigurationSection getDefaultSection() { + Configuration root = getRoot(); + Configuration defaults = root == null ? null : root.getDefaults(); + + if (defaults != null) { + if (defaults.isConfigurationSection(getCurrentPath())) { + return defaults.getConfigurationSection(getCurrentPath()); + } + } + + return null; + } + + public void set(String path, Object value) { + if (path == null) throw new NullPointerException("Cannot set to an empty path"); + + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot use section without a root"); + } + + final char separator = root.options().pathSeparator(); + // i1 is the leading (higher) index + // i2 is the trailing (lower) index + int i1 = -1, i2; + ConfigurationSection section = this; + while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { + String node = path.substring(i2, i1); + ConfigurationSection subSection = section.getConfigurationSection(node); + if (subSection == null) { + section = section.createSection(node); + } else { + section = subSection; + } + } + + String key = path.substring(i2); + if (section == this) { + if (value == null) { + map.remove(key); + } else { + map.put(key, value); + } + } else { + section.set(key, value); + } + } + + public Object get(String path) { + return get(path, getDefault(path)); + } + + public Object get(String path, Object def) { + if (path == null) throw new NullPointerException("Path cannot be null"); + + if (path.length() == 0) { + return this; + } + + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot access section without a root"); + } + + final char separator = root.options().pathSeparator(); + // i1 is the leading (higher) index + // i2 is the trailing (lower) index + int i1 = -1, i2; + ConfigurationSection section = this; + while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { + section = section.getConfigurationSection(path.substring(i2, i1)); + if (section == null) { + return def; + } + } + + String key = path.substring(i2); + if (section == this) { + Object result = map.get(key); + return (result == null) ? def : result; + } + return section.get(key, def); + } + + public ConfigurationSection createSection(String path) { + if (path == null) throw new NullPointerException("Cannot create section at empty path"); + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot create section without a root"); + } + + final char separator = root.options().pathSeparator(); + // i1 is the leading (higher) index + // i2 is the trailing (lower) index + int i1 = -1, i2; + ConfigurationSection section = this; + while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { + String node = path.substring(i2, i1); + ConfigurationSection subSection = section.getConfigurationSection(node); + if (subSection == null) { + section = section.createSection(node); + } else { + section = subSection; + } + } + + String key = path.substring(i2); + if (section == this) { + ConfigurationSection result = new MemorySection(this, key); + map.put(key, result); + return result; + } + return section.createSection(key); + } + + public ConfigurationSection createSection(String path, Map map) { + ConfigurationSection section = createSection(path); + + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() instanceof Map) { + section.createSection(entry.getKey().toString(), (Map) entry.getValue()); + } else { + section.set(entry.getKey().toString(), entry.getValue()); + } + } + + return section; + } + + // Primitives + public String getString(String path) { + Object def = getDefault(path); + return getString(path, def != null ? def.toString() : null); + } + + public String getString(String path, String def) { + Object val = get(path, def); + return (val != null) ? val.toString() : def; + } + + public boolean isString(String path) { + Object val = get(path); + return val instanceof String; + } + + public int getInt(String path) { + Object def = getDefault(path); + return getInt(path, (def instanceof Number) ? toInt(def) : 0); + } + + public int getInt(String path, int def) { + Object val = get(path, def); + return (val instanceof Number) ? toInt(val) : def; + } + + public boolean isInt(String path) { + Object val = get(path); + return val instanceof Integer; + } + + public boolean getBoolean(String path) { + Object def = getDefault(path); + return getBoolean(path, (def instanceof Boolean) ? (Boolean) def : false); + } + + public boolean getBoolean(String path, boolean def) { + Object val = get(path, def); + return (val instanceof Boolean) ? (Boolean) val : def; + } + + public boolean isBoolean(String path) { + Object val = get(path); + return val instanceof Boolean; + } + + public double getDouble(String path) { + Object def = getDefault(path); + return getDouble(path, (def instanceof Number) ? toDouble(def) : 0); + } + + public double getDouble(String path, double def) { + Object val = get(path, def); + return (val instanceof Number) ? toDouble(val) : def; + } + + public boolean isDouble(String path) { + Object val = get(path); + return val instanceof Double; + } + + public long getLong(String path) { + Object def = getDefault(path); + return getLong(path, (def instanceof Number) ? toLong(def) : 0); + } + + public long getLong(String path, long def) { + Object val = get(path, def); + return (val instanceof Number) ? toLong(val) : def; + } + + public boolean isLong(String path) { + Object val = get(path); + return val instanceof Long; + } + + // Java + public List getList(String path) { + Object def = getDefault(path); + return getList(path, (def instanceof List) ? (List) def : null); + } + + public List getList(String path, List def) { + Object val = get(path, def); + return (List) ((val instanceof List) ? val : def); + } + + public boolean isList(String path) { + Object val = get(path); + return val instanceof List; + } + + public List getStringList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if ((object instanceof String) || (isPrimitiveWrapper(object))) { + result.add(String.valueOf(object)); + } + } + + return result; + } + + public List getIntegerList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Integer) { + result.add((Integer) object); + } else if (object instanceof String) { + try { + result.add(Integer.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((int) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).intValue()); + } + } + + return result; + } + + public List getBooleanList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Boolean) { + result.add((Boolean) object); + } else if (object instanceof String) { + if (Boolean.TRUE.toString().equals(object)) { + result.add(true); + } else if (Boolean.FALSE.toString().equals(object)) { + result.add(false); + } + } + } + + return result; + } + + public List getDoubleList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Double) { + result.add((Double) object); + } else if (object instanceof String) { + try { + result.add(Double.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((double) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).doubleValue()); + } + } + + return result; + } + + public List getFloatList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Float) { + result.add((Float) object); + } else if (object instanceof String) { + try { + result.add(Float.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((float) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).floatValue()); + } + } + + return result; + } + + public List getLongList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Long) { + result.add((Long) object); + } else if (object instanceof String) { + try { + result.add(Long.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((long) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).longValue()); + } + } + + return result; + } + + public List getByteList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Byte) { + result.add((Byte) object); + } else if (object instanceof String) { + try { + result.add(Byte.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((byte) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).byteValue()); + } + } + + return result; + } + + public List getCharacterList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Character) { + result.add((Character) object); + } else if (object instanceof String) { + String str = (String) object; + + if (str.length() == 1) { + result.add(str.charAt(0)); + } + } else if (object instanceof Number) { + result.add((char) ((Number) object).intValue()); + } + } + + return result; + } + + public List getShortList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Short) { + result.add((Short) object); + } else if (object instanceof String) { + try { + result.add(Short.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((short) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).shortValue()); + } + } + + return result; + } + + public List> getMapList(String path) { + List list = getList(path); + List> result = new ArrayList>(); + + if (list == null) { + return result; + } + + for (Object object : list) { + if (object instanceof Map) { + result.add((Map) object); + } + } + + return result; + } + + public ConfigurationSection getConfigurationSection(String path) { + Object val = get(path, null); + if (val != null) { + return (val instanceof ConfigurationSection) ? (ConfigurationSection) val : null; + } + + val = get(path, getDefault(path)); + return (val instanceof ConfigurationSection) ? createSection(path) : null; + } + + public boolean isConfigurationSection(String path) { + Object val = get(path); + return val instanceof ConfigurationSection; + } + + protected boolean isPrimitiveWrapper(Object input) { + return input instanceof Integer || input instanceof Boolean || + input instanceof Character || input instanceof Byte || + input instanceof Short || input instanceof Double || + input instanceof Long || input instanceof Float; + } + + protected Object getDefault(String path) { + if (path == null) throw new NullPointerException("Path may not be null"); + + Configuration root = getRoot(); + Configuration defaults = root == null ? null : root.getDefaults(); + return (defaults == null) ? null : defaults.get(createPath(this, path)); + } + + protected void mapChildrenKeys(Set output, ConfigurationSection section, boolean deep) { + if (section instanceof MemorySection) { + MemorySection sec = (MemorySection) section; + + for (Map.Entry entry : sec.map.entrySet()) { + output.add(createPath(section, entry.getKey(), this)); + + if ((deep) && (entry.getValue() instanceof ConfigurationSection)) { + ConfigurationSection subsection = (ConfigurationSection) entry.getValue(); + mapChildrenKeys(output, subsection, deep); + } + } + } else { + Set keys = section.getKeys(deep); + + for (String key : keys) { + output.add(createPath(section, key, this)); + } + } + } + + protected void mapChildrenValues(Map output, ConfigurationSection section, boolean deep) { + if (section instanceof MemorySection) { + MemorySection sec = (MemorySection) section; + + for (Map.Entry entry : sec.map.entrySet()) { + output.put(createPath(section, entry.getKey(), this), entry.getValue()); + + if (entry.getValue() instanceof ConfigurationSection) { + if (deep) { + mapChildrenValues(output, (ConfigurationSection) entry.getValue(), deep); + } + } + } + } else { + Map values = section.getValues(deep); + + for (Map.Entry entry : values.entrySet()) { + output.put(createPath(section, entry.getKey(), this), entry.getValue()); + } + } + } + + /** + * Creates a full path to the given {@link ConfigurationSection} from its + * root {@link Configuration}. + *

+ * You may use this method for any given {@link ConfigurationSection}, not + * only {@link MemorySection}. + * + * @param section Section to create a path for. + * @param key Name of the specified section. + * @return Full path of the section from its root. + */ + public static String createPath(ConfigurationSection section, String key) { + return createPath(section, key, (section == null) ? null : section.getRoot()); + } + + /** + * Creates a relative path to the given {@link ConfigurationSection} from + * the given relative section. + *

+ * You may use this method for any given {@link ConfigurationSection}, not + * only {@link MemorySection}. + * + * @param section Section to create a path for. + * @param key Name of the specified section. + * @param relativeTo Section to create the path relative to. + * @return Full path of the section from its root. + */ + public static String createPath(ConfigurationSection section, String key, ConfigurationSection relativeTo) { + if (section == null) throw new NullPointerException("Cannot create path without a section"); + Configuration root = section.getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot create path without a root"); + } + char separator = root.options().pathSeparator(); + + StringBuilder builder = new StringBuilder(); + if (section != null) { + for (ConfigurationSection parent = section; (parent != null) && (parent != relativeTo); parent = parent.getParent()) { + if (builder.length() > 0) { + builder.insert(0, separator); + } + + builder.insert(0, parent.getName()); + } + } + + if ((key != null) && (key.length() > 0)) { + if (builder.length() > 0) { + builder.append(separator); + } + + builder.append(key); + } + + return builder.toString(); + } + + @Override + public String toString() { + Configuration root = getRoot(); + return new StringBuilder() + .append(getClass().getSimpleName()) + .append("[path='") + .append(getCurrentPath()) + .append("', root='") + .append(root == null ? null : root.getClass().getSimpleName()) + .append("']") + .toString(); + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/FileConfiguration.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/FileConfiguration.java new file mode 100644 index 000000000..3c39c26fa --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/FileConfiguration.java @@ -0,0 +1,287 @@ +package com.intellectualcrafters.configuration.file; + +import com.intellectualcrafters.configuration.InvalidConfigurationException; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +import com.intellectualcrafters.configuration.Configuration; +import com.intellectualcrafters.configuration.MemoryConfiguration; + +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; + +/** + * This is a base class for all File based implementations of {@link + * Configuration} + */ +public abstract class FileConfiguration extends MemoryConfiguration { + /** + * This value specified that the system default encoding should be + * completely ignored, as it cannot handle the ASCII character set, or it + * is a strict-subset of UTF8 already (plain ASCII). + * + * @deprecated temporary compatibility measure + */ + @Deprecated + public static final boolean UTF8_OVERRIDE; + /** + * This value specifies if the system default encoding is unicode, but + * cannot parse standard ASCII. + * + * @deprecated temporary compatibility measure + */ + @Deprecated + public static final boolean UTF_BIG; + /** + * This value specifies if the system supports unicode. + * + * @deprecated temporary compatibility measure + */ + @Deprecated + public static final boolean SYSTEM_UTF; + static { + final byte[] testBytes = Base64Coder.decode("ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX4NCg=="); + final String testString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\r\n"; + final Charset defaultCharset = Charset.defaultCharset(); + final String resultString = new String(testBytes, defaultCharset); + final boolean trueUTF = defaultCharset.name().contains("UTF"); + UTF8_OVERRIDE = !testString.equals(resultString) || defaultCharset.equals(Charset.forName("US-ASCII")); + SYSTEM_UTF = trueUTF || UTF8_OVERRIDE; + UTF_BIG = trueUTF && UTF8_OVERRIDE; + } + + /** + * Creates an empty {@link FileConfiguration} with no default values. + */ + public FileConfiguration() { + super(); + } + + /** + * Creates an empty {@link FileConfiguration} using the specified {@link + * Configuration} as a source for all default values. + * + * @param defaults Default value provider + */ + public FileConfiguration(Configuration defaults) { + super(defaults); + } + + /** + * Saves this {@link FileConfiguration} to the specified location. + *

+ * If the file does not exist, it will be created. If already exists, it + * will be overwritten. If it cannot be overwritten or created, an + * exception will be thrown. + *

+ * This method will save using the system default encoding, or possibly + * using UTF8. + * + * @param file File to save to. + * @throws IOException Thrown when the given file cannot be written to for + * any reason. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void save(File file) throws IOException { + if (file == null) throw new NullPointerException("File cannot be null"); + file.getParentFile().mkdirs(); + + String data = saveToString(); + + Writer writer = new OutputStreamWriter(new FileOutputStream(file), UTF8_OVERRIDE && !UTF_BIG ? StandardCharsets.UTF_8 : Charset.defaultCharset()); + + try { + writer.write(data); + } finally { + writer.close(); + } + } + + /** + * Saves this {@link FileConfiguration} to the specified location. + *

+ * If the file does not exist, it will be created. If already exists, it + * will be overwritten. If it cannot be overwritten or created, an + * exception will be thrown. + *

+ * This method will save using the system default encoding, or possibly + * using UTF8. + * + * @param file File to save to. + * @throws IOException Thrown when the given file cannot be written to for + * any reason. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void save(String file) throws IOException { + if (file == null) throw new NullPointerException("File cannot be null"); + + save(new File(file)); + } + + /** + * Saves this {@link FileConfiguration} to a string, and returns it. + * + * @return String containing this configuration. + */ + public abstract String saveToString(); + + /** + * Loads this {@link FileConfiguration} from the specified location. + *

+ * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given file. + *

+ * If the file cannot be loaded for any reason, an exception will be + * thrown. + *

+ * This will attempt to use the {@link Charset#defaultCharset()} for + * files, unless {@link #UTF8_OVERRIDE} but not {@link #UTF_BIG} is + * specified. + * + * @param file File to load from. + * @throws FileNotFoundException Thrown when the given file cannot be + * opened. + * @throws IOException Thrown when the given file cannot be read. + * @throws InvalidConfigurationException Thrown when the given file is not + * a valid Configuration. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void load(File file) throws FileNotFoundException, IOException, InvalidConfigurationException { + if (file == null) throw new NullPointerException("File cannot be null"); + + final FileInputStream stream = new FileInputStream(file); + + load(new InputStreamReader(stream, UTF8_OVERRIDE && !UTF_BIG ? StandardCharsets.UTF_8 : Charset.defaultCharset())); + } + + /** + * Loads this {@link FileConfiguration} from the specified stream. + *

+ * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given stream. + *

+ * This will attempt to use the {@link Charset#defaultCharset()}, unless + * {@link #UTF8_OVERRIDE} or {@link #UTF_BIG} is specified. + * + * @param stream Stream to load from + * @throws IOException Thrown when the given file cannot be read. + * @throws InvalidConfigurationException Thrown when the given file is not + * a valid Configuration. + * @throws IllegalArgumentException Thrown when stream is null. + * @deprecated This does not consider encoding + * @see #load(Reader) + */ + @Deprecated + public void load(InputStream stream) throws IOException, InvalidConfigurationException { + if (stream == null) throw new NullPointerException("Stream cannot be null"); + + load(new InputStreamReader(stream, UTF8_OVERRIDE ? StandardCharsets.UTF_8 : Charset.defaultCharset())); + } + + /** + * Loads this {@link FileConfiguration} from the specified reader. + *

+ * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given stream. + * + * @param reader the reader to load from + * @throws IOException thrown when underlying reader throws an IOException + * @throws InvalidConfigurationException thrown when the reader does not + * represent a valid Configuration + * @throws IllegalArgumentException thrown when reader is null + */ + public void load(Reader reader) throws IOException, InvalidConfigurationException { + BufferedReader input = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); + + StringBuilder builder = new StringBuilder(); + + try { + String line; + + while ((line = input.readLine()) != null) { + builder.append(line); + builder.append('\n'); + } + } finally { + input.close(); + } + + loadFromString(builder.toString()); + } + + /** + * Loads this {@link FileConfiguration} from the specified location. + *

+ * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given file. + *

+ * If the file cannot be loaded for any reason, an exception will be + * thrown. + * + * @param file File to load from. + * @throws FileNotFoundException Thrown when the given file cannot be + * opened. + * @throws IOException Thrown when the given file cannot be read. + * @throws InvalidConfigurationException Thrown when the given file is not + * a valid Configuration. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void load(String file) throws FileNotFoundException, IOException, InvalidConfigurationException { + if (file == null) throw new NullPointerException("File cannot be null"); + + load(new File(file)); + } + + /** + * Loads this {@link FileConfiguration} from the specified string, as + * opposed to from file. + *

+ * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given string. + *

+ * If the string is invalid in any way, an exception will be thrown. + * + * @param contents Contents of a Configuration to load. + * @throws InvalidConfigurationException Thrown if the specified string is + * invalid. + * @throws IllegalArgumentException Thrown if contents is null. + */ + public abstract void loadFromString(String contents) throws InvalidConfigurationException; + + /** + * Compiles the header for this {@link FileConfiguration} and returns the + * result. + *

+ * This will use the header from {@link #options()} -> {@link + * FileConfigurationOptions#header()}, respecting the rules of {@link + * FileConfigurationOptions#copyHeader()} if set. + * + * @return Compiled header + */ + protected abstract String buildHeader(); + + @Override + public FileConfigurationOptions options() { + if (options == null) { + options = new FileConfigurationOptions(this); + } + + return (FileConfigurationOptions) options; + } +} \ No newline at end of file diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/FileConfigurationOptions.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/FileConfigurationOptions.java new file mode 100644 index 000000000..fcee3061a --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/FileConfigurationOptions.java @@ -0,0 +1,118 @@ +package com.intellectualcrafters.configuration.file; + +import com.intellectualcrafters.configuration.*; + +/** + * Various settings for controlling the input and output of a {@link + * FileConfiguration} + */ +public class FileConfigurationOptions extends MemoryConfigurationOptions { + private String header = null; + private boolean copyHeader = true; + + protected FileConfigurationOptions(MemoryConfiguration configuration) { + super(configuration); + } + + @Override + public FileConfiguration configuration() { + return (FileConfiguration) super.configuration(); + } + + @Override + public FileConfigurationOptions copyDefaults(boolean value) { + super.copyDefaults(value); + return this; + } + + @Override + public FileConfigurationOptions pathSeparator(char value) { + super.pathSeparator(value); + return this; + } + + /** + * Gets the header that will be applied to the top of the saved output. + *

+ * This header will be commented out and applied directly at the top of + * the generated output of the {@link FileConfiguration}. It is not + * required to include a newline at the end of the header as it will + * automatically be applied, but you may include one if you wish for extra + * spacing. + *

+ * Null is a valid value which will indicate that no header is to be + * applied. The default value is null. + * + * @return Header + */ + public String header() { + return header; + } + + /** + * Sets the header that will be applied to the top of the saved output. + *

+ * This header will be commented out and applied directly at the top of + * the generated output of the {@link FileConfiguration}. It is not + * required to include a newline at the end of the header as it will + * automatically be applied, but you may include one if you wish for extra + * spacing. + *

+ * Null is a valid value which will indicate that no header is to be + * applied. + * + * @param value New header + * @return This object, for chaining + */ + public FileConfigurationOptions header(String value) { + this.header = value; + return this; + } + + /** + * Gets whether or not the header should be copied from a default source. + *

+ * If this is true, if a default {@link FileConfiguration} is passed to + * {@link + * FileConfiguration#setDefaults(com.intellectualcrafters.configuration.Configuration)} + * then upon saving it will use the header from that config, instead of + * the one provided here. + *

+ * If no default is set on the configuration, or the default is not of + * type FileConfiguration, or that config has no header ({@link #header()} + * returns null) then the header specified in this configuration will be + * used. + *

+ * Defaults to true. + * + * @return Whether or not to copy the header + */ + public boolean copyHeader() { + return copyHeader; + } + + /** + * Sets whether or not the header should be copied from a default source. + *

+ * If this is true, if a default {@link FileConfiguration} is passed to + * {@link + * FileConfiguration#setDefaults(com.intellectualcrafters.configuration.Configuration)} + * then upon saving it will use the header from that config, instead of + * the one provided here. + *

+ * If no default is set on the configuration, or the default is not of + * type FileConfiguration, or that config has no header ({@link #header()} + * returns null) then the header specified in this configuration will be + * used. + *

+ * Defaults to true. + * + * @param value Whether or not to copy the header + * @return This object, for chaining + */ + public FileConfigurationOptions copyHeader(boolean value) { + copyHeader = value; + + return this; + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConfiguration.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConfiguration.java new file mode 100644 index 000000000..2379c6915 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConfiguration.java @@ -0,0 +1,254 @@ +package com.intellectualcrafters.configuration.file; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Map; + +import com.intellectualcrafters.configuration.Configuration; +import com.intellectualcrafters.configuration.ConfigurationSection; +import com.intellectualcrafters.configuration.InvalidConfigurationException; +import com.intellectualcrafters.plot.PS; + +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.representer.Representer; + +/** + * An implementation of {@link Configuration} which saves all files in Yaml. + * Note that this implementation is not synchronized. + */ +public class YamlConfiguration extends FileConfiguration { + protected static final String COMMENT_PREFIX = "# "; + protected static final String BLANK_CONFIG = "{}\n"; + private final DumperOptions yamlOptions = new DumperOptions(); + private final Representer yamlRepresenter = new YamlRepresenter(); + private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions); + + @Override + public String saveToString() { + yamlOptions.setIndent(options().indent()); + yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + yamlOptions.setAllowUnicode(SYSTEM_UTF); + yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + + String header = buildHeader(); + String dump = yaml.dump(getValues(false)); + + if (dump.equals(BLANK_CONFIG)) { + dump = ""; + } + + return header + dump; + } + + @Override + public void loadFromString(String contents) throws InvalidConfigurationException { + if (contents == null) throw new NullPointerException("Contents cannot be null"); + + Map input; + try { + input = (Map) yaml.load(contents); + } catch (YAMLException e) { + throw new InvalidConfigurationException(e); + } catch (ClassCastException e) { + throw new InvalidConfigurationException("Top level is not a Map."); + } + + String header = parseHeader(contents); + if (header.length() > 0) { + options().header(header); + } + + if (input != null) { + convertMapsToSections(input, this); + } + } + + protected void convertMapsToSections(Map input, ConfigurationSection section) { + for (Map.Entry entry : input.entrySet()) { + String key = entry.getKey().toString(); + Object value = entry.getValue(); + + if (value instanceof Map) { + convertMapsToSections((Map) value, section.createSection(key)); + } else { + section.set(key, value); + } + } + } + + protected String parseHeader(String input) { + String[] lines = input.split("\r?\n", -1); + StringBuilder result = new StringBuilder(); + boolean readingHeader = true; + boolean foundHeader = false; + + for (int i = 0; (i < lines.length) && (readingHeader); i++) { + String line = lines[i]; + + if (line.startsWith(COMMENT_PREFIX)) { + if (i > 0) { + result.append("\n"); + } + + if (line.length() > COMMENT_PREFIX.length()) { + result.append(line.substring(COMMENT_PREFIX.length())); + } + + foundHeader = true; + } else if ((foundHeader) && (line.length() == 0)) { + result.append("\n"); + } else if (foundHeader) { + readingHeader = false; + } + } + + return result.toString(); + } + + @Override + protected String buildHeader() { + String header = options().header(); + + if (options().copyHeader()) { + Configuration def = getDefaults(); + + if ((def != null) && (def instanceof FileConfiguration)) { + FileConfiguration filedefaults = (FileConfiguration) def; + String defaultsHeader = filedefaults.buildHeader(); + + if ((defaultsHeader != null) && (defaultsHeader.length() > 0)) { + return defaultsHeader; + } + } + } + + if (header == null) { + return ""; + } + + StringBuilder builder = new StringBuilder(); + String[] lines = header.split("\r?\n", -1); + boolean startedHeader = false; + + for (int i = lines.length - 1; i >= 0; i--) { + builder.insert(0, "\n"); + + if ((startedHeader) || (lines[i].length() != 0)) { + builder.insert(0, lines[i]); + builder.insert(0, COMMENT_PREFIX); + startedHeader = true; + } + } + + return builder.toString(); + } + + @Override + public YamlConfigurationOptions options() { + if (options == null) { + options = new YamlConfigurationOptions(this); + } + + return (YamlConfigurationOptions) options; + } + + /** + * Creates a new {@link YamlConfiguration}, loading from the given file. + *

+ * Any errors loading the Configuration will be logged and then ignored. + * If the specified input is not a valid config, a blank config will be + * returned. + *

+ * The encoding used may follow the system dependent default. + * + * @param file Input file + * @return Resulting configuration + * @throws IllegalArgumentException Thrown if file is null + */ + public static YamlConfiguration loadConfiguration(File file) { + if (file == null) throw new NullPointerException("File cannot be null"); + + YamlConfiguration config = new YamlConfiguration(); + + try { + config.load(file); + } catch (FileNotFoundException ex) { + } catch (IOException ex) { + PS.log("Cannot load " + file); + ex.printStackTrace(); + } catch (InvalidConfigurationException ex) { + PS.log("Cannot load " + file); + ex.printStackTrace(); + } + + return config; + } + + /** + * Creates a new {@link YamlConfiguration}, loading from the given stream. + *

+ * Any errors loading the Configuration will be logged and then ignored. + * If the specified input is not a valid config, a blank config will be + * returned. + * + * @param stream Input stream + * @return Resulting configuration + * @throws IllegalArgumentException Thrown if stream is null + * @deprecated does not properly consider encoding + * @see #load(InputStream) + * @see #loadConfiguration(Reader) + */ + @Deprecated + public static YamlConfiguration loadConfiguration(InputStream stream) { + if (stream == null) throw new NullPointerException("Stream cannot be null"); + + YamlConfiguration config = new YamlConfiguration(); + + try { + config.load(stream); + } catch (IOException ex) { + PS.log("Cannot load configuration from stream"); + ex.printStackTrace(); + } catch (InvalidConfigurationException ex) { + ex.printStackTrace(); + PS.log("Cannot load configuration from stream"); + } + + return config; + } + + + /** + * Creates a new {@link YamlConfiguration}, loading from the given reader. + *

+ * Any errors loading the Configuration will be logged and then ignored. + * If the specified input is not a valid config, a blank config will be + * returned. + * + * @param reader input + * @return resulting configuration + * @throws IllegalArgumentException Thrown if stream is null + */ + public static YamlConfiguration loadConfiguration(Reader reader) { + if (reader == null) throw new NullPointerException("Reader cannot be null"); + + YamlConfiguration config = new YamlConfiguration(); + + try { + config.load(reader); + } catch (IOException ex) { + PS.log("Cannot load configuration from stream"); + ex.printStackTrace(); + } catch (InvalidConfigurationException ex) { + PS.log("Cannot load configuration from stream"); + ex.printStackTrace(); + } + + return config; + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConfigurationOptions.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConfigurationOptions.java new file mode 100644 index 000000000..6783d5ce4 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConfigurationOptions.java @@ -0,0 +1,69 @@ +package com.intellectualcrafters.configuration.file; + +/** + * Various settings for controlling the input and output of a {@link + * YamlConfiguration} + */ +public class YamlConfigurationOptions extends FileConfigurationOptions { + private int indent = 2; + + protected YamlConfigurationOptions(YamlConfiguration configuration) { + super(configuration); + } + + @Override + public YamlConfiguration configuration() { + return (YamlConfiguration) super.configuration(); + } + + @Override + public YamlConfigurationOptions copyDefaults(boolean value) { + super.copyDefaults(value); + return this; + } + + @Override + public YamlConfigurationOptions pathSeparator(char value) { + super.pathSeparator(value); + return this; + } + + @Override + public YamlConfigurationOptions header(String value) { + super.header(value); + return this; + } + + @Override + public YamlConfigurationOptions copyHeader(boolean value) { + super.copyHeader(value); + return this; + } + + /** + * Gets how much spaces should be used to indent each line. + *

+ * The minimum value this may be is 2, and the maximum is 9. + * + * @return How much to indent by + */ + public int indent() { + return indent; + } + + /** + * Sets how much spaces should be used to indent each line. + *

+ * The minimum value this may be is 2, and the maximum is 9. + * + * @param value New indent + * @return This object, for chaining + */ + public YamlConfigurationOptions indent(int value) { + if (value < 2) throw new IllegalArgumentException("Indent must be at least 2 characters"); + if (value > 9) throw new IllegalArgumentException("Indent cannot be greater than 9 characters"); + + this.indent = value; + return this; + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConstructor.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConstructor.java new file mode 100644 index 000000000..024456a4c --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlConstructor.java @@ -0,0 +1,49 @@ +package com.intellectualcrafters.configuration.file; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.nodes.Tag; + +import com.intellectualcrafters.configuration.serialization.ConfigurationSerialization; + +public class YamlConstructor extends SafeConstructor { + + public YamlConstructor() { + this.yamlConstructors.put(Tag.MAP, new ConstructCustomObject()); + } + + private class ConstructCustomObject extends ConstructYamlMap { + @Override + public Object construct(Node node) { + if (node.isTwoStepsConstruction()) { + throw new YAMLException("Unexpected referential mapping structure. Node: " + node); + } + + Map raw = (Map) super.construct(node); + + if (raw.containsKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) { + Map typed = new LinkedHashMap(raw.size()); + for (Map.Entry entry : raw.entrySet()) { + typed.put(entry.getKey().toString(), entry.getValue()); + } + + try { + return ConfigurationSerialization.deserializeObject(typed); + } catch (IllegalArgumentException ex) { + throw new YAMLException("Could not deserialize object", ex); + } + } + + return raw; + } + + @Override + public void construct2ndStep(Node node, Object object) { + throw new YAMLException("Unexpected referential mapping structure. Node: " + node); + } + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlRepresenter.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlRepresenter.java new file mode 100644 index 000000000..de52340c5 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/file/YamlRepresenter.java @@ -0,0 +1,38 @@ +package com.intellectualcrafters.configuration.file; + +import java.util.LinkedHashMap; +import java.util.Map; + +import com.intellectualcrafters.configuration.ConfigurationSection; +import com.intellectualcrafters.configuration.serialization.ConfigurationSerializable; +import com.intellectualcrafters.configuration.serialization.ConfigurationSerialization; + +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.representer.Representer; + +public class YamlRepresenter extends Representer { + + public YamlRepresenter() { + this.multiRepresenters.put(ConfigurationSection.class, new RepresentConfigurationSection()); + this.multiRepresenters.put(ConfigurationSerializable.class, new RepresentConfigurationSerializable()); + } + + private class RepresentConfigurationSection extends RepresentMap { + @Override + public Node representData(Object data) { + return super.representData(((ConfigurationSection) data).getValues(false)); + } + } + + private class RepresentConfigurationSerializable extends RepresentMap { + @Override + public Node representData(Object data) { + ConfigurationSerializable serializable = (ConfigurationSerializable) data; + Map values = new LinkedHashMap(); + values.put(ConfigurationSerialization.SERIALIZED_TYPE_KEY, ConfigurationSerialization.getAlias(serializable.getClass())); + values.putAll(serializable.serialize()); + + return super.representData(values); + } + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/ConfigurationSerializable.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/ConfigurationSerializable.java new file mode 100644 index 000000000..e403ebed9 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/ConfigurationSerializable.java @@ -0,0 +1,35 @@ +package com.intellectualcrafters.configuration.serialization; + +import java.util.Map; + +/** + * Represents an object that may be serialized. + *

+ * These objects MUST implement one of the following, in addition to the + * methods as defined by this interface: + *

+ * In addition to implementing this interface, you must register the class + * with {@link ConfigurationSerialization#registerClass(Class)}. + * + * @see DelegateDeserialization + * @see SerializableAs + */ +public interface ConfigurationSerializable { + + /** + * Creates a Map representation of this class. + *

+ * 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 serialize(); +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/ConfigurationSerialization.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/ConfigurationSerialization.java new file mode 100644 index 000000000..4c2f4a5bc --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/ConfigurationSerialization.java @@ -0,0 +1,266 @@ +package com.intellectualcrafters.configuration.serialization; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +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 com.intellectualcrafters.configuration.Configuration; + +/** + * Utility class for storing and retrieving classes for {@link Configuration}. + */ +public class ConfigurationSerialization { + public static final String SERIALIZED_TYPE_KEY = "=="; + private final Class clazz; + private static Map> aliases = new HashMap>(); + + protected ConfigurationSerialization(Class 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 getConstructor() { + try { + return clazz.getConstructor(Map.class); + } catch (NoSuchMethodException ex) { + return null; + } catch (SecurityException ex) { + return null; + } + } + + protected ConfigurationSerializable deserializeViaMethod(Method method, Map 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 instanceof InvocationTargetException ? ex.getCause() : ex); + } + + return null; + } + + protected ConfigurationSerializable deserializeViaCtor(Constructor ctor, Map 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 instanceof InvocationTargetException ? ex.getCause() : ex); + } + + return null; + } + + public ConfigurationSerializable deserialize(Map args) { + if (args == null) { + throw new NullPointerException("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 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. + *

+ * The class must implement {@link ConfigurationSerializable}, including + * the extra methods as specified in the javadoc of + * ConfigurationSerializable. + *

+ * 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 args, Class clazz) { + return new ConfigurationSerialization(clazz).deserialize(args); + } + + /** + * Attempts to deserialize the given arguments into a new instance of the + * given class. + *

+ * The class must implement {@link ConfigurationSerializable}, including + * the extra methods as specified in the javadoc of + * ConfigurationSerializable. + *

+ * 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 args) { + Class clazz = null; + + if (args.containsKey(SERIALIZED_TYPE_KEY)) { + try { + String alias = (String) args.get(SERIALIZED_TYPE_KEY); + + if (alias == null) { + throw new IllegalArgumentException("Cannot have null alias"); + } + clazz = getClassByAlias(alias); + if (clazz == null) { + throw new IllegalArgumentException("Specified class does not exist ('" + 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 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 + * @see SerializableAs + */ + public static void registerClass(Class 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 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 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 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(); + } +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/DelegateDeserialization.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/DelegateDeserialization.java new file mode 100644 index 000000000..bc505ccf2 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/DelegateDeserialization.java @@ -0,0 +1,22 @@ +package com.intellectualcrafters.configuration.serialization; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Applies to 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 value(); +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/SerializableAs.java b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/SerializableAs.java new file mode 100644 index 000000000..668da9ac9 --- /dev/null +++ b/PlotSquared/src/main/java/com/intellectualcrafters/configuration/serialization/SerializableAs.java @@ -0,0 +1,34 @@ +package com.intellectualcrafters.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. + *

+ * This value will be stored in the configuration so that the configuration + * deserialization can determine what type it is. + *

+ * Using this annotation on any other class than a {@link + * ConfigurationSerializable} will have no effect. + * + * @see ConfigurationSerialization#registerClass(Class, String) + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface SerializableAs { + /** + * This is the name your class will be stored and retrieved as. + *

+ * 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(); +} diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/PS.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/PS.java index 1cf8d25bd..bf0bb1e0a 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/PS.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/PS.java @@ -16,7 +16,7 @@ import com.intellectualcrafters.plot.util.Logger.LogLevel; import com.intellectualcrafters.plot.util.bukkit.UUIDHandler; import com.sk89q.worldedit.bukkit.WorldEditPlugin; -import org.bukkit.configuration.file.YamlConfiguration; +import com.intellectualcrafters.configuration.file.YamlConfiguration; import java.io.File; import java.io.FileInputStream; @@ -1084,7 +1084,7 @@ public class PS { Settings.CONFIRM_UNLINK = config.getBoolean("confirmation.unlink"); // Protection - Settings.REDSTONE_DISABLER = config.getBoolean("protection.tnt-listener.enabled"); + Settings.REDSTONE_DISABLER = config.getBoolean("protection.redstone.disable-offline"); Settings.TNT_LISTENER = config.getBoolean("protection.tnt-listener.enabled"); Settings.PISTON_FALLING_BLOCK_CHECK = config.getBoolean("protection.piston.falling-blocks"); diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/api/PlotAPI.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/api/PlotAPI.java index d13602959..57164503c 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/api/PlotAPI.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/api/PlotAPI.java @@ -35,7 +35,7 @@ import com.intellectualcrafters.plot.util.bukkit.UUIDHandler; import com.intellectualcrafters.plot.uuid.UUIDWrapper; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.configuration.file.YamlConfiguration; +import com.intellectualcrafters.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/MusicSubcommand.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/MusicSubcommand.java index ef012b036..e8150d205 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/MusicSubcommand.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/MusicSubcommand.java @@ -32,6 +32,7 @@ import com.intellectualcrafters.plot.object.PlotItemStack; import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.util.BlockManager; import com.intellectualcrafters.plot.util.MainUtil; +import com.intellectualcrafters.plot.util.TaskManager; public class MusicSubcommand extends SubCommand { public MusicSubcommand() { @@ -52,8 +53,19 @@ public class MusicSubcommand extends SubCommand { PlotInventory inv = new PlotInventory(player, 2, "Plot Jukebox") { public boolean onClick(int index) { PlotItemStack item = getItem(index); - FlagManager.addPlotFlag(plot, new Flag(FlagManager.getFlag("music"), item.id)); - PlotListener.manager.plotEntry(player, plot); + int id = item.id == 7 ? 0 : item.id; + if (id == 0) { + FlagManager.removePlotFlag(plot, "music"); + } + else { + FlagManager.addPlotFlag(plot, new Flag(FlagManager.getFlag("music"), id)); + } + TaskManager.runTaskLater(new Runnable() { + @Override + public void run() { + PlotListener.manager.plotEntry(player, plot); + } + }, 1); close(); return false; } @@ -66,6 +78,11 @@ public class MusicSubcommand extends SubCommand { inv.setItem(index, item); index++; } + if (player.getMeta("music") != null) { + String name = "&r&6Cancel music"; + String[] lore = {"&r&cClick to cancel!"}; + inv.setItem(index, new PlotItemStack(7, (short) 0, 1, name, lore)); + } inv.openInventory(); return true; } diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/Template.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/Template.java index 442c0a4cc..1641118e2 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/Template.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/Template.java @@ -28,8 +28,8 @@ import com.intellectualcrafters.plot.util.BlockManager; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.SetupUtils; import com.intellectualcrafters.plot.util.TaskManager; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; +import com.intellectualcrafters.configuration.ConfigurationSection; +import com.intellectualcrafters.configuration.file.YamlConfiguration; import java.io.File; import java.io.FileInputStream; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/Trim.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/Trim.java index 80f48006e..b4f0588fc 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/Trim.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/commands/Trim.java @@ -127,12 +127,16 @@ public class Trim extends SubCommand { plots.remove(0); final Location pos1 = MainUtil.getPlotBottomLoc(world, plot.id); final Location pos2 = MainUtil.getPlotTopLoc(world, plot.id); - final Location pos3 = new Location(world, pos1.getX(), 64, pos2.getZ()); - final Location pos4 = new Location(world, pos2.getX(), 64, pos1.getZ()); - chunks.remove(ChunkManager.getChunkChunk(pos1)); - chunks.remove(ChunkManager.getChunkChunk(pos2)); - chunks.remove(ChunkManager.getChunkChunk(pos3)); - chunks.remove(ChunkManager.getChunkChunk(pos4)); + System.out.print(plot); + System.out.print(pos1); + System.out.print(pos2); + for (int x = pos1.getX(); x <= pos2.getX(); x += 512 ) { + for (int z = pos1.getZ(); z <= pos2.getZ(); z += 512 ) { + ChunkLoc chunk = ChunkManager.getChunkChunk(new Location(world, x, 0, z)); + System.out.print(chunk.x + "," + chunk.z); + chunks.remove(chunk); + } + } } } }, 20); diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/config/C.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/config/C.java index cba885309..83aa7bbe3 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/config/C.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/config/C.java @@ -22,6 +22,7 @@ package com.intellectualcrafters.plot.config; import org.bukkit.ChatColor; +import com.boydti.buildoff.config.BBC; import com.intellectualsites.translation.TranslationFile; import com.intellectualsites.translation.TranslationLanguage; import com.intellectualsites.translation.TranslationManager; @@ -545,6 +546,20 @@ public enum C { C(final String d, String cat) { this(d, true, cat.toLowerCase()); } + + public static String format(C c, Object... args) { + String m = c.s; + for (int i = args.length - 1 ; i >= 0; i--) { + if (args[i] == null) { + continue; + } + m = m.replaceAll("%s" + i, args[i].toString()); + } + if (args.length > 0) { + m = m.replaceAll("%s", args[0].toString()); + } + return m; + } public static void setupTranslations() { manager = new TranslationManager(); diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/APlotMeConnector.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/APlotMeConnector.java index 2b87ecd2e..4a14e80b1 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/APlotMeConnector.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/APlotMeConnector.java @@ -4,7 +4,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; -import org.bukkit.configuration.file.FileConfiguration; +import com.intellectualcrafters.configuration.file.FileConfiguration; import com.intellectualcrafters.plot.object.Plot; import com.intellectualcrafters.plot.object.PlotId; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/ClassicPlotMeConnector.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/ClassicPlotMeConnector.java index 580d96d23..66992ce4b 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/ClassicPlotMeConnector.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/ClassicPlotMeConnector.java @@ -11,7 +11,7 @@ import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.bukkit.UUIDHandler; import org.bukkit.Bukkit; import org.bukkit.World; -import org.bukkit.configuration.file.FileConfiguration; +import com.intellectualcrafters.configuration.file.FileConfiguration; import java.io.File; import java.sql.*; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/LikePlotMeConverter.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/LikePlotMeConverter.java index 09a725a31..38f3351c2 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/LikePlotMeConverter.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/database/plotme/LikePlotMeConverter.java @@ -30,8 +30,8 @@ import com.intellectualcrafters.plot.util.TaskManager; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.WorldCreator; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; +import com.intellectualcrafters.configuration.file.FileConfiguration; +import com.intellectualcrafters.configuration.file.YamlConfiguration; import java.io.File; import java.io.IOException; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotWorld.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotWorld.java index 423493796..bbbf76c69 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotWorld.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotWorld.java @@ -1,7 +1,7 @@ package com.intellectualcrafters.plot.generator; import org.apache.commons.lang.StringUtils; -import org.bukkit.configuration.ConfigurationSection; +import com.intellectualcrafters.configuration.ConfigurationSection; import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.config.Configuration; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotWorld.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotWorld.java index 8f3e570e8..1c4726a7c 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotWorld.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotWorld.java @@ -25,7 +25,7 @@ import java.util.HashSet; import java.util.Map.Entry; import org.apache.commons.lang.StringUtils; -import org.bukkit.configuration.ConfigurationSection; +import com.intellectualcrafters.configuration.ConfigurationSection; import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.config.C; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/SquarePlotWorld.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/SquarePlotWorld.java index d6c5a5eb7..e4f157dc9 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/SquarePlotWorld.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/generator/SquarePlotWorld.java @@ -1,6 +1,6 @@ package com.intellectualcrafters.plot.generator; -import org.bukkit.configuration.ConfigurationSection; +import com.intellectualcrafters.configuration.ConfigurationSection; import com.intellectualcrafters.plot.PS; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/listeners/PlotListener.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/listeners/PlotListener.java index 88b7d9301..51f459e74 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/listeners/PlotListener.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/listeners/PlotListener.java @@ -34,8 +34,10 @@ import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.object.comment.CommentManager; import com.intellectualcrafters.plot.titles.AbstractTitle; import com.intellectualcrafters.plot.util.MainUtil; +import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.bukkit.BukkitUtil; import com.intellectualcrafters.plot.util.bukkit.UUIDHandler; + import org.bukkit.*; import org.bukkit.entity.Player; @@ -91,7 +93,7 @@ public class PlotListener extends APlotListener { } public void plotEntry(final PlotPlayer pp, final Plot plot) { - Player player = ((BukkitPlayer) pp).player; + final Player player = ((BukkitPlayer) pp).player; if (plot.hasOwner()) { final Flag gamemodeFlag = FlagManager.getPlotFlag(plot, "gamemode"); if (gamemodeFlag != null) { @@ -137,18 +139,31 @@ public class PlotListener extends APlotListener { } Flag musicFlag = FlagManager.getPlotFlag(plot, "music"); if (musicFlag != null) { - player.playEffect(player.getLocation(), Effect.RECORD_PLAY, 0); - Integer id = (Integer) musicFlag.getValue(); - if (id >= 2256 && id <= 2267) { - Location center = MainUtil.getPlotCenter(plot); - org.bukkit.Location newLoc = BukkitUtil.getLocation(center); - newLoc.setY(Math.min((player.getLocation().getBlockY() / 16) * 16, 240)); + final Integer id = (Integer) musicFlag.getValue(); + if ((id >= 2256 && id <= 2267) || id == 0) { + final org.bukkit.Location loc = player.getLocation(); + org.bukkit.Location lastLoc = (org.bukkit.Location) pp.getMeta("music"); + if (lastLoc != null) { + player.playEffect(lastLoc, Effect.RECORD_PLAY, 0); + if (id == 0) { + pp.deleteMeta("music"); + return; + } + } try { - player.playEffect(newLoc, Effect.RECORD_PLAY, Material.getMaterial(id)); + pp.setMeta("music", loc); + player.playEffect(loc, Effect.RECORD_PLAY, Material.getMaterial(id)); } catch (Exception e) {} } } + else { + org.bukkit.Location lastLoc = (org.bukkit.Location) pp.getMeta("music"); + if (lastLoc != null) { + pp.deleteMeta("music"); + player.playEffect(lastLoc, Effect.RECORD_PLAY, 0); + } + } CommentManager.sendTitle(pp, plot); } } @@ -169,13 +184,10 @@ public class PlotListener extends APlotListener { if (FlagManager.getPlotFlag(plot, "weather") != null) { player.resetPlayerWeather(); } - if (FlagManager.getPlotFlag(plot, "music") != null) { - Location center = MainUtil.getPlotCenter(plot); - for (int i = 0; i < 256; i+= 16) { - org.bukkit.Location newLoc = BukkitUtil.getLocation(center); - newLoc.setY(i); - player.playEffect(newLoc, Effect.RECORD_PLAY, 0); - } + org.bukkit.Location lastLoc = (org.bukkit.Location) pp.getMeta("music"); + if (lastLoc != null) { + pp.deleteMeta("music"); + player.playEffect(lastLoc, Effect.RECORD_PLAY, 0); } } diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/listeners/PlotPlusListener.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/listeners/PlotPlusListener.java index b30f24be1..1ff7ab1b7 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/listeners/PlotPlusListener.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/listeners/PlotPlusListener.java @@ -199,7 +199,6 @@ public class PlotPlusListener extends PlotListener implements Listener { @EventHandler public void onPlotLeave(final PlayerLeavePlotEvent event) { final Player leaver = event.getPlayer(); - leaver.playEffect(leaver.getLocation(), Effect.RECORD_PLAY, 0); final Plot plot = event.getPlot(); if (FlagManager.getPlotFlag(plot, "farewell") != null) { event.getPlayer().sendMessage(ChatColor.translateAlternateColorCodes('&', C.PREFIX_FAREWELL.s().replaceAll("%id%", plot.id + "") + FlagManager.getPlotFlag(plot, "farewell").getValueString())); diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/object/PlotWorld.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/object/PlotWorld.java index 1aa4053d8..eee59475e 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/object/PlotWorld.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/object/PlotWorld.java @@ -28,7 +28,7 @@ import com.intellectualcrafters.plot.flag.Flag; import com.intellectualcrafters.plot.flag.FlagManager; import com.intellectualcrafters.plot.util.EconHandler; import org.apache.commons.lang.StringUtils; -import org.bukkit.configuration.ConfigurationSection; +import com.intellectualcrafters.configuration.ConfigurationSection; import java.util.ArrayList; import java.util.HashMap; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/Metrics.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/Metrics.java index e5a1d0867..b12e6be97 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/Metrics.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/Metrics.java @@ -42,8 +42,8 @@ import java.util.logging.Level; import java.util.zip.GZIPOutputStream; import org.bukkit.Bukkit; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; +import com.intellectualcrafters.configuration.InvalidConfigurationException; +import com.intellectualcrafters.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/FancyMessage.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/FancyMessage.java index 64fecb47b..b10e0ce4e 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/FancyMessage.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/FancyMessage.java @@ -24,8 +24,8 @@ import org.bukkit.Material; import org.bukkit.Statistic; import org.bukkit.Statistic.Type; import org.bukkit.command.CommandSender; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.configuration.serialization.ConfigurationSerialization; +import com.intellectualcrafters.configuration.serialization.ConfigurationSerializable; +import com.intellectualcrafters.configuration.serialization.ConfigurationSerialization; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/JsonString.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/JsonString.java index 4453aeb72..5a6d6c26c 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/JsonString.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/JsonString.java @@ -4,7 +4,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import org.bukkit.configuration.serialization.ConfigurationSerializable; +import com.intellectualcrafters.configuration.serialization.ConfigurationSerializable; import com.google.gson.stream.JsonWriter; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/MessagePart.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/MessagePart.java index 7da75d714..bb3f94c31 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/MessagePart.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/MessagePart.java @@ -8,8 +8,8 @@ import java.util.logging.Level; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.configuration.serialization.ConfigurationSerialization; +import com.intellectualcrafters.configuration.serialization.ConfigurationSerializable; +import com.intellectualcrafters.configuration.serialization.ConfigurationSerialization; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableBiMap; diff --git a/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/TextualComponent.java b/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/TextualComponent.java index 2be0c8d1d..a57cab317 100644 --- a/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/TextualComponent.java +++ b/PlotSquared/src/main/java/com/intellectualcrafters/plot/util/bukkit/chat/TextualComponent.java @@ -4,8 +4,8 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.configuration.serialization.ConfigurationSerialization; +import com.intellectualcrafters.configuration.serialization.ConfigurationSerializable; +import com.intellectualcrafters.configuration.serialization.ConfigurationSerialization; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; diff --git a/PlotSquared/src/main/java/com/intellectualsites/translation/YamlTranslationFile.java b/PlotSquared/src/main/java/com/intellectualsites/translation/YamlTranslationFile.java index d6ee8d1d2..245c0dd9f 100644 --- a/PlotSquared/src/main/java/com/intellectualsites/translation/YamlTranslationFile.java +++ b/PlotSquared/src/main/java/com/intellectualsites/translation/YamlTranslationFile.java @@ -9,7 +9,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.bukkit.configuration.file.YamlConfiguration; +import com.intellectualcrafters.configuration.file.YamlConfiguration; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml;