Calling code could access sturmeh's baby eating state by using
+ * getBoolean("sturmeh.eats.babies", false). For lists, there are
+ * methods such as getStringList that will return a type safe list.
+ *
+ *
This class is currently incomplete. It is not yet possible to get a node.
+ *
+ *
+ */
+public class Configuration extends ConfigurationNode {
+ private Yaml yaml;
+ private File file;
+ private String header = null;
+
+
+ public Configuration(File file) {
+ super(new HashMap());
+
+ DumperOptions options = new DumperOptions();
+
+ options.setIndent(4);
+ options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+
+ yaml = new Yaml(new SafeConstructor(), new EmptyNullRepresenter(), options);
+
+ this.file = file;
+ }
+
+ /**
+ * Loads the configuration file. All errors are thrown away.
+ */
+ public void load() {
+ FileInputStream stream = null;
+
+ try {
+ stream = new FileInputStream(file);
+ read(yaml.load(new UnicodeReader(stream)));
+ } catch (IOException e) {
+ root = new HashMap();
+ } catch (ConfigurationException e) {
+ root = new HashMap();
+ } finally {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {}
+ }
+ }
+
+ /**
+ * Set the header for the file as a series of lines that are terminated
+ * by a new line sequence.
+ *
+ * @param headerLines header lines to prepend
+ */
+ public void setHeader(String... headerLines) {
+ StringBuilder header = new StringBuilder();
+
+ for (String line : headerLines) {
+ if (header.length() > 0) {
+ header.append("\r\n");
+ }
+ header.append(line);
+ }
+
+ setHeader(header.toString());
+ }
+
+ /**
+ * Set the header for the file. A header can be provided to prepend the
+ * YAML data output on configuration save. The header is
+ * printed raw and so must be manually commented if used. A new line will
+ * be appended after the header, however, if a header is provided.
+ *
+ * @param header header to prepend
+ */
+ public void setHeader(String header) {
+ this.header = header;
+ }
+
+ /**
+ * Return the set header.
+ *
+ * @return The header comment.
+ */
+ public String getHeader() {
+ return header;
+ }
+
+ /**
+ * Saves the configuration to disk. All errors are clobbered.
+ *
+ * @return true if it was successful
+ */
+ public boolean save() {
+ FileOutputStream stream = null;
+
+ File parent = file.getParentFile();
+
+ if (parent != null) {
+ parent.mkdirs();
+ }
+
+ try {
+ stream = new FileOutputStream(file);
+ OutputStreamWriter writer = new OutputStreamWriter(stream, "UTF-8");
+ if (header != null) {
+ writer.append(header);
+ writer.append("\r\n");
+ }
+ yaml.dump(root, writer);
+ return true;
+ } catch (IOException e) {} finally {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {}
+ }
+
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void read(Object input) throws ConfigurationException {
+ try {
+ if (null == input) {
+ root = new HashMap();
+ } else {
+ root = (Map) input;
+ }
+ } catch (ClassCastException e) {
+ throw new ConfigurationException("Root document must be an key-value structure");
+ }
+ }
+
+ /**
+ * This method returns an empty ConfigurationNode for using as a
+ * default in methods that select a node from a node list.
+ * @return The empty node.
+ */
+ public static ConfigurationNode getEmptyNode() {
+ return new ConfigurationNode(new HashMap());
+ }
+}
+
+class EmptyNullRepresenter extends Representer {
+
+ public EmptyNullRepresenter() {
+ super();
+ this.nullRepresenter = new EmptyRepresentNull();
+ }
+
+ protected class EmptyRepresentNull implements Represent {
+ public Node representData(Object data) {
+ return representScalar(Tag.NULL, ""); // Changed "null" to "" so as to avoid writing nulls
+ }
+ }
+
+ // Code borrowed from snakeyaml (http://code.google.com/p/snakeyaml/source/browse/src/test/java/org/yaml/snakeyaml/issues/issue60/SkipBeanTest.java)
+ @Override
+ protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, Tag customTag) {
+ NodeTuple tuple = super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
+ Node valueNode = tuple.getValueNode();
+ if (valueNode instanceof CollectionNode) {
+ // Removed null check
+ if (Tag.SEQ.equals(valueNode.getTag())) {
+ SequenceNode seq = (SequenceNode) valueNode;
+ if (seq.getValue().isEmpty()) {
+ return null; // skip empty lists
+ }
+ }
+ if (Tag.MAP.equals(valueNode.getTag())) {
+ MappingNode seq = (MappingNode) valueNode;
+ if (seq.getValue().isEmpty()) {
+ return null; // skip empty maps
+ }
+ }
+ }
+ return tuple;
+ }
+ // End of borrowed code
+}
\ No newline at end of file
diff --git a/src/org/bukkit/util/config/ConfigurationException.java b/src/org/bukkit/util/config/ConfigurationException.java
new file mode 100644
index 0000000..5dfc0f0
--- /dev/null
+++ b/src/org/bukkit/util/config/ConfigurationException.java
@@ -0,0 +1,24 @@
+package org.bukkit.util.config;
+
+/*
+ * Revived classes from a now-ancient version of the bukkit api.
+ *
+ * https://github.com/Bukkit/Bukkit/tree/55f405f4a855fcf165e02379cae5bebc76c517d4/src/main/java/org/bukkit/util/config
+ */
+
+/**
+ * Configuration exception.
+ *
+ * @author sk89q
+ */
+public class ConfigurationException extends Exception {
+ private static final long serialVersionUID = -2442886939908724203L;
+
+ public ConfigurationException() {
+ super();
+ }
+
+ public ConfigurationException(String msg) {
+ super(msg);
+ }
+}
\ No newline at end of file
diff --git a/src/org/bukkit/util/config/ConfigurationNode.java b/src/org/bukkit/util/config/ConfigurationNode.java
new file mode 100644
index 0000000..4440f2d
--- /dev/null
+++ b/src/org/bukkit/util/config/ConfigurationNode.java
@@ -0,0 +1,593 @@
+package org.bukkit.util.config;
+
+/*
+ * Revived classes from a now-ancient version of the bukkit api.
+ *
+ * https://github.com/Bukkit/Bukkit/tree/55f405f4a855fcf165e02379cae5bebc76c517d4/src/main/java/org/bukkit/util/config
+ */
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * Represents a configuration node.
+ */
+public class ConfigurationNode {
+ protected Map root;
+
+ protected ConfigurationNode(Map root) {
+ this.root = root;
+ }
+
+ /**
+ * Gets all of the cofiguration values within the Node as
+ * a key value pair, with the key being the full path and the
+ * value being the Object that is at the path.
+ *
+ * @return A map of key value pairs with the path as the key and the object as the value
+ */
+ public Map getAll() {
+ return recursiveBuilder(root);
+ }
+
+ /**
+ * A helper method for the getAll method that deals with the recursion
+ * involved in traversing the tree
+ *
+ * @param node The map for that node of the tree
+ * @return The fully pathed map for that point in the tree, with the path as the key
+ */
+ @SuppressWarnings("unchecked")
+ protected Map recursiveBuilder(Map node) {
+ Map map = new TreeMap();
+
+ Set keys = node.keySet();
+ for( String k : keys ) {
+ Object tmp = node.get(k);
+ if( tmp instanceof Map,?> ) {
+ Map rec = recursiveBuilder((Map ) tmp);
+
+ Set subkeys = rec.keySet();
+ for( String sk : subkeys ) {
+ map.put(k + "." + sk, rec.get(sk));
+ }
+ }
+ else {
+ map.put(k, tmp);
+ }
+ }
+
+ return map;
+ }
+
+ /**
+ * Gets a property at a location. This will either return an Object
+ * or null, with null meaning that no configuration value exists at
+ * that location. This could potentially return a default value (not yet
+ * implemented) as defined by a plugin, if this is a plugin-tied
+ * configuration.
+ *
+ * @param path path to node (dot notation)
+ * @return object or null
+ */
+ @SuppressWarnings("unchecked")
+ public Object getProperty(String path) {
+ if (!path.contains(".")) {
+ Object val = root.get(path);
+
+ if (val == null) {
+ return null;
+ }
+ return val;
+ }
+
+ String[] parts = path.split("\\.");
+ Map node = root;
+
+ for (int i = 0; i < parts.length; i++) {
+ Object o = node.get(parts[i]);
+
+ if (o == null) {
+ return null;
+ }
+
+ if (i == parts.length - 1) {
+ return o;
+ }
+
+ try {
+ node = (Map) o;
+ } catch (ClassCastException e) {
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Set the property at a location. This will override existing
+ * configuration data to have it conform to key/value mappings.
+ *
+ * @param path The property path
+ * @param value The new value
+ */
+ @SuppressWarnings("unchecked")
+ public void setProperty(String path, Object value) {
+ if (!path.contains(".")) {
+ root.put(path, value);
+ return;
+ }
+
+ String[] parts = path.split("\\.");
+ Map node = root;
+
+ for (int i = 0; i < parts.length; i++) {
+ Object o = node.get(parts[i]);
+
+ // Found our target!
+ if (i == parts.length - 1) {
+ node.put(parts[i], value);
+ return;
+ }
+
+ if (o == null || !(o instanceof Map)) {
+ // This will override existing configuration data!
+ o = new HashMap();
+ node.put(parts[i], o);
+ }
+
+ node = (Map) o;
+ }
+ }
+
+ /**
+ * Gets a string at a location. This will either return an String
+ * or null, with null meaning that no configuration value exists at
+ * that location. If the object at the particular location is not actually
+ * a string, it will be converted to its string representation.
+ *
+ * @param path path to node (dot notation)
+ * @return string or null
+ */
+ public String getString(String path) {
+ Object o = getProperty(path);
+
+ if (o == null) {
+ return null;
+ }
+ return o.toString();
+ }
+
+ /**
+ * Gets a string at a location. This will either return an String
+ * or the default value. If the object at the particular location is not
+ * actually a string, it will be converted to its string representation.
+ *
+ * @param path path to node (dot notation)
+ * @param def default value
+ * @return string or default
+ */
+ public String getString(String path, String def) {
+ String o = getString(path);
+
+ if (o == null) {
+ setProperty(path, def);
+ return def;
+ }
+ return o;
+ }
+
+ /**
+ * Gets an integer at a location. This will either return an integer
+ * or the default value. If the object at the particular location is not
+ * actually a integer, the default value will be returned. However, other
+ * number types will be casted to an integer.
+ *
+ * @param path path to node (dot notation)
+ * @param def default value
+ * @return int or default
+ */
+ public int getInt(String path, int def) {
+ Integer o = castInt(getProperty(path));
+
+ if (o == null) {
+ setProperty(path, def);
+ return def;
+ } else {
+ return o;
+ }
+ }
+
+ /**
+ * Gets a double at a location. This will either return an double
+ * or the default value. If the object at the particular location is not
+ * actually a double, the default value will be returned. However, other
+ * number types will be casted to an double.
+ *
+ * @param path path to node (dot notation)
+ * @param def default value
+ * @return double or default
+ */
+ public double getDouble(String path, double def) {
+ Double o = castDouble(getProperty(path));
+
+ if (o == null) {
+ setProperty(path, def);
+ return def;
+ } else {
+ return o;
+ }
+ }
+
+ /**
+ * Gets a boolean at a location. This will either return an boolean
+ * or the default value. If the object at the particular location is not
+ * actually a boolean, the default value will be returned.
+ *
+ * @param path path to node (dot notation)
+ * @param def default value
+ * @return boolean or default
+ */
+ public boolean getBoolean(String path, boolean def) {
+ Boolean o = castBoolean(getProperty(path));
+
+ if (o == null) {
+ setProperty(path, def);
+ return def;
+ } else {
+ return o;
+ }
+ }
+
+ /**
+ * Get a list of keys at a location. If the map at the particular location
+ * does not exist or it is not a map, null will be returned.
+ *
+ * @param path path to node (dot notation)
+ * @return list of keys
+ */
+ @SuppressWarnings("unchecked")
+ public List getKeys(String path) {
+ if (path == null) {
+ return new ArrayList(root.keySet());
+ }
+ Object o = getProperty(path);
+
+ if (o == null) {
+ return null;
+ } else if (o instanceof Map) {
+ return new ArrayList(((Map) o).keySet());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a list of all keys at the root path
+ *
+ * @return List of keys
+ */
+ public List getKeys() {
+ return new ArrayList(root.keySet());
+ }
+
+ /**
+ * Gets a list of objects at a location. If the list is not defined,
+ * null will be returned. The node must be an actual list.
+ *
+ * @param path path to node (dot notation)
+ * @return boolean or default
+ */
+ @SuppressWarnings("unchecked")
+ public List