summaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
authorWesley Wolfe <wesley.d.wolfe+git@gmail.com>2014-03-25 00:05:21 -0500
committerWesley Wolfe <wesley.d.wolfe+git@gmail.com>2014-05-14 07:07:37 -0500
commit8ac95b65e0767b8f4b614ab86785778098f1a25d (patch)
tree7498a37d45e9830907d1a3ef00b0e77bab7c2c28 /src/main
parent48756ec1d69b68a105e5dd043319c21b4d92ff8a (diff)
downloadbukkit-8ac95b65e0767b8f4b614ab86785778098f1a25d.tar
bukkit-8ac95b65e0767b8f4b614ab86785778098f1a25d.tar.gz
bukkit-8ac95b65e0767b8f4b614ab86785778098f1a25d.tar.lz
bukkit-8ac95b65e0767b8f4b614ab86785778098f1a25d.tar.xz
bukkit-8ac95b65e0767b8f4b614ab86785778098f1a25d.zip
Change YamlConfiguration encoding styles.
On JVMs with UTF-8 default encoding, this commit has no change in behavior. On JVMs with ascii default encoding (like some minimal linux installa- tions), this commit now uses UTF-8 for YamlConfiguration operations. Because all ascii is valid UTF-8, there is no feature degradation or data loss during the transition. On JVMs with any non-unicode but ascii-compliant encoding, this commit now forces YamlConfiguration to escape special characters when writing to files, effectively rendering the encoding to be plain ascii. Any affected file will now be able to migrate to UTF-8 in the future without data-loss or explicit conversion. When reading files, YamlConfiguration will use the system default encoding to handle any incoming non-utf8 data, with the expectation that any newly written file is still compliant with the system's default encoding. On JVMs with any non-unicode, but ascii-incompliant encoding (this may be the case for some Eastern character sets on Windows systems), this change is breaking, but is justified in claim that these systems would otherwise be unable to read YamlConfiguration for implementation dependent settings or from plugins themselves. For these systems, all uses of the encoding will be forced to use UTF-8 in all cases, and is effectively treated as if it was configured to be UTF-8 by default. On JVMs with unicode encoding of UTF-16 or UTF-32, the ability to load any configurations from almost any source prior to this change would have been unfeasible, if not impossible. As of this change, however, these systems now behave as expected when writing or reading files. However, when reading from any plugin jar, UTF-8 will be used, matching a super-majority of plugin developer base and requirements for the plugin.yml. Plugin developers may now mark their plugin as UTF-8 compliant, as documented in the PluginDescriptionFile class. This change will cause the appropriate APIs in JavaPlugin to ignore any system default encoding, instead using a Reader with the UTF-8 encoding, effectively rendering the jar system independent. This does not affect the aformentioned JVM settings for reading and writing files. To coincide with these changes, YamlConfiguration methods that utilize a stream are now deprecated to encourage use of a more strict denotation. File methods carry system-specific behaviors to prevent unncessary data loss during the transitional phase, while Reader methods are now provided that have a very well-defined encoder behavior. For the transition from InputStream methods to Reader methods, an API has been added to JavaPlugin to provide a Reader that matches the previous behavior as well as compliance to the UTF-8 flag in the PluginDescriptionFile. Addresses BUKKIT-314, BUKKIT-1466, BUKKIT-3377
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/org/bukkit/configuration/file/FileConfiguration.java89
-rw-r--r--src/main/java/org/bukkit/configuration/file/YamlConfiguration.java36
-rw-r--r--src/main/java/org/bukkit/plugin/PluginAwareness.java29
-rw-r--r--src/main/java/org/bukkit/plugin/PluginDescriptionFile.java102
-rw-r--r--src/main/java/org/bukkit/plugin/java/JavaPlugin.java84
5 files changed, 326 insertions, 14 deletions
diff --git a/src/main/java/org/bukkit/configuration/file/FileConfiguration.java b/src/main/java/org/bukkit/configuration/file/FileConfiguration.java
index 3f9992e7..9d6d1c61 100644
--- a/src/main/java/org/bukkit/configuration/file/FileConfiguration.java
+++ b/src/main/java/org/bukkit/configuration/file/FileConfiguration.java
@@ -1,19 +1,27 @@
package org.bukkit.configuration.file;
+import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.InvalidConfigurationException;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileWriter;
+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 org.bukkit.configuration.Configuration;
import org.bukkit.configuration.MemoryConfiguration;
+import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
/**
* This is a base class for all File based implementations of {@link
@@ -21,6 +29,41 @@ import org.bukkit.configuration.MemoryConfiguration;
*/
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() {
@@ -43,6 +86,9 @@ public abstract class FileConfiguration extends MemoryConfiguration {
* 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.
+ * <p>
+ * 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
@@ -56,7 +102,7 @@ public abstract class FileConfiguration extends MemoryConfiguration {
String data = saveToString();
- FileWriter writer = new FileWriter(file);
+ Writer writer = new OutputStreamWriter(new FileOutputStream(file), UTF8_OVERRIDE && !UTF_BIG ? Charsets.UTF_8 : Charset.defaultCharset());
try {
writer.write(data);
@@ -71,6 +117,9 @@ public abstract class FileConfiguration extends MemoryConfiguration {
* 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.
+ * <p>
+ * 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
@@ -99,6 +148,10 @@ public abstract class FileConfiguration extends MemoryConfiguration {
* <p>
* If the file cannot be loaded for any reason, an exception will be
* thrown.
+ * <p>
+ * 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
@@ -111,7 +164,9 @@ public abstract class FileConfiguration extends MemoryConfiguration {
public void load(File file) throws FileNotFoundException, IOException, InvalidConfigurationException {
Validate.notNull(file, "File cannot be null");
- load(new FileInputStream(file));
+ final FileInputStream stream = new FileInputStream(file);
+
+ load(new InputStreamReader(stream, UTF8_OVERRIDE && !UTF_BIG ? Charsets.UTF_8 : Charset.defaultCharset()));
}
/**
@@ -120,20 +175,42 @@ public abstract class FileConfiguration extends MemoryConfiguration {
* 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.
+ * <p>
+ * 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 {
Validate.notNull(stream, "Stream cannot be null");
- InputStreamReader reader = new InputStreamReader(stream);
- StringBuilder builder = new StringBuilder();
- BufferedReader input = new BufferedReader(reader);
+ load(new InputStreamReader(stream, UTF8_OVERRIDE ? Charsets.UTF_8 : Charset.defaultCharset()));
+ }
+
+ /**
+ * Loads this {@link FileConfiguration} from the specified reader.
+ * <p>
+ * 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;
diff --git a/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java b/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java
index 18be0dc6..ea4c2b35 100644
--- a/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java
+++ b/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java
@@ -4,6 +4,7 @@ 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 java.util.logging.Level;
@@ -32,6 +33,7 @@ public class YamlConfiguration extends FileConfiguration {
public String saveToString() {
yamlOptions.setIndent(options().indent());
yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+ yamlOptions.setAllowUnicode(SYSTEM_UTF);
yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
String header = buildHeader();
@@ -162,6 +164,8 @@ public class YamlConfiguration extends FileConfiguration {
* 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.
+ * <p>
+ * The encoding used may follow the system dependent default.
*
* @param file Input file
* @return Resulting configuration
@@ -194,7 +198,11 @@ public class YamlConfiguration extends FileConfiguration {
* @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) {
Validate.notNull(stream, "Stream cannot be null");
@@ -210,4 +218,32 @@ public class YamlConfiguration extends FileConfiguration {
return config;
}
+
+
+ /**
+ * Creates a new {@link YamlConfiguration}, loading from the given reader.
+ * <p>
+ * 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) {
+ Validate.notNull(reader, "Stream cannot be null");
+
+ YamlConfiguration config = new YamlConfiguration();
+
+ try {
+ config.load(reader);
+ } catch (IOException ex) {
+ Bukkit.getLogger().log(Level.SEVERE, "Cannot load configuration from stream", ex);
+ } catch (InvalidConfigurationException ex) {
+ Bukkit.getLogger().log(Level.SEVERE, "Cannot load configuration from stream", ex);
+ }
+
+ return config;
+ }
}
diff --git a/src/main/java/org/bukkit/plugin/PluginAwareness.java b/src/main/java/org/bukkit/plugin/PluginAwareness.java
new file mode 100644
index 00000000..ddb47b7e
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/PluginAwareness.java
@@ -0,0 +1,29 @@
+package org.bukkit.plugin;
+
+import java.util.Set;
+
+import org.bukkit.plugin.java.JavaPlugin;
+
+/**
+ * Represents a concept that a plugin is aware of.
+ * <p>
+ * The internal representation may be singleton, or be a parameterized
+ * instance, but must be immutable.
+ */
+public interface PluginAwareness {
+ /**
+ * Each entry here represents a particular plugin's awareness. These can
+ * be checked by using {@link PluginDescriptionFile#getAwareness()}.{@link
+ * Set#contains(Object) contains(flag)}.
+ */
+ public enum Flags implements PluginAwareness {
+ /**
+ * This specifies that all (text) resources stored in a plugin's jar
+ * use UTF-8 encoding.
+ *
+ * @see JavaPlugin#getTextResource(String)
+ */
+ UTF8,
+ ;
+ }
+}
diff --git a/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java b/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java
index cfd4b71b..0fd966c6 100644
--- a/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java
+++ b/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java
@@ -4,8 +4,10 @@ import java.io.InputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
@@ -15,10 +17,14 @@ import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
/**
* This type is the runtime-container for the information in the plugin.yml.
@@ -111,6 +117,10 @@ import com.google.common.collect.ImmutableMap;
* <td>The default {@link Permission#getDefault() default} permission
* state for defined {@link #getPermissions() permissions} the plugin
* will register</td>
+ * </tr><tr>
+ * <td><code>awareness</code></td>
+ * <td>{@link #getAwareness()}</td>
+ * <td>The concepts that the plugin acknowledges</td>
* </tr>
* </table>
* <p>
@@ -165,7 +175,39 @@ import com.google.common.collect.ImmutableMap;
*</pre></blockquote>
*/
public final class PluginDescriptionFile {
- private static final Yaml yaml = new Yaml(new SafeConstructor());
+ private static final ThreadLocal<Yaml> YAML = new ThreadLocal<Yaml>() {
+ @Override
+ protected Yaml initialValue() {
+ return new Yaml(new SafeConstructor() {
+ {
+ yamlConstructors.put(null, new AbstractConstruct() {
+ @Override
+ public Object construct(final Node node) {
+ if (!node.getTag().startsWith("!@")) {
+ // Unknown tag - will fail
+ return SafeConstructor.undefinedConstructor.construct(node);
+ }
+ // Unknown awareness - provide a graceful substitution
+ return new PluginAwareness() {
+ @Override
+ public String toString() {
+ return node.toString();
+ }
+ };
+ }
+ });
+ for (final PluginAwareness.Flags flag : PluginAwareness.Flags.values()) {
+ yamlConstructors.put(new Tag("!@" + flag.name()), new AbstractConstruct() {
+ @Override
+ public PluginAwareness.Flags construct(final Node node) {
+ return flag;
+ }
+ });
+ }
+ }
+ });
+ }
+ };
String rawName = null;
private String name = null;
private String main = null;
@@ -184,9 +226,10 @@ public final class PluginDescriptionFile {
private List<Permission> permissions = null;
private Map<?, ?> lazyPermissions = null;
private PermissionDefault defaultPerm = PermissionDefault.OP;
+ private Set<PluginAwareness> awareness = ImmutableSet.of();
public PluginDescriptionFile(final InputStream stream) throws InvalidDescriptionException {
- loadMap(asMap(yaml.load(stream)));
+ loadMap(asMap(YAML.get().load(stream)));
}
/**
@@ -197,7 +240,7 @@ public final class PluginDescriptionFile {
* invalid
*/
public PluginDescriptionFile(final Reader reader) throws InvalidDescriptionException {
- loadMap(asMap(yaml.load(reader)));
+ loadMap(asMap(YAML.get().load(reader)));
}
/**
@@ -768,6 +811,45 @@ public final class PluginDescriptionFile {
}
/**
+ * Gives a set of every {@link PluginAwareness} for a plugin. An awareness
+ * dictates something that a plugin developer acknowledges when the plugin
+ * is compiled. Some implementions may define extra awarenesses that are
+ * not included in the API. Any unrecognized
+ * awareness (one unsupported or in a future version) will cause a dummy
+ * object to be created instead of failing.
+ * <p>
+ * <ul>
+ * <li>Currently only supports the enumerated values in {@link
+ * PluginAwareness.Flags}.
+ * <li>Each awareness starts the identifier with bang-at
+ * (<code>!@</code>).
+ * <li>Unrecognized (future / unimplemented) entries are quietly replaced
+ * by a generic object that implements PluginAwareness.
+ * <li>A type of awareness must be defined by the runtime and acknowledged
+ * by the API, effectively discluding any derived type from any
+ * plugin's classpath.
+ * <li><code>awareness</code> must be in <a
+ * href="http://en.wikipedia.org/wiki/YAML#Lists">YAML list
+ * format</a>.
+ * </ul>
+ * <p>
+ * In the plugin.yml, this entry is named <code>awareness</code>.
+ * <p>
+ * Example:<blockquote><pre>awareness:
+ *- !@UTF8</pre></blockquote>
+ * <p>
+ * <b>Note:</b> Although unknown versions of some future awareness are
+ * gracefully substituted, previous versions of Bukkit (ones prior to the
+ * first implementation of awareness) will fail to load a plugin that
+ * defines any awareness.
+ *
+ * @return a set containing every awareness for the plugin
+ */
+ public Set<PluginAwareness> getAwareness() {
+ return awareness;
+ }
+
+ /**
* Returns the name of a plugin, including the version. This method is
* provided for convenience; it uses the {@link #getName()} and {@link
* #getVersion()} entries.
@@ -796,7 +878,7 @@ public final class PluginDescriptionFile {
* @param writer Writer to output this file to
*/
public void save(Writer writer) {
- yaml.dump(saveMap(), writer);
+ YAML.get().dump(saveMap(), writer);
}
private void loadMap(Map<?, ?> map) throws InvalidDescriptionException {
@@ -926,6 +1008,18 @@ public final class PluginDescriptionFile {
}
}
+ if (map.get("awareness") instanceof Iterable) {
+ Set<PluginAwareness> awareness = new HashSet<PluginAwareness>();
+ try {
+ for (Object o : (Iterable<?>) map.get("awareness")) {
+ awareness.add((PluginAwareness) o);
+ }
+ } catch (ClassCastException ex) {
+ throw new InvalidDescriptionException(ex, "awareness has wrong type");
+ }
+ this.awareness = ImmutableSet.copyOf(awareness);
+ }
+
try {
lazyPermissions = (Map<?, ?>) map.get("permissions");
} catch (ClassCastException ex) {
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
index a0b609fa..19893f31 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
@@ -4,9 +4,12 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
@@ -18,10 +21,12 @@ import org.bukkit.Warning.WarningState;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
+import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.AuthorNagException;
+import org.bukkit.plugin.PluginAwareness;
import org.bukkit.plugin.PluginBase;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader;
@@ -33,6 +38,8 @@ import com.avaje.ebean.config.DataSourceConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.ddl.DdlGenerator;
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteStreams;
/**
* Represents a Java plugin
@@ -89,6 +96,7 @@ public abstract class JavaPlugin extends PluginBase {
*
* @return The folder.
*/
+ @Override
public final File getDataFolder() {
return dataFolder;
}
@@ -98,6 +106,7 @@ public abstract class JavaPlugin extends PluginBase {
*
* @return PluginLoader that controls this plugin
*/
+ @Override
public final PluginLoader getPluginLoader() {
return loader;
}
@@ -107,6 +116,7 @@ public abstract class JavaPlugin extends PluginBase {
*
* @return Server running this plugin
*/
+ @Override
public final Server getServer() {
return server;
}
@@ -117,6 +127,7 @@ public abstract class JavaPlugin extends PluginBase {
*
* @return true if this plugin is enabled, otherwise false
*/
+ @Override
public final boolean isEnabled() {
return isEnabled;
}
@@ -135,10 +146,12 @@ public abstract class JavaPlugin extends PluginBase {
*
* @return Contents of the plugin.yaml file
*/
+ @Override
public final PluginDescriptionFile getDescription() {
return description;
}
+ @Override
public FileConfiguration getConfig() {
if (newConfig == null) {
reloadConfig();
@@ -146,17 +159,67 @@ public abstract class JavaPlugin extends PluginBase {
return newConfig;
}
+ /**
+ * Provides a reader for a text file located inside the jar. The behavior
+ * of this method adheres to {@link PluginAwareness.Flags#UTF8}, or if not
+ * defined, uses UTF8 if {@link FileConfiguration#UTF8_OVERRIDE} is
+ * specified, or system default otherwise.
+ *
+ * @param file the filename of the resource to load
+ * @return null if {@link #getResource(String)} returns null
+ * @throws IllegalArgumentException if file is null
+ * @see ClassLoader#getResourceAsStream(String)
+ */
+ @SuppressWarnings("deprecation")
+ protected final Reader getTextResource(String file) {
+ final InputStream in = getResource(file);
+
+ return in == null ? null : new InputStreamReader(in, isStrictlyUTF8() || FileConfiguration.UTF8_OVERRIDE ? Charsets.UTF_8 : Charset.defaultCharset());
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
public void reloadConfig() {
newConfig = YamlConfiguration.loadConfiguration(configFile);
- InputStream defConfigStream = getResource("config.yml");
- if (defConfigStream != null) {
- YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);
+ final InputStream defConfigStream = getResource("config.yml");
+ if (defConfigStream == null) {
+ return;
+ }
+
+ final YamlConfiguration defConfig;
+ if (isStrictlyUTF8() || FileConfiguration.UTF8_OVERRIDE) {
+ defConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream, Charsets.UTF_8));
+ } else {
+ final byte[] contents;
+ defConfig = new YamlConfiguration();
+ try {
+ contents = ByteStreams.toByteArray(defConfigStream);
+ } catch (final IOException e) {
+ getLogger().log(Level.SEVERE, "Unexpected failure reading config.yml", e);
+ return;
+ }
+
+ final String text = new String(contents, Charset.defaultCharset());
+ if (!text.equals(new String(contents, Charsets.UTF_8))) {
+ getLogger().warning("Default system encoding may have misread config.yml from plugin jar");
+ }
- newConfig.setDefaults(defConfig);
+ try {
+ defConfig.loadFromString(text);
+ } catch (final InvalidConfigurationException e) {
+ getLogger().log(Level.SEVERE, "Cannot load configuration from jar", e);
+ }
}
+
+ newConfig.setDefaults(defConfig);
+ }
+
+ private boolean isStrictlyUTF8() {
+ return getDescription().getAwareness().contains(PluginAwareness.Flags.UTF8);
}
+ @Override
public void saveConfig() {
try {
getConfig().save(configFile);
@@ -165,12 +228,14 @@ public abstract class JavaPlugin extends PluginBase {
}
}
+ @Override
public void saveDefaultConfig() {
if (!configFile.exists()) {
saveResource("config.yml", false);
}
}
+ @Override
public void saveResource(String resourcePath, boolean replace) {
if (resourcePath == null || resourcePath.equals("")) {
throw new IllegalArgumentException("ResourcePath cannot be null or empty");
@@ -208,6 +273,7 @@ public abstract class JavaPlugin extends PluginBase {
}
}
+ @Override
public InputStream getResource(String filename) {
if (filename == null) {
throw new IllegalArgumentException("Filename cannot be null");
@@ -328,6 +394,7 @@ public abstract class JavaPlugin extends PluginBase {
/**
* {@inheritDoc}
*/
+ @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
return false;
}
@@ -335,6 +402,7 @@ public abstract class JavaPlugin extends PluginBase {
/**
* {@inheritDoc}
*/
+ @Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
return null;
}
@@ -362,24 +430,31 @@ public abstract class JavaPlugin extends PluginBase {
}
}
+ @Override
public void onLoad() {}
+ @Override
public void onDisable() {}
+ @Override
public void onEnable() {}
+ @Override
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
return null;
}
+ @Override
public final boolean isNaggable() {
return naggable;
}
+ @Override
public final void setNaggable(boolean canNag) {
this.naggable = canNag;
}
+ @Override
public EbeanServer getDatabase() {
return ebean;
}
@@ -398,6 +473,7 @@ public abstract class JavaPlugin extends PluginBase {
gen.runScript(true, gen.generateDropDdl());
}
+ @Override
public final Logger getLogger() {
return logger;
}