From 52982c7c0f4ea676ad53762930a3a2e517218c86 Mon Sep 17 00:00:00 2001 From: zml2008 Date: Sat, 22 Oct 2011 22:22:50 -0700 Subject: [PATCH] Moved YAML classes from RegionBook from WorldGuard into WorldEdit and made everything use those. --- .../ConfigurationPermissionsResolver.java | 30 +- .../bukkit/migration/DinnerPermsResolver.java | 4 +- .../FlatFilePermissionsResolver.java | 5 +- .../migration/NijiPermissionsResolver.java | 28 +- .../migration/PermissionsExResolver.java | 5 +- .../migration/PermissionsResolverManager.java | 52 +- .../migration/PluginPermissionsResolver.java | 4 +- .../java/com/sk89q/util/yaml/YAMLFormat.java | 18 + .../java/com/sk89q/util/yaml/YAMLNode.java | 797 ++++++++++++++++++ .../com/sk89q/util/yaml/YAMLProcessor.java | 206 +++++ .../util/yaml/YAMLProcessorException.java | 37 + .../worldedit/bukkit/BukkitConfiguration.java | 13 +- .../worldedit/bukkit/WorldEditPlugin.java | 4 +- 13 files changed, 1126 insertions(+), 77 deletions(-) create mode 100644 src/main/java/com/sk89q/util/yaml/YAMLFormat.java create mode 100644 src/main/java/com/sk89q/util/yaml/YAMLNode.java create mode 100644 src/main/java/com/sk89q/util/yaml/YAMLProcessor.java create mode 100644 src/main/java/com/sk89q/util/yaml/YAMLProcessorException.java diff --git a/src/main/java/com/sk89q/bukkit/migration/ConfigurationPermissionsResolver.java b/src/main/java/com/sk89q/bukkit/migration/ConfigurationPermissionsResolver.java index 766210231..0646998a9 100644 --- a/src/main/java/com/sk89q/bukkit/migration/ConfigurationPermissionsResolver.java +++ b/src/main/java/com/sk89q/bukkit/migration/ConfigurationPermissionsResolver.java @@ -21,27 +21,27 @@ package com.sk89q.bukkit.migration; import java.util.*; -import org.bukkit.configuration.Configuration; -import org.bukkit.configuration.ConfigurationSection; +import com.sk89q.util.yaml.YAMLNode; +import com.sk89q.util.yaml.YAMLProcessor; public class ConfigurationPermissionsResolver implements PermissionsResolver { - private Configuration config; + private YAMLProcessor config; private Map> userPermissionsCache; private Set defaultPermissionsCache; private Map> userGroups; - public ConfigurationPermissionsResolver(Configuration config) { + public ConfigurationPermissionsResolver(YAMLProcessor config) { this.config = config; } - public static ConfigurationSection generateDefaultPerms(ConfigurationSection section) { - section.set("permissions.groups.default.permissions", Arrays.asList( + public static YAMLNode generateDefaultPerms(YAMLNode section) { + section.setProperty("permissions.groups.default.permissions", new String[] { "worldedit.reload", "worldedit.selection", - "worlds.creative.worldedit.region")); - section.set("permissions.groups.admins.permissions", Arrays.asList("*")); - section.set("permissions.users.sk89q.permissions", Arrays.asList("worldedit")); - section.set("permissions.users.sk89q.groups", Arrays.asList("admins")); + "worlds.creative.worldedit.region"}); + section.setProperty("permissions.groups.admins.permissions", new String[]{"*"}); + section.setProperty("permissions.users.sk89q.permissions", new String[]{"worldedit"}); + section.setProperty("permissions.users.sk89q.groups", new String[]{"admins"}); return section; } @@ -52,12 +52,12 @@ public class ConfigurationPermissionsResolver implements PermissionsResolver { Map> userGroupPermissions = new HashMap>(); - List groupKeys = config.getList("permissions.groups"); + List groupKeys = config.getStringList("permissions.groups", null); if (groupKeys != null) { for (String key : groupKeys) { List permissions = - config.getList("permissions.groups." + key + ".permissions", null); + config.getStringList("permissions.groups." + key + ".permissions", null); if (permissions.size() > 0) { Set groupPerms = new HashSet(permissions); @@ -70,21 +70,21 @@ public class ConfigurationPermissionsResolver implements PermissionsResolver { } } - List userKeys = config.getList("permissions.users"); + List userKeys = config.getStringList("permissions.users", null); if (userKeys != null) { for (String key : userKeys) { Set permsCache = new HashSet(); List permissions = - config.getList("permissions.users." + key + ".permissions", null); + config.getStringList("permissions.users." + key + ".permissions", null); if (permissions.size() > 0) { permsCache.addAll(permissions); } List groups = - config.getList("permissions.users." + key + ".groups", null); + config.getStringList("permissions.users." + key + ".groups", null); groups.add("default"); if (groups.size() > 0) { diff --git a/src/main/java/com/sk89q/bukkit/migration/DinnerPermsResolver.java b/src/main/java/com/sk89q/bukkit/migration/DinnerPermsResolver.java index c2dd6ae90..344db2352 100644 --- a/src/main/java/com/sk89q/bukkit/migration/DinnerPermsResolver.java +++ b/src/main/java/com/sk89q/bukkit/migration/DinnerPermsResolver.java @@ -19,6 +19,7 @@ package com.sk89q.bukkit.migration; +import com.sk89q.util.yaml.YAMLProcessor; import org.bukkit.Server; import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permission; @@ -26,7 +27,6 @@ import org.bukkit.permissions.PermissionAttachmentInfo; import java.util.ArrayList; import java.util.List; -import org.bukkit.configuration.Configuration; public class DinnerPermsResolver implements PermissionsResolver { @@ -37,7 +37,7 @@ public class DinnerPermsResolver implements PermissionsResolver { this.server = server; } - public static PermissionsResolver factory(Server server, Configuration config) { + public static PermissionsResolver factory(Server server, YAMLProcessor config) { return new DinnerPermsResolver(server); } diff --git a/src/main/java/com/sk89q/bukkit/migration/FlatFilePermissionsResolver.java b/src/main/java/com/sk89q/bukkit/migration/FlatFilePermissionsResolver.java index ec0ab92df..6d9180b41 100644 --- a/src/main/java/com/sk89q/bukkit/migration/FlatFilePermissionsResolver.java +++ b/src/main/java/com/sk89q/bukkit/migration/FlatFilePermissionsResolver.java @@ -28,8 +28,9 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.HashMap; + +import com.sk89q.util.yaml.YAMLProcessor; import org.bukkit.Server; -import org.bukkit.configuration.Configuration; public class FlatFilePermissionsResolver implements PermissionsResolver { private Map> userPermissionsCache; @@ -39,7 +40,7 @@ public class FlatFilePermissionsResolver implements PermissionsResolver { protected File groupFile; protected File userFile; - public static PermissionsResolver factory(Server server, Configuration config) { + public static PermissionsResolver factory(Server server, YAMLProcessor config) { File groups = new File("perms_groups.txt"); File users = new File("perms_users.txt"); diff --git a/src/main/java/com/sk89q/bukkit/migration/NijiPermissionsResolver.java b/src/main/java/com/sk89q/bukkit/migration/NijiPermissionsResolver.java index 71f0f8a16..ab6c80144 100644 --- a/src/main/java/com/sk89q/bukkit/migration/NijiPermissionsResolver.java +++ b/src/main/java/com/sk89q/bukkit/migration/NijiPermissionsResolver.java @@ -19,6 +19,7 @@ package com.sk89q.bukkit.migration; +import com.sk89q.util.yaml.YAMLProcessor; import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.command.PluginCommand; @@ -26,13 +27,12 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import com.nijikokun.bukkit.Permissions.Permissions; -import org.bukkit.configuration.Configuration; public class NijiPermissionsResolver implements PermissionsResolver { private Server server; private Permissions api; - public static PermissionsResolver factory(Server server, Configuration config) { + public static PermissionsResolver factory(Server server, YAMLProcessor config) { PluginManager pluginManager = server.getPluginManager(); Plugin plugin = pluginManager.getPlugin("Permissions"); @@ -62,7 +62,7 @@ public class NijiPermissionsResolver implements PermissionsResolver { @SuppressWarnings("static-access") public boolean hasPermission(String name, String permission) { try { - Player player = server.getPlayer(name); + Player player = server.getPlayerExact(name); if (player == null) return false; try { return api.getHandler().has(player, permission); @@ -80,7 +80,7 @@ public class NijiPermissionsResolver implements PermissionsResolver { try { return api.getHandler().has(worldName, name, permission); } catch (Throwable t) { - return api.getHandler().has(server.getPlayer(name), permission); + return api.getHandler().has(server.getPlayerExact(name), permission); } } catch (Throwable t) { t.printStackTrace(); @@ -91,7 +91,7 @@ public class NijiPermissionsResolver implements PermissionsResolver { @SuppressWarnings("static-access") public boolean inGroup(String name, String group) { try { - Player player = server.getPlayer(name); + Player player = server.getPlayerExact(name); if (player == null) return false; try { return api.getHandler().inGroup(player.getWorld().getName(), name, group); @@ -107,7 +107,7 @@ public class NijiPermissionsResolver implements PermissionsResolver { @SuppressWarnings("static-access") public String[] getGroups(String name) { try { - Player player = server.getPlayer(name); + Player player = server.getPlayerExact(name); if (player == null) return new String[0]; String[] groups = null; try { @@ -127,22 +127,6 @@ public class NijiPermissionsResolver implements PermissionsResolver { } } - public static class PluginAccessException extends Exception { - private static final long serialVersionUID = 7044832912491608706L; - } - - @Deprecated - public static boolean checkRealNijiPerms(boolean ignoreBridges) { - if (!ignoreBridges) { - return true; - } - PluginCommand permsCommand = Bukkit.getServer().getPluginCommand("permissions"); - if (permsCommand == null) { - return false; - } - return permsCommand.getPlugin().getDescription().getName().equals("Permissions"); - } - public static boolean isFakeNijiPerms(Plugin plugin) { PluginCommand permsCommand = Bukkit.getServer().getPluginCommand("permissions"); diff --git a/src/main/java/com/sk89q/bukkit/migration/PermissionsExResolver.java b/src/main/java/com/sk89q/bukkit/migration/PermissionsExResolver.java index d45b881f4..84b86fe01 100644 --- a/src/main/java/com/sk89q/bukkit/migration/PermissionsExResolver.java +++ b/src/main/java/com/sk89q/bukkit/migration/PermissionsExResolver.java @@ -19,19 +19,18 @@ package com.sk89q.bukkit.migration; +import com.sk89q.util.yaml.YAMLProcessor; import org.bukkit.Server; import org.bukkit.entity.Player; import ru.tehkode.permissions.PermissionManager; import ru.tehkode.permissions.PermissionUser; -import org.bukkit.configuration.Configuration; - public class PermissionsExResolver implements PermissionsResolver { private final PermissionManager manager; private final Server server; - public static PermissionsResolver factory(Server server, Configuration config) { + public static PermissionsResolver factory(Server server, YAMLProcessor config) { PermissionManager manager = server.getServicesManager().load(PermissionManager.class); if (manager == null) { diff --git a/src/main/java/com/sk89q/bukkit/migration/PermissionsResolverManager.java b/src/main/java/com/sk89q/bukkit/migration/PermissionsResolverManager.java index d5b99dff5..b6a81c839 100644 --- a/src/main/java/com/sk89q/bukkit/migration/PermissionsResolverManager.java +++ b/src/main/java/com/sk89q/bukkit/migration/PermissionsResolverManager.java @@ -23,14 +23,12 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.util.*; -import java.util.logging.Level; import java.util.logging.Logger; + +import com.sk89q.util.yaml.YAMLFormat; +import com.sk89q.util.yaml.YAMLProcessor; import org.bukkit.Server; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.configuration.file.YamlConfigurationOptions; import org.bukkit.plugin.Plugin; -import org.bukkit.configuration.Configuration; public class PermissionsResolverManager implements PermissionsResolver { private static final String CONFIG_HEADER = "#\r\n" + @@ -59,7 +57,7 @@ public class PermissionsResolverManager implements PermissionsResolver { private Server server; private PermissionsResolver permissionResolver; private PermissionsResolverServerListener listener; - private FileConfiguration config; + private YAMLProcessor config; private String name; private Logger logger; private List> enabledResolvers = new ArrayList>(); @@ -95,7 +93,7 @@ public class PermissionsResolverManager implements PermissionsResolver { public void findResolver() { for (Class resolverClass : enabledResolvers) { try { - Method factoryMethod = resolverClass.getMethod("factory", Server.class, Configuration.class); + Method factoryMethod = resolverClass.getMethod("factory", Server.class, YAMLProcessor.class); this.permissionResolver = (PermissionsResolver) factoryMethod.invoke(null, this.server, this.config); @@ -153,27 +151,33 @@ public class PermissionsResolverManager implements PermissionsResolver { e.printStackTrace(); } } - config = YamlConfiguration.loadConfiguration(file); - ((YamlConfigurationOptions)config.options()).indent(4); - Set keys = config.getKeys(true); - config.options().header(CONFIG_HEADER); + config = new YAMLProcessor(file, false, YAMLFormat.EXTENDED); + try { + config.load(); + } catch (IOException e) { + logger.severe("Error loading WEPIF Config: " + e); + e.printStackTrace(); + } + List keys = config.getKeys(null); + config.setHeader(CONFIG_HEADER); if (!keys.contains("ignore-nijiperms-bridges")) { - config.set("ignore-nijiperms-bridges", true); + config.setProperty("ignore-nijiperms-bridges", true); isUpdated = true; } - if (!keys.contains("resolvers.enabled")) { + if (!keys.contains("resolvers")) { + List resolverKeys = config.getKeys("resolvers"); List resolvers = new ArrayList(); for (Class clazz : availableResolvers) { resolvers.add(clazz.getSimpleName()); } enabledResolvers.addAll(Arrays.asList(availableResolvers)); - config.set("resolvers.enabled", resolvers); + config.setProperty("resolvers.enabled", resolvers); isUpdated = true; } else { - List disabledResolvers = config.getList("resolvers.disabled", new ArrayList()); - List stagedEnabled = config.getList("resolvers.enabled"); + List disabledResolvers = config.getStringList("resolvers.disabled", new ArrayList()); + List stagedEnabled = config.getStringList("resolvers.enabled", null); for (Iterator i = stagedEnabled.iterator(); i.hasNext();) { String nextName = i.next(); Class next = null; @@ -202,27 +206,23 @@ public class PermissionsResolverManager implements PermissionsResolver { isUpdated = true; } } - config.set("resolvers.disabled", disabledResolvers); - config.set("resolvers.enabled", stagedEnabled); + config.setProperty("resolvers.disabled", disabledResolvers); + config.setProperty("resolvers.enabled", stagedEnabled); } if (keys.contains("dinner-perms") || keys.contains("dinnerperms")) { - config.set("dinner-perms", null); - config.set("dinnerperms", null); + config.setProperty("dinner-perms", null); + config.setProperty("dinnerperms", null); isUpdated = true; } if (!keys.contains("permissions")) { ConfigurationPermissionsResolver.generateDefaultPerms( - config.createSection("permissions")); + config.addNode("permissions")); isUpdated = true; } if (isUpdated) { logger.info("WEPIF: Updated config file"); - try { - config.save(file); - } catch (IOException e) { - logger.log(Level.SEVERE, "WEPIF: Unable to save config file", e); - } + config.save(); } return isUpdated; } diff --git a/src/main/java/com/sk89q/bukkit/migration/PluginPermissionsResolver.java b/src/main/java/com/sk89q/bukkit/migration/PluginPermissionsResolver.java index 39c983299..da23002ea 100644 --- a/src/main/java/com/sk89q/bukkit/migration/PluginPermissionsResolver.java +++ b/src/main/java/com/sk89q/bukkit/migration/PluginPermissionsResolver.java @@ -19,17 +19,17 @@ package com.sk89q.bukkit.migration; +import com.sk89q.util.yaml.YAMLProcessor; import org.bukkit.Server; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.RegisteredServiceProvider; -import org.bukkit.configuration.Configuration; public class PluginPermissionsResolver implements PermissionsResolver { protected PermissionsProvider resolver; protected Plugin plugin; - public static PermissionsResolver factory(Server server, Configuration config) { + public static PermissionsResolver factory(Server server, YAMLProcessor config) { // Looking for service RegisteredServiceProvider serviceProvider = server.getServicesManager().getRegistration(PermissionsProvider.class); diff --git a/src/main/java/com/sk89q/util/yaml/YAMLFormat.java b/src/main/java/com/sk89q/util/yaml/YAMLFormat.java new file mode 100644 index 000000000..bacd18baf --- /dev/null +++ b/src/main/java/com/sk89q/util/yaml/YAMLFormat.java @@ -0,0 +1,18 @@ +package com.sk89q.util.yaml; + +import org.yaml.snakeyaml.DumperOptions.FlowStyle; + +public enum YAMLFormat { + EXTENDED(FlowStyle.BLOCK), + COMPACT(FlowStyle.AUTO); + + private final FlowStyle style; + + YAMLFormat(FlowStyle style) { + this.style = style; + } + + public FlowStyle getStyle() { + return style; + } +} diff --git a/src/main/java/com/sk89q/util/yaml/YAMLNode.java b/src/main/java/com/sk89q/util/yaml/YAMLNode.java new file mode 100644 index 000000000..333c8b075 --- /dev/null +++ b/src/main/java/com/sk89q/util/yaml/YAMLNode.java @@ -0,0 +1,797 @@ +// $Id$ +/* + * RegionBook + * Copyright (C) 2010, 2011 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package com.sk89q.util.yaml; + +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Represents a configuration node. + * + * @author sk89q + */ +public class YAMLNode { + protected Map root; + private boolean writeDefaults; + public YAMLNode(Map root, boolean writeDefaults) { + this.root = root; + this.writeDefaults = writeDefaults; + } + + /** + * Return the underlying map. + * + * @return the map + */ + public Map getMap() { + return root; + } + + /** + * Clear all nodes. + */ + public void clear() { + root.clear(); + } + + /** + * 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; + } + + /** + * Prepare a value for serialization, in case it's not a native type + * (and we don't want to serialize objects as YAML objects). + * + * @param value + * @return + */ + private Object prepareSerialization(Object value) { + if (value instanceof Vector) { + Map out = new HashMap(); + Vector vec = (Vector) value; + out.put("x", vec.getX()); + out.put("y", vec.getY()); + out.put("z", vec.getZ()); + return out; + } + + return value; + } + + /** + * Set the property at a location. This will override existing + * configuration data to have it conform to key/value mappings. + * + * @param path + * @param value + */ + @SuppressWarnings("unchecked") + public void setProperty(String path, Object value) { + value = prepareSerialization(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; + } + } + + /** + * Adds a new node to the given path. The returned object is a reference + * to the new node. This method will replace an existing node at + * the same path. See setProperty. + * + * @param path + * @return + */ + public YAMLNode addNode(String path) { + Map map = new HashMap(); + YAMLNode node = new YAMLNode(map, writeDefaults); + setProperty(path, map); + return node; + } + + /** + * 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 vector at a location. This will either return an Vector + * or a null. 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 default + */ + public Vector getVector(String path) { + YAMLNode o = getNode(path); + if (o == null) { + return null; + } + + Double x = o.getDouble("x"); + Double y = o.getDouble("y"); + Double z = o.getDouble("z"); + + if (x == null || y == null || z == null) { + return null; + } + + return new Vector(x, y, z); + } + + /** + * Gets a 2D vector at a location. This will either return an Vector + * or a null. 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 default + */ + public Vector2D getVector2d(String path) { + YAMLNode o = getNode(path); + if (o == null) { + return null; + } + + Double x = o.getDouble("x"); + Double z = o.getDouble("z"); + + if (x == null || z == null) { + return null; + } + + return new Vector2D(x, z); + } + + /** + * Gets a string at a location. This will either return an Vector + * 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 Vector getVector(String path, Vector def) { + Vector v = getVector(path); + if (v == null) { + if (writeDefaults) setProperty(path, def); + return def; + } + return v; + } + + /** + * 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) { + if (writeDefaults) setProperty(path, def); + return def; + } + return o; + } + + /** + * Gets an integer at a location. This will either return an integer + * or null. 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) + * @return integer or null + */ + public Integer getInt(String path) { + Integer o = castInt(getProperty(path)); + if (o == null) { + return null; + } else { + 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) { + if (writeDefaults) setProperty(path, def); + return def; + } else { + return o; + } + } + + /** + * Gets a double at a location. This will either return an double + * or null. 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) + * @return double or null + */ + public Double getDouble(String path) { + Double o = castDouble(getProperty(path)); + if (o == null) { + return null; + } 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) { + if (writeDefaults) setProperty(path, def); + return def; + } else { + return o; + } + } + + /** + * Gets a boolean at a location. This will either return an boolean + * or null. 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) + * @return boolean or null + */ + public Boolean getBoolean(String path) { + Boolean o = castBoolean(getProperty(path)); + if (o == null) { + return null; + } 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) { + if (writeDefaults) 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; + } + } + + /** + * 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 getList(String path) { + Object o = getProperty(path); + if (o == null) { + return null; + } else if (o instanceof List) { + return (List)o; + } else { + return null; + } + } + + /** + * Gets a list of strings. Non-valid entries will not be in the list. + * There will be no null slots. If the list is not defined, the + * default will be returned. 'null' can be passed for the default + * and an empty list will be returned instead. If an item in the list + * is not a string, it will be converted to a string. The node must be + * an actual list and not just a string. + * + * @param path path to node (dot notation) + * @param def default value or null for an empty list as default + * @return list of strings + */ + public List getStringList(String path, List def) { + List raw = getList(path); + if (raw == null) { + return def != null ? def : new ArrayList(); + } + + List list = new ArrayList(); + for (Object o : raw) { + if (o == null) { + continue; + } + + list.add(o.toString()); + } + + return list; + } + + /** + * Gets a list of integers. Non-valid entries will not be in the list. + * There will be no null slots. If the list is not defined, the + * default will be returned. 'null' can be passed for the default + * and an empty list will be returned instead. The node must be + * an actual list and not just an integer. + * + * @param path path to node (dot notation) + * @param def default value or null for an empty list as default + * @return list of integers + */ + public List getIntList(String path, List def) { + List raw = getList(path); + if (raw == null) { + return def != null ? def : new ArrayList(); + } + + List list = new ArrayList(); + for (Object o : raw) { + Integer i = castInt(o); + if (i != null) { + list.add(i); + } + } + + return list; + } + + /** + * Gets a list of doubles. Non-valid entries will not be in the list. + * There will be no null slots. If the list is not defined, the + * default will be returned. 'null' can be passed for the default + * and an empty list will be returned instead. The node must be + * an actual list and cannot be just a double. + * + * @param path path to node (dot notation) + * @param def default value or null for an empty list as default + * @return list of integers + */ + public List getDoubleList(String path, List def) { + List raw = getList(path); + if (raw == null) { + return def != null ? def : new ArrayList(); + } + + List list = new ArrayList(); + for (Object o : raw) { + Double i = castDouble(o); + if (i != null) { + list.add(i); + } + } + + return list; + } + + /** + * Gets a list of booleans. Non-valid entries will not be in the list. + * There will be no null slots. If the list is not defined, the + * default will be returned. 'null' can be passed for the default + * and an empty list will be returned instead. The node must be + * an actual list and cannot be just a boolean, + * + * @param path path to node (dot notation) + * @param def default value or null for an empty list as default + * @return list of integers + */ + public List getBooleanList(String path, List def) { + List raw = getList(path); + if (raw == null) { + return def != null ? def : new ArrayList(); + } + + List list = new ArrayList(); + for (Object o : raw) { + Boolean tetsu = castBoolean(o); + if (tetsu != null) { + list.add(tetsu); + } + } + + return list; + } + + /** + * Gets a list of vectors. Non-valid entries will not be in the list. + * There will be no null slots. If the list is not defined, the + * default will be returned. 'null' can be passed for the default + * and an empty list will be returned instead. The node must be + * an actual node and cannot be just a vector, + * + * @param path path to node (dot notation) + * @param def default value or null for an empty list as default + * @return list of integers + */ + public List getVectorList( + String path, List def) { + + List raw = getNodeList(path, null); + List list = new ArrayList(); + + for (YAMLNode o : raw) { + Double x = o.getDouble("x"); + Double y = o.getDouble("y"); + Double z = o.getDouble("z"); + + if (x == null || y == null || z == null) { + continue; + } + + list.add(new Vector(x, y, z)); + } + + return list; + } + + /** + * Gets a list of 2D vectors. Non-valid entries will not be in the list. + * There will be no null slots. If the list is not defined, the + * default will be returned. 'null' can be passed for the default + * and an empty list will be returned instead. The node must be + * an actual node and cannot be just a vector, + * + * @param path path to node (dot notation) + * @param def default value or null for an empty list as default + * @return list of integers + */ + public List getVector2dList( + String path, List def) { + + List raw = getNodeList(path, null); + List list = new ArrayList(); + + for (YAMLNode o : raw) { + Double x = o.getDouble("x"); + Double z = o.getDouble("z"); + + if (x == null || z == null) { + continue; + } + + list.add(new Vector2D(x, z)); + } + + return list; + } + + /** + * Gets a list of 2D vectors. Non-valid entries will not be in the list. + * There will be no null slots. If the list is not defined, the + * default will be returned. 'null' can be passed for the default + * and an empty list will be returned instead. The node must be + * an actual node and cannot be just a vector, + * + * @param path path to node (dot notation) + * @param def default value or null for an empty list as default + * @return list of integers + */ + public List getBlockVector2dList( + String path, List def) { + + List raw = getNodeList(path, null); + List list = new ArrayList(); + + for (YAMLNode o : raw) { + Double x = o.getDouble("x"); + Double z = o.getDouble("z"); + + if (x == null || z == null) { + continue; + } + + list.add(new BlockVector2D(x, z)); + } + + return list; + } + + /** + * Gets a list of nodes. Non-valid entries will not be in the list. + * There will be no null slots. If the list is not defined, the + * default will be returned. 'null' can be passed for the default + * and an empty list will be returned instead. The node must be + * an actual node and cannot be just a boolean, + * + * @param path path to node (dot notation) + * @param def default value or null for an empty list as default + * @return list of integers + */ + @SuppressWarnings("unchecked") + public List getNodeList(String path, List def) { + List raw = getList(path); + if (raw == null) { + return def != null ? def : new ArrayList(); + } + + List list = new ArrayList(); + for (Object o : raw) { + if (o instanceof Map) { + list.add(new YAMLNode((Map)o, writeDefaults)); + } + } + + return list; + } + + /** + * Get a configuration node at a path. If the node doesn't exist or the + * path does not lead to a node, null will be returned. A node has + * key/value mappings. + * + * @param path + * @return node or null + */ + @SuppressWarnings("unchecked") + public YAMLNode getNode(String path) { + Object raw = getProperty(path); + if (raw instanceof Map) { + return new YAMLNode((Map)raw, writeDefaults); + } + + return null; + } + + /** + * Get a list of nodes 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 map of nodes + */ + @SuppressWarnings("unchecked") + public Map getNodes(String path) { + Object o = getProperty(path); + if (o == null) { + return null; + } else if (o instanceof Map) { + Map nodes = + new HashMap(); + + for (Map.Entry entry : ((Map)o).entrySet()) { + if (entry.getValue() instanceof Map) { + nodes.put(entry.getKey(), + new YAMLNode((Map) entry.getValue(), writeDefaults)); + } + } + + return nodes; + } else { + return null; + } + } + + /** + * Casts a value to an integer. May return null. + * + * @param o + * @return + */ + private static Integer castInt(Object o) { + if (o == null) { + return null; + } else if (o instanceof Number) { + return ((Number)o).intValue(); + } else { + return null; + } + } + + /** + * Casts a value to a double. May return null. + * + * @param o + * @return + */ + private static Double castDouble(Object o) { + if (o == null) { + return null; + } else if (o instanceof Number) { + return ((Number)o).doubleValue(); + } else { + return null; + } + } + + /** + * Casts a value to a boolean. May return null. + * + * @param o + * @return + */ + private static Boolean castBoolean(Object o) { + if (o == null) { + return null; + } else if (o instanceof Boolean) { + return (Boolean)o; + } else { + return null; + } + } + + /** + * Remove the property at a location. This will override existing + * configuration data to have it conform to key/value mappings. + * + * @param path + */ + @SuppressWarnings("unchecked") + public void removeProperty(String path) { + if (!path.contains(".")) { + root.remove(path); + 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.remove(parts[i]); + return; + } + + node = (Map)o; + } + } + + public boolean writeDefaults() { + return writeDefaults; + } + + public void setWriteDefaults(boolean writeDefaults) { + this.writeDefaults = writeDefaults; + } +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/util/yaml/YAMLProcessor.java b/src/main/java/com/sk89q/util/yaml/YAMLProcessor.java new file mode 100644 index 000000000..989f61530 --- /dev/null +++ b/src/main/java/com/sk89q/util/yaml/YAMLProcessor.java @@ -0,0 +1,206 @@ +// $Id$ +/* + * RegionBook + * Copyright (C) 2010, 2011 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package com.sk89q.util.yaml; + +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.reader.UnicodeReader; +import org.yaml.snakeyaml.representer.Representer; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; + +/** + * YAML configuration loader. To use this class, construct it with path to + * a file and call its load() method. For specifying node paths in the + * various get*() methods, they support SK's path notation, allowing you to + * select child nodes by delimiting node names with periods. + * + *

+ * For example, given the following configuration file:

+ * + *
members:
+ *     - Hollie
+ *     - Jason
+ *     - Bobo
+ *     - Aya
+ *     - Tetsu
+ * worldguard:
+ *     fire:
+ *         spread: false
+ *         blocks: [cloth, rock, glass]
+ * sturmeh:
+ *     cool: false
+ *     eats:
+ *         babies: true
+ * + *

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. + *

+ * + * @author sk89q + */ +public class YAMLProcessor extends YAMLNode { + private Yaml yaml; + private File file; + private String header = null; + + public YAMLProcessor(File file, boolean writeDefaults, YAMLFormat format) { + super(new HashMap(), writeDefaults); + + DumperOptions options = new DumperOptions(); + options.setIndent(4); + options.setDefaultFlowStyle(format.getStyle()); + Representer representer = new Representer(); + representer.setDefaultFlowStyle(format.getStyle()); + + yaml = new Yaml(new SafeConstructor(), representer, options); + + this.file = file; + } + + public YAMLProcessor(File file, boolean writeDefaults) { + this(file, writeDefaults, YAMLFormat.COMPACT); + } + + /** + * Loads the configuration file. + * + * @throws java.io.IOException + */ + public void load() throws IOException { + FileInputStream stream = null; + + try { + stream = new FileInputStream(file); + read(yaml.load(new UnicodeReader(stream))); + } catch (YAMLProcessorException 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 + */ + 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 YAMLProcessorException { + try { + if ( null == input ) { + root = new HashMap(); + } else { + root = (Map)input; + } + } catch (ClassCastException e) { + throw new YAMLProcessorException("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 + */ + public static YAMLNode getEmptyNode(boolean writeDefaults) { + return new YAMLNode(new HashMap(), writeDefaults); + } +} diff --git a/src/main/java/com/sk89q/util/yaml/YAMLProcessorException.java b/src/main/java/com/sk89q/util/yaml/YAMLProcessorException.java new file mode 100644 index 000000000..c53ffb0d2 --- /dev/null +++ b/src/main/java/com/sk89q/util/yaml/YAMLProcessorException.java @@ -0,0 +1,37 @@ +// $Id$ +/* + * RegionBook + * Copyright (C) 2010, 2011 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package com.sk89q.util.yaml; + +/** + * YAMLProcessor exception. + * + * @author sk89q + */ +public class YAMLProcessorException extends Exception { + private static final long serialVersionUID = -2442886939908724203L; + + public YAMLProcessorException() { + super(); + } + + public YAMLProcessorException(String msg) { + super(msg); + } +} diff --git a/src/main/java/com/sk89q/worldedit/bukkit/BukkitConfiguration.java b/src/main/java/com/sk89q/worldedit/bukkit/BukkitConfiguration.java index 3a12469d6..11b0415ea 100644 --- a/src/main/java/com/sk89q/worldedit/bukkit/BukkitConfiguration.java +++ b/src/main/java/com/sk89q/worldedit/bukkit/BukkitConfiguration.java @@ -25,26 +25,33 @@ import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; -import org.bukkit.util.config.Configuration; + +import com.sk89q.util.yaml.YAMLProcessor; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LogFormat; import com.sk89q.worldedit.snapshots.SnapshotRepository; public class BukkitConfiguration extends LocalConfiguration { - private Configuration config; + private YAMLProcessor config; private Logger logger; private FileHandler logFileHandler; public boolean noOpPermissions = false; - public BukkitConfiguration(Configuration config, Logger logger) { + public BukkitConfiguration(YAMLProcessor config, Logger logger) { this.config = config; this.logger = logger; } @Override public void load() { + try { + config.load(); + } catch (IOException e) { + logger.severe("Error loading WorldEdit configuration: " + e); + e.printStackTrace(); + } showFirstUseVersion = false; profile = config.getBoolean("debug", profile); diff --git a/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 0b96d3048..279454897 100644 --- a/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -28,6 +28,7 @@ import java.util.jar.JarFile; import java.util.logging.Logger; import java.util.zip.ZipEntry; +import com.sk89q.util.yaml.YAMLProcessor; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -89,7 +90,7 @@ public class WorldEditPlugin extends JavaPlugin { // Set up configuration and such, including the permissions // resolver - config = new BukkitConfiguration(getConfiguration(), logger); + config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "config.yml"), true), logger); perms = new PermissionsResolverManager(this, "WorldEdit", logger); // Load the configuration @@ -126,7 +127,6 @@ public class WorldEditPlugin extends JavaPlugin { * Loads and reloads all configuration. */ protected void loadConfiguration() { - getConfiguration().load(); config.unload(); config.load(); perms.load();