00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 package com.realtime.crossfire.jxclient.settings;
00023
00024 import com.realtime.crossfire.jxclient.util.NumberParser;
00025 import java.io.BufferedWriter;
00026 import java.io.File;
00027 import java.io.FileInputStream;
00028 import java.io.FileNotFoundException;
00029 import java.io.FileOutputStream;
00030 import java.io.IOException;
00031 import java.io.InputStreamReader;
00032 import java.io.LineNumberReader;
00033 import java.io.OutputStreamWriter;
00034 import java.util.Map;
00035 import java.util.TreeMap;
00036 import org.jetbrains.annotations.NotNull;
00037 import org.jetbrains.annotations.Nullable;
00038
00043 public class Settings {
00044
00048 @NotNull
00049 private final File file;
00050
00054 @NotNull
00055 private final Map<String, Entry> values = new TreeMap<String, Entry>();
00056
00060 private boolean noSave = true;
00061
00066 public Settings(@NotNull final File file) {
00067 this.file = file;
00068 loadValues();
00069 noSave = false;
00070 }
00071
00079 @NotNull
00080 public String getString(@NotNull final String key, @NotNull final String defaultValue) {
00081 final Entry entry = values.get(key);
00082 return entry != null ? entry.getValue() : defaultValue;
00083 }
00084
00092 public boolean getBoolean(@NotNull final String key, final boolean defaultValue) {
00093 final String value = getString(key, Boolean.toString(defaultValue));
00094 try {
00095 return Boolean.parseBoolean(value);
00096 } catch (final NumberFormatException ignored) {
00097 return defaultValue;
00098 }
00099 }
00100
00108 public int getInt(@NotNull final String key, final int defaultValue) {
00109 return NumberParser.parseInt(getString(key, Integer.toString(defaultValue)), defaultValue);
00110 }
00111
00119 public long getLong(@NotNull final String key, final long defaultValue) {
00120 return NumberParser.parseLong(getString(key, Long.toString(defaultValue)), defaultValue);
00121 }
00122
00130 public void putString(@NotNull final String key, @NotNull final String value, @Nullable final String documentation) {
00131 final Entry oldEntry = values.get(key);
00132 if (oldEntry != null) {
00133 oldEntry.setDocumentation(documentation);
00134 if (!oldEntry.getValue().equals(value)) {
00135 oldEntry.setValue(value);
00136 setChanged();
00137 }
00138 } else {
00139 values.put(key, new Entry(value, documentation));
00140 }
00141 }
00142
00149 public void putBoolean(@NotNull final String key, final boolean value, @NotNull final String documentation) {
00150 putString(key, Boolean.toString(value), documentation);
00151 }
00152
00159 public void putInt(@NotNull final String key, final int value, @NotNull final String documentation) {
00160 putString(key, Integer.toString(value), documentation);
00161 }
00162
00169 public void putLong(@NotNull final String key, final long value, @NotNull final String documentation) {
00170 putString(key, Long.toString(value), documentation);
00171 }
00172
00177 public void remove(@NotNull final String key) {
00178 if (values.remove(key) != null) {
00179 setChanged();
00180 }
00181 }
00182
00187 private void setChanged() {
00188 if (noSave) {
00189 return;
00190 }
00191
00192 try {
00193 saveValues();
00194 } catch (final IOException ex) {
00195 System.err.println(file+": "+ex.getMessage());
00196 }
00197 }
00198
00202 private void loadValues() {
00203 values.clear();
00204
00205 try {
00206 final FileInputStream fis = new FileInputStream(file);
00207 try {
00208 final InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
00209 try {
00210 final LineNumberReader lnr = new LineNumberReader(isr);
00211 try {
00212 while (true) {
00213 final String line2 = lnr.readLine();
00214 if (line2 == null) {
00215 break;
00216 }
00217 final String line = Codec.decode(line2.trim());
00218 if (line == null || line.startsWith("#") || line.length() == 0) {
00219 continue;
00220 }
00221
00222 final String[] tmp = line.split("=", 2);
00223 if (tmp.length != 2) {
00224 System.err.println(file+":"+lnr.getLineNumber()+": syntax error");
00225 continue;
00226 }
00227 final String key = tmp[0];
00228 final String value = tmp[1];
00229
00230 putString(key, value, null);
00231 }
00232 } finally {
00233 lnr.close();
00234 }
00235 } finally {
00236 isr.close();
00237 }
00238 } finally {
00239 fis.close();
00240 }
00241 } catch (final FileNotFoundException ignored) {
00242
00243 } catch (final IOException ex) {
00244 System.err.println(file+": "+ex.getMessage());
00245 }
00246 }
00247
00252 private void saveValues() throws IOException {
00253 final File tmpFile = new File(file.getPath()+".tmp");
00254 final FileOutputStream fos = new FileOutputStream(tmpFile);
00255 try {
00256 final OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
00257 try {
00258 final BufferedWriter bw = new BufferedWriter(osw);
00259 try {
00260 saveNode(bw, values);
00261 } finally {
00262 bw.close();
00263 }
00264 } finally {
00265 osw.close();
00266 }
00267 } finally {
00268 fos.close();
00269 }
00270
00271 if (!tmpFile.renameTo(file)) {
00272 throw new IOException("cannot rename "+tmpFile+" to "+file);
00273 }
00274 }
00275
00282 private static void saveNode(@NotNull final BufferedWriter writer, @NotNull final Map<String, Entry> node) throws IOException {
00283 if (node.isEmpty()) {
00284 return;
00285 }
00286
00287 for (final Map.Entry<String, Entry> entry : node.entrySet()) {
00288 final Entry value = entry.getValue();
00289
00290 writer.newLine();
00291
00292 final String documentation = value.getDocumentation();
00293 if (documentation != null) {
00294 writer.write("# ");
00295 writer.write(Codec.encode(documentation));
00296 writer.newLine();
00297 }
00298
00299 writer.write(Codec.encode(entry.getKey()));
00300 writer.write("=");
00301 writer.write(Codec.encode(value.getValue()));
00302 writer.newLine();
00303 }
00304 }
00305
00306 }