diff --git a/paper-api/src/main/java/org/bukkit/configuration/file/FileConfiguration.java b/paper-api/src/main/java/org/bukkit/configuration/file/FileConfiguration.java index 3f9992e76b..9d6d1c6132 100644 --- a/paper-api/src/main/java/org/bukkit/configuration/file/FileConfiguration.java +++ b/paper-api/src/main/java/org/bukkit/configuration/file/FileConfiguration.java @@ -1,25 +1,68 @@ 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 * 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. */ @@ -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. + *
+ * 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. + *
+ * 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 { *
* 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 @@ -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. + *
+ * 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. + *
+ * 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/paper-api/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java b/paper-api/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java index 18be0dc624..ea4c2b3540 100644 --- a/paper-api/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java +++ b/paper-api/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. + *
+ * 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. + *
+ * 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/paper-api/src/main/java/org/bukkit/plugin/PluginAwareness.java b/paper-api/src/main/java/org/bukkit/plugin/PluginAwareness.java new file mode 100644 index 0000000000..ddb47b7e6a --- /dev/null +++ b/paper-api/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. + *
+ * 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/paper-api/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java b/paper-api/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java index cfd4b71b08..0fd966c6e4 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java +++ b/paper-api/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; *
awareness
@@ -165,7 +175,39 @@ import com.google.common.collect.ImmutableMap;
*
*/
public final class PluginDescriptionFile {
- private static final Yaml yaml = new Yaml(new SafeConstructor());
+ private static final ThreadLocal
+ *
+ * In the plugin.yml, this entry is named
+ * Example:
+ * Note: 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
+ *
+ * !@
).
+ * awareness
must be in YAML list
+ * format.
+ * awareness
.
+ *
+ * awareness:
+ *- !@UTF8