// Path: src/main/java/org/tyrannyofheaven/bukkit/util/ToHLoggingUtils.java
// public static void log(Plugin plugin, Level level, String format, Object... args) {
//     Logger logger = plugin.getLogger();
//     if (logger.isLoggable(level)) { // Avoid unnecessary String.format() calls
//         if (args.length > 0 && args[args.length - 1] instanceof Throwable) {
//             // Last argument is a Throwable, treat accordingly
//             logger.log(level, createLogMessage(format, Arrays.copyOf(args, args.length - 1)), (Throwable)args[args.length - 1]);
//         }
//         else {
//             logger.log(level, createLogMessage(format, args));
//         }
//     }
// }
// 
// Path: src/main/java/org/tyrannyofheaven/bukkit/util/configuration/AnnotatedYamlConfiguration.java
// public class AnnotatedYamlConfiguration extends YamlConfiguration {
// 
//     private final DumperOptions yamlOptions = new DumperOptions();
// 
//     private final Representer yamlRepresenter = new YamlRepresenter();
// 
//     private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions);
// 
//     // Map from property key to comment. Comment may have multiple lines that are newline-separated.
//     private final Map<String, String> comments = new HashMap<>();
// 
//     @Override
//     public String saveToString() {
//         // Wish these were protected...
//         yamlOptions.setIndent(options().indent());
//         yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
//         yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
//         
//         StringBuilder builder = new StringBuilder();
//         builder.append(buildHeader());
//         if (builder.length() > 0)
//             builder.append('\n'); // Newline after header, if present
// 
//         // Iterate over each root-level property and dump
//         for (Iterator<Map.Entry<String, Object>> i = getValues(false).entrySet().iterator(); i.hasNext();) {
//             Map.Entry<String, Object> entry = i.next();
// 
//             // Output comment, if present
//             String comment = comments.get(entry.getKey());
//             if (comment != null) {
//                 builder.append(buildComment(comment));
//             }
//             
//             // Dump property
//             builder.append(yaml.dump(Collections.singletonMap(entry.getKey(), entry.getValue())));
// 
//             // Output newline, if not the last
//             if (i.hasNext())
//                 builder.append('\n');
//         }
// 
//         String dump = builder.toString();
//         
//         if (dump.equals(BLANK_CONFIG)) {
//             dump = "";
//         }
//         
//         return dump;
//     }
// 
//     /**
//      * Format a multi-line property comment.
//      * 
//      * @param comment the original comment string
//      * @return the formatted comment string
//      */
//     protected String buildComment(String comment) {
//         StringBuilder builder = new StringBuilder();
//         for (String line : comment.split("\r?\n")) {
//             builder.append(COMMENT_PREFIX);
//             builder.append(line);
//             builder.append('\n');
//         }
//         return builder.toString();
//     }
// 
//     /**
//      * Returns a root-level comment.
//      * 
//      * @param key the property key
//      * @return the comment or <code>null</code>
//      */
//     public String getComment(String key) {
//         return comments.get(key);
//     }
// 
//     /**
//      * Set a root-level comment.
//      * 
//      * @param key the property key
//      * @param comment the comment. May be <code>null</code>, in which case the comment
//      *   is removed.
//      */
//     public void setComment(String key, String... comment) {
//         if (comment != null && comment.length > 0) {
//             String s = Joiner.on('\n').join(comment);
//             comments.put(key, s);
//         }
//         else {
//             comments.remove(key);
//         }
//     }
// 
//     /**
//      * Returns root-level comments.
//      * 
//      * @return map of root-level comments
//      */
//     public Map<String, String> getComments() {
//         return Collections.unmodifiableMap(comments);
//     }
// 
//     /**
//      * Set root-level comments from a map.
//      * 
//      * @param comments comment map
//      */
//     public void setComments(Map<String, String> comments) {
//         this.comments.clear();
//         if (comments != null)
//             this.comments.putAll(comments);
//     }
// 
// }


import static org.tyrannyofheaven.bukkit.util.ToHLoggingUtils.log;
import java.io.File;
import java.io.FileNotFoundException;
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.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.tyrannyofheaven.bukkit.util.configuration.AnnotatedYamlConfiguration;
import com.google.common.base.Charsets;
     * 
     * @param clazz the class
     * @param resourceName resource name relative to the class
     * @param outFile the output File
     * @throws IOException
     */
    public static void copyResourceToFile(Class<?> clazz, String resourceName, File outFile) throws IOException {
        InputStream is = clazz.getResourceAsStream(resourceName);
        try {
            copyFile(is, outFile);
        }
        finally {
            is.close();
        }
    }

    /**
     * Copies a resource relative to the Plugin class to a file.
     * 
     * @param plugin the plugin
     * @param resourceName resource name relative to plugin's class
     * @param outFile the output file
     * @return true if successful, false otherwise
     */
    public static boolean copyResourceToFile(Plugin plugin, String resourceName, File outFile) {
        try {
            copyResourceToFile(plugin.getClass(), resourceName, outFile);
            return true;
        }
        catch (IOException e) {
// Path: src/main/java/org/tyrannyofheaven/bukkit/util/ToHLoggingUtils.java
// public static void log(Plugin plugin, Level level, String format, Object... args) {
//     Logger logger = plugin.getLogger();
//     if (logger.isLoggable(level)) { // Avoid unnecessary String.format() calls
//         if (args.length > 0 && args[args.length - 1] instanceof Throwable) {
//             // Last argument is a Throwable, treat accordingly
//             logger.log(level, createLogMessage(format, Arrays.copyOf(args, args.length - 1)), (Throwable)args[args.length - 1]);
//         }
//         else {
//             logger.log(level, createLogMessage(format, args));
//         }
//     }
// }
// 
// Path: src/main/java/org/tyrannyofheaven/bukkit/util/configuration/AnnotatedYamlConfiguration.java
// public class AnnotatedYamlConfiguration extends YamlConfiguration {
// 
//     private final DumperOptions yamlOptions = new DumperOptions();
// 
//     private final Representer yamlRepresenter = new YamlRepresenter();
// 
//     private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions);
// 
//     // Map from property key to comment. Comment may have multiple lines that are newline-separated.
//     private final Map<String, String> comments = new HashMap<>();
// 
//     @Override
//     public String saveToString() {
//         // Wish these were protected...
//         yamlOptions.setIndent(options().indent());
//         yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
//         yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
//         
//         StringBuilder builder = new StringBuilder();
//         builder.append(buildHeader());
//         if (builder.length() > 0)
//             builder.append('\n'); // Newline after header, if present
// 
//         // Iterate over each root-level property and dump
//         for (Iterator<Map.Entry<String, Object>> i = getValues(false).entrySet().iterator(); i.hasNext();) {
//             Map.Entry<String, Object> entry = i.next();
// 
//             // Output comment, if present
//             String comment = comments.get(entry.getKey());
//             if (comment != null) {
//                 builder.append(buildComment(comment));
//             }
//             
//             // Dump property
//             builder.append(yaml.dump(Collections.singletonMap(entry.getKey(), entry.getValue())));
// 
//             // Output newline, if not the last
//             if (i.hasNext())
//                 builder.append('\n');
//         }
// 
//         String dump = builder.toString();
//         
//         if (dump.equals(BLANK_CONFIG)) {
//             dump = "";
//         }
//         
//         return dump;
//     }
// 
//     /**
//      * Format a multi-line property comment.
//      * 
//      * @param comment the original comment string
//      * @return the formatted comment string
//      */
//     protected String buildComment(String comment) {
//         StringBuilder builder = new StringBuilder();
//         for (String line : comment.split("\r?\n")) {
//             builder.append(COMMENT_PREFIX);
//             builder.append(line);
//             builder.append('\n');
//         }
//         return builder.toString();
//     }
// 
//     /**
//      * Returns a root-level comment.
//      * 
//      * @param key the property key
//      * @return the comment or <code>null</code>
//      */
//     public String getComment(String key) {
//         return comments.get(key);
//     }
// 
//     /**
//      * Set a root-level comment.
//      * 
//      * @param key the property key
//      * @param comment the comment. May be <code>null</code>, in which case the comment
//      *   is removed.
//      */
//     public void setComment(String key, String... comment) {
//         if (comment != null && comment.length > 0) {
//             String s = Joiner.on('\n').join(comment);
//             comments.put(key, s);
//         }
//         else {
//             comments.remove(key);
//         }
//     }
// 
//     /**
//      * Returns root-level comments.
//      * 
//      * @return map of root-level comments
//      */
//     public Map<String, String> getComments() {
//         return Collections.unmodifiableMap(comments);
//     }
// 
//     /**
//      * Set root-level comments from a map.
//      * 
//      * @param comments comment map
//      */
//     public void setComments(Map<String, String> comments) {
//         this.comments.clear();
//         if (comments != null)
//             this.comments.putAll(comments);
//     }
// 
// }


// Path: src/main/java/org/tyrannyofheaven/bukkit/util/ToHFileUtils.java
import static org.tyrannyofheaven.bukkit.util.ToHLoggingUtils.log;
import java.io.File;
import java.io.FileNotFoundException;
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.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.tyrannyofheaven.bukkit.util.configuration.AnnotatedYamlConfiguration;
import com.google.common.base.Charsets;

     * 
     * @param clazz the class
     * @param resourceName resource name relative to the class
     * @param outFile the output File
     * @throws IOException
     */
    public static void copyResourceToFile(Class<?> clazz, String resourceName, File outFile) throws IOException {
        InputStream is = clazz.getResourceAsStream(resourceName);
        try {
            copyFile(is, outFile);
        }
        finally {
            is.close();
        }
    }

    /**
     * Copies a resource relative to the Plugin class to a file.
     * 
     * @param plugin the plugin
     * @param resourceName resource name relative to plugin's class
     * @param outFile the output file
     * @return true if successful, false otherwise
     */
    public static boolean copyResourceToFile(Plugin plugin, String resourceName, File outFile) {
        try {
            copyResourceToFile(plugin.getClass(), resourceName, outFile);
            return true;
        }
        catch (IOException e) {
            log(plugin, Level.SEVERE, "Error copying %s to %s", resourceName, outFile, e);