001 package org.bukkit.configuration.file; 002 003 import com.google.common.base.Charsets; 004 import com.google.common.io.Files; 005 006 import org.apache.commons.lang.Validate; 007 import org.bukkit.configuration.InvalidConfigurationException; 008 009 import java.io.BufferedReader; 010 import java.io.File; 011 import java.io.FileInputStream; 012 import java.io.FileNotFoundException; 013 import java.io.FileOutputStream; 014 import java.io.IOException; 015 import java.io.InputStream; 016 import java.io.InputStreamReader; 017 import java.io.OutputStreamWriter; 018 import java.io.Reader; 019 import java.io.Writer; 020 import java.nio.charset.Charset; 021 022 import org.bukkit.configuration.Configuration; 023 import org.bukkit.configuration.MemoryConfiguration; 024 import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; 025 026 /** 027 * This is a base class for all File based implementations of {@link 028 * Configuration} 029 */ 030 public abstract class FileConfiguration extends MemoryConfiguration { 031 /** 032 * This value specified that the system default encoding should be 033 * completely ignored, as it cannot handle the ASCII character set, or it 034 * is a strict-subset of UTF8 already (plain ASCII). 035 * 036 * @deprecated temporary compatibility measure 037 */ 038 @Deprecated 039 public static final boolean UTF8_OVERRIDE; 040 /** 041 * This value specifies if the system default encoding is unicode, but 042 * cannot parse standard ASCII. 043 * 044 * @deprecated temporary compatibility measure 045 */ 046 @Deprecated 047 public static final boolean UTF_BIG; 048 /** 049 * This value specifies if the system supports unicode. 050 * 051 * @deprecated temporary compatibility measure 052 */ 053 @Deprecated 054 public static final boolean SYSTEM_UTF; 055 static { 056 final byte[] testBytes = Base64Coder.decode("ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX4NCg=="); 057 final String testString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\r\n"; 058 final Charset defaultCharset = Charset.defaultCharset(); 059 final String resultString = new String(testBytes, defaultCharset); 060 final boolean trueUTF = defaultCharset.name().contains("UTF"); 061 UTF8_OVERRIDE = !testString.equals(resultString) || defaultCharset.equals(Charset.forName("US-ASCII")); 062 SYSTEM_UTF = trueUTF || UTF8_OVERRIDE; 063 UTF_BIG = trueUTF && UTF8_OVERRIDE; 064 } 065 066 /** 067 * Creates an empty {@link FileConfiguration} with no default values. 068 */ 069 public FileConfiguration() { 070 super(); 071 } 072 073 /** 074 * Creates an empty {@link FileConfiguration} using the specified {@link 075 * Configuration} as a source for all default values. 076 * 077 * @param defaults Default value provider 078 */ 079 public FileConfiguration(Configuration defaults) { 080 super(defaults); 081 } 082 083 /** 084 * Saves this {@link FileConfiguration} to the specified location. 085 * <p> 086 * If the file does not exist, it will be created. If already exists, it 087 * will be overwritten. If it cannot be overwritten or created, an 088 * exception will be thrown. 089 * <p> 090 * This method will save using the system default encoding, or possibly 091 * using UTF8. 092 * 093 * @param file File to save to. 094 * @throws IOException Thrown when the given file cannot be written to for 095 * any reason. 096 * @throws IllegalArgumentException Thrown when file is null. 097 */ 098 public void save(File file) throws IOException { 099 Validate.notNull(file, "File cannot be null"); 100 101 Files.createParentDirs(file); 102 103 String data = saveToString(); 104 105 Writer writer = new OutputStreamWriter(new FileOutputStream(file), UTF8_OVERRIDE && !UTF_BIG ? Charsets.UTF_8 : Charset.defaultCharset()); 106 107 try { 108 writer.write(data); 109 } finally { 110 writer.close(); 111 } 112 } 113 114 /** 115 * Saves this {@link FileConfiguration} to the specified location. 116 * <p> 117 * If the file does not exist, it will be created. If already exists, it 118 * will be overwritten. If it cannot be overwritten or created, an 119 * exception will be thrown. 120 * <p> 121 * This method will save using the system default encoding, or possibly 122 * using UTF8. 123 * 124 * @param file File to save to. 125 * @throws IOException Thrown when the given file cannot be written to for 126 * any reason. 127 * @throws IllegalArgumentException Thrown when file is null. 128 */ 129 public void save(String file) throws IOException { 130 Validate.notNull(file, "File cannot be null"); 131 132 save(new File(file)); 133 } 134 135 /** 136 * Saves this {@link FileConfiguration} to a string, and returns it. 137 * 138 * @return String containing this configuration. 139 */ 140 public abstract String saveToString(); 141 142 /** 143 * Loads this {@link FileConfiguration} from the specified location. 144 * <p> 145 * All the values contained within this configuration will be removed, 146 * leaving only settings and defaults, and the new values will be loaded 147 * from the given file. 148 * <p> 149 * If the file cannot be loaded for any reason, an exception will be 150 * thrown. 151 * <p> 152 * This will attempt to use the {@link Charset#defaultCharset()} for 153 * files, unless {@link #UTF8_OVERRIDE} but not {@link #UTF_BIG} is 154 * specified. 155 * 156 * @param file File to load from. 157 * @throws FileNotFoundException Thrown when the given file cannot be 158 * opened. 159 * @throws IOException Thrown when the given file cannot be read. 160 * @throws InvalidConfigurationException Thrown when the given file is not 161 * a valid Configuration. 162 * @throws IllegalArgumentException Thrown when file is null. 163 */ 164 public void load(File file) throws FileNotFoundException, IOException, InvalidConfigurationException { 165 Validate.notNull(file, "File cannot be null"); 166 167 final FileInputStream stream = new FileInputStream(file); 168 169 load(new InputStreamReader(stream, UTF8_OVERRIDE && !UTF_BIG ? Charsets.UTF_8 : Charset.defaultCharset())); 170 } 171 172 /** 173 * Loads this {@link FileConfiguration} from the specified stream. 174 * <p> 175 * All the values contained within this configuration will be removed, 176 * leaving only settings and defaults, and the new values will be loaded 177 * from the given stream. 178 * <p> 179 * This will attempt to use the {@link Charset#defaultCharset()}, unless 180 * {@link #UTF8_OVERRIDE} or {@link #UTF_BIG} is specified. 181 * 182 * @param stream Stream to load from 183 * @throws IOException Thrown when the given file cannot be read. 184 * @throws InvalidConfigurationException Thrown when the given file is not 185 * a valid Configuration. 186 * @throws IllegalArgumentException Thrown when stream is null. 187 * @deprecated This does not consider encoding 188 * @see #load(Reader) 189 */ 190 @Deprecated 191 public void load(InputStream stream) throws IOException, InvalidConfigurationException { 192 Validate.notNull(stream, "Stream cannot be null"); 193 194 load(new InputStreamReader(stream, UTF8_OVERRIDE ? Charsets.UTF_8 : Charset.defaultCharset())); 195 } 196 197 /** 198 * Loads this {@link FileConfiguration} from the specified reader. 199 * <p> 200 * All the values contained within this configuration will be removed, 201 * leaving only settings and defaults, and the new values will be loaded 202 * from the given stream. 203 * 204 * @param reader the reader to load from 205 * @throws IOException thrown when underlying reader throws an IOException 206 * @throws InvalidConfigurationException thrown when the reader does not 207 * represent a valid Configuration 208 * @throws IllegalArgumentException thrown when reader is null 209 */ 210 public void load(Reader reader) throws IOException, InvalidConfigurationException { 211 BufferedReader input = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); 212 213 StringBuilder builder = new StringBuilder(); 214 215 try { 216 String line; 217 218 while ((line = input.readLine()) != null) { 219 builder.append(line); 220 builder.append('\n'); 221 } 222 } finally { 223 input.close(); 224 } 225 226 loadFromString(builder.toString()); 227 } 228 229 /** 230 * Loads this {@link FileConfiguration} from the specified location. 231 * <p> 232 * All the values contained within this configuration will be removed, 233 * leaving only settings and defaults, and the new values will be loaded 234 * from the given file. 235 * <p> 236 * If the file cannot be loaded for any reason, an exception will be 237 * thrown. 238 * 239 * @param file File to load from. 240 * @throws FileNotFoundException Thrown when the given file cannot be 241 * opened. 242 * @throws IOException Thrown when the given file cannot be read. 243 * @throws InvalidConfigurationException Thrown when the given file is not 244 * a valid Configuration. 245 * @throws IllegalArgumentException Thrown when file is null. 246 */ 247 public void load(String file) throws FileNotFoundException, IOException, InvalidConfigurationException { 248 Validate.notNull(file, "File cannot be null"); 249 250 load(new File(file)); 251 } 252 253 /** 254 * Loads this {@link FileConfiguration} from the specified string, as 255 * opposed to from file. 256 * <p> 257 * All the values contained within this configuration will be removed, 258 * leaving only settings and defaults, and the new values will be loaded 259 * from the given string. 260 * <p> 261 * If the string is invalid in any way, an exception will be thrown. 262 * 263 * @param contents Contents of a Configuration to load. 264 * @throws InvalidConfigurationException Thrown if the specified string is 265 * invalid. 266 * @throws IllegalArgumentException Thrown if contents is null. 267 */ 268 public abstract void loadFromString(String contents) throws InvalidConfigurationException; 269 270 /** 271 * Compiles the header for this {@link FileConfiguration} and returns the 272 * result. 273 * <p> 274 * This will use the header from {@link #options()} -> {@link 275 * FileConfigurationOptions#header()}, respecting the rules of {@link 276 * FileConfigurationOptions#copyHeader()} if set. 277 * 278 * @return Compiled header 279 */ 280 protected abstract String buildHeader(); 281 282 @Override 283 public FileConfigurationOptions options() { 284 if (options == null) { 285 options = new FileConfigurationOptions(this); 286 } 287 288 return (FileConfigurationOptions) options; 289 } 290 }