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 }