Crossfire JXClient, Trunk
Settings.java
Go to the documentation of this file.
1 /*
2  * This file is part of JXClient, the Fullscreen Java Crossfire Client.
3  *
4  * JXClient is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * JXClient is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with JXClient; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  * Copyright (C) 2005-2008 Yann Chachkoff
19  * Copyright (C) 2006-2017,2019-2023 Andreas Kirschbaum
20  * Copyright (C) 2010-2012,2014-2018,2020-2023 Nicolas Weeger
21  */
22 
23 package com.realtime.crossfire.jxclient.settings;
24 
27 import java.io.BufferedReader;
28 import java.io.BufferedWriter;
29 import java.io.IOException;
30 import java.io.LineNumberReader;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.file.Files;
33 import java.nio.file.NoSuchFileException;
34 import java.nio.file.Path;
35 import java.nio.file.StandardCopyOption;
36 import java.util.Map;
37 import java.util.TreeMap;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40 
45 public class Settings {
46 
50  @NotNull
51  private final Path file;
52 
56  @NotNull
57  private final Map<String, Entry> values = new TreeMap<>();
58 
62  private boolean noSave = true;
63 
68  public Settings(@NotNull final Path file) {
69  this.file = file;
70  loadValues();
71  noSave = false;
72  }
73 
80  @NotNull
81  public String getString(@NotNull final SettingsEntry<?> key) {
82  Entry entry = values.get(key.getKey());
83  if (entry == null && (key.getKey().equals("sound_music_enabled") || key.getKey().equals("sound_effects_enabled"))) { // hack to support old settings key as well
84  entry = values.get("sound_enabled");
85  }
86  return entry == null ? key.getDefaultValue().toString() : entry.getValue();
87  }
88 
95  public boolean getBoolean(@NotNull final SettingsEntry<Boolean> key) {
96  final String value = getString(key);
97  try {
98  return Boolean.parseBoolean(value);
99  } catch (final NumberFormatException ignored) {
100  return key.getDefaultValue();
101  }
102  }
103 
110  public int getInt(@NotNull final SettingsEntry<Integer> key) {
111  return NumberParser.parseInt(getString(key), key.getDefaultValue());
112  }
113 
120  public long getLong(@NotNull final SettingsEntry<Long> key) {
121  return NumberParser.parseLong(getString(key), key.getDefaultValue());
122  }
123 
129  public void putString(@NotNull final SettingsEntry<?> key, @NotNull final String value) {
130  final Entry oldEntry = values.get(key.getKey());
131  if (oldEntry == null) {
132  values.put(key.getKey(), new Entry(value, key.getComment()));
133  setChanged();
134  } else {
135  final boolean documentationChanged = oldEntry.setDocumentation(key.getComment());
136  final boolean valueChanged = !oldEntry.getValue().equals(value);
137  if (valueChanged) {
138  oldEntry.setValue(value);
139  }
140  if (documentationChanged || valueChanged) {
141  setChanged();
142  }
143  }
144  }
145 
151  public void putBoolean(@NotNull final SettingsEntry<Boolean> key, final boolean value) {
152  putString(key, Boolean.toString(value));
153  }
154 
160  public void putInt(@NotNull final SettingsEntry<Integer> key, final int value) {
161  putString(key, Integer.toString(value));
162  }
163 
169  public void putLong(@NotNull final SettingsEntry<Long> key, final long value) {
170  putString(key, Long.toString(value));
171  }
172 
177  public void remove(@NotNull final String key) {
178  if (values.remove(key) != null) {
179  setChanged();
180  }
181  }
182 
187  private void setChanged() {
188  if (noSave) {
189  return;
190  }
191 
192  try {
193  saveValues();
194  } catch (final IOException ex) {
195  System.err.println(file+": "+ex.getMessage());
196  }
197  }
198 
202  private void loadValues() {
203  values.clear();
204 
205  try {
206  try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
207  try (LineNumberReader lnr = new LineNumberReader(reader)) {
208  loadValues(lnr);
209  }
210  }
211  } catch (final NoSuchFileException ignored) {
212  // ignore
213  } catch (final IOException ex) {
214  System.err.println(file+": "+ex.getMessage());
215  }
216  }
217 
223  private void loadValues(@NotNull final LineNumberReader lnr) throws IOException {
224  @Nullable String comment = null;
225  while (true) {
226  final String line2 = lnr.readLine();
227  if (line2 == null) {
228  break;
229  }
230  final String line = Codec.decode(line2.trim());
231  if (line.isEmpty()) {
232  comment = null;
233  continue;
234  }
235  if (line.startsWith("#")) {
236  comment = line.substring(1).trim();
237  continue;
238  }
239 
240  final String[] tmp = line.split("=", 2);
241  if (tmp.length != 2) {
242  System.err.println(file+":"+lnr.getLineNumber()+": syntax error");
243  continue;
244  }
245  final String key = tmp[0];
246  final String value = tmp[1];
247 
248  putString(new SettingsEntry<>(key, "", comment), value);
249  comment = null;
250  }
251  }
252 
257  private void saveValues() throws IOException {
258  final Path tmpFile = file.resolveSibling(file.getFileName()+".tmp");
259  try (BufferedWriter bw = Files.newBufferedWriter(tmpFile, StandardCharsets.UTF_8)) {
260  saveNode(bw, values);
261  }
262 
263  Files.move(tmpFile, file, StandardCopyOption.REPLACE_EXISTING);
264  }
265 
272  private static void saveNode(@NotNull final BufferedWriter writer, @NotNull final Map<String, Entry> node) throws IOException {
273  if (node.isEmpty()) {
274  return;
275  }
276 
277  for (Map.Entry<String, Entry> entry : node.entrySet()) {
278  final Entry value = entry.getValue();
279 
280  writer.newLine();
281 
282  final String documentation = value.getDocumentation();
283  if (documentation != null) {
284  writer.write("# ");
285  writer.write(Codec.encode(documentation));
286  writer.newLine();
287  }
288 
289  writer.write(Codec.encode(entry.getKey()));
290  writer.write("=");
291  writer.write(Codec.encode(value.getValue()));
292  writer.newLine();
293  }
294  }
295 
296 }
com.realtime.crossfire.jxclient
com.realtime.crossfire.jxclient.util.NumberParser.parseLong
static long parseLong(@NotNull final String string, final long defaultValue)
Definition: NumberParser.java:80
com.realtime.crossfire.jxclient.settings.Settings.loadValues
void loadValues(@NotNull final LineNumberReader lnr)
Definition: Settings.java:223
com.realtime.crossfire.jxclient.settings.Settings.getLong
long getLong(@NotNull final SettingsEntry< Long > key)
Definition: Settings.java:120
com.realtime.crossfire.jxclient.settings.Settings.getString
String getString(@NotNull final SettingsEntry<?> key)
Definition: Settings.java:81
com.realtime.crossfire.jxclient.settings.Settings.putString
void putString(@NotNull final SettingsEntry<?> key, @NotNull final String value)
Definition: Settings.java:129
com.realtime.crossfire.jxclient.settings.Entry.setValue
void setValue(@NotNull final String value)
Definition: Entry.java:69
com.realtime.crossfire.jxclient.settings.Settings.file
final Path file
Definition: Settings.java:51
com.realtime.crossfire.jxclient.settings.Settings.noSave
boolean noSave
Definition: Settings.java:62
com.realtime.crossfire.jxclient.settings.Settings.putBoolean
void putBoolean(@NotNull final SettingsEntry< Boolean > key, final boolean value)
Definition: Settings.java:151
com.realtime.crossfire.jxclient.util.Codec.encode
static String encode(@NotNull final String str)
Definition: Codec.java:93
com.realtime.crossfire.jxclient.settings.Settings.loadValues
void loadValues()
Definition: Settings.java:202
com.realtime.crossfire.jxclient.util.NumberParser.parseInt
static int parseInt(@NotNull final String string, final int defaultValue)
Definition: NumberParser.java:46
com.realtime.crossfire.jxclient.settings.Settings.values
final Map< String, Entry > values
Definition: Settings.java:57
com.realtime.crossfire.jxclient.settings.Settings.Settings
Settings(@NotNull final Path file)
Definition: Settings.java:68
com.realtime.crossfire.jxclient.settings.Entry
Definition: Entry.java:32
com.realtime.crossfire.jxclient.settings.Settings
Definition: Settings.java:45
com.realtime.crossfire.jxclient.util.Codec
Definition: Codec.java:36
com.realtime.crossfire.jxclient.settings.Settings.saveValues
void saveValues()
Definition: Settings.java:257
com.realtime.crossfire.jxclient.util
Definition: Codec.java:23
com.realtime.crossfire.jxclient.settings.Entry.getValue
String getValue()
Definition: Entry.java:61
com.realtime.crossfire.jxclient.settings.Settings.putInt
void putInt(@NotNull final SettingsEntry< Integer > key, final int value)
Definition: Settings.java:160
com.realtime.crossfire.jxclient.settings.Settings.putLong
void putLong(@NotNull final SettingsEntry< Long > key, final long value)
Definition: Settings.java:169
com.realtime.crossfire.jxclient.util.Codec.decode
static String decode(@NotNull final String str)
Definition: Codec.java:109
com.realtime.crossfire
com.realtime.crossfire.jxclient.settings.Settings.saveNode
static void saveNode(@NotNull final BufferedWriter writer, @NotNull final Map< String, Entry > node)
Definition: Settings.java:272
com.realtime
com.realtime.crossfire.jxclient.settings.Entry.setDocumentation
boolean setDocumentation(@Nullable final String documentation)
Definition: Entry.java:87
com
com.realtime.crossfire.jxclient.settings.Entry.getDocumentation
String getDocumentation()
Definition: Entry.java:78
com.realtime.crossfire.jxclient.settings.Settings.getBoolean
boolean getBoolean(@NotNull final SettingsEntry< Boolean > key)
Definition: Settings.java:95
com.realtime.crossfire.jxclient.util.NumberParser
Definition: NumberParser.java:32
com.realtime.crossfire.jxclient.settings.Settings.setChanged
void setChanged()
Definition: Settings.java:187
com.realtime.crossfire.jxclient.settings.Settings.getInt
int getInt(@NotNull final SettingsEntry< Integer > key)
Definition: Settings.java:110
com.realtime.crossfire.jxclient.settings.SettingsEntry
Definition: SettingsEntry.java:34