20 package net.sf.gridarta.preferences;
22 import java.io.BufferedWriter;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStreamReader;
29 import java.io.LineNumberReader;
30 import java.io.OutputStreamWriter;
33 import java.util.TreeMap;
34 import java.util.TreeSet;
35 import java.util.prefs.BackingStoreException;
36 import java.util.regex.Pattern;
37 import net.
sf.japi.swing.action.ActionBuilder;
38 import net.
sf.japi.swing.action.ActionBuilderFactory;
39 import org.apache.log4j.Category;
40 import org.apache.log4j.Logger;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
60 private static final Category
LOG = Logger.getLogger(
Storage.class);
89 private final Map<String, Map<String, String>>
values =
new TreeMap<>();
97 public Storage(@NotNull
final String defaultPath, @Nullable
final File file) {
98 if (LOG.isDebugEnabled()) {
113 public void newNode(@NotNull
final String path) {
114 if (LOG.isDebugEnabled()) {
115 LOG.debug(
"newNode(" + path +
")");
118 if (!values.containsKey(path)) {
119 values.put(path,
new TreeMap<>());
131 if (LOG.isDebugEnabled()) {
132 LOG.debug(
"childrenNames(" + path +
")");
135 final String prefix = path +
"/";
136 final Set<String> result =
new TreeSet<>();
137 for (
final String key : values.keySet()) {
138 if (key.startsWith(prefix)) {
139 result.add(key.substring(prefix.length()));
142 return result.toArray(
new String[result.size()]);
153 public String
getValue(@NotNull
final String path, @NotNull
final String key) {
154 if (LOG.isDebugEnabled()) {
155 LOG.debug(
"getValue(" + path +
", " + key +
")");
158 final Map<String, String> map = values.get(path);
170 public String[]
getKeys(@NotNull
final String path) {
171 if (LOG.isDebugEnabled()) {
172 LOG.debug(
"getKeys(" + path +
")");
175 final Map<String, String> map = values.get(path);
177 final Set<String> keys = map.keySet();
178 return keys.toArray(
new String[keys.size()]);
187 public void putValue(@NotNull
final String path, @NotNull
final String key, @NotNull
final String value) {
188 if (LOG.isDebugEnabled()) {
189 LOG.debug(
"putValue(" + path +
", " + key +
", " + value +
")");
192 final Map<String, String> map = values.get(path);
194 final String oldValue = map.put(key, value);
195 if (oldValue == null || !oldValue.equals(value)) {
205 if (LOG.isDebugEnabled()) {
206 LOG.debug(
"removeNode(" + path +
")");
209 if (values.remove(path) != null) {
219 public void removeValue(@NotNull
final String path, @NotNull
final String key) {
220 if (LOG.isDebugEnabled()) {
221 LOG.debug(
"removeValue(" + path +
", " + key +
")");
224 final Map<String, String> map = values.get(path);
226 if (map.remove(key) != null) {
237 public void sync(
final boolean sync)
throws BackingStoreException {
238 if (LOG.isDebugEnabled()) {
239 LOG.debug(
"sync(" +
sync +
")");
244 }
catch (
final IOException ex) {
245 throw new BackingStoreException(ex);
260 }
catch (
final IOException ex) {
261 LOG.warn(file +
": " + ex.getMessage());
273 if (LOG.isDebugEnabled()) {
274 LOG.debug(
"loadValues: " + file);
280 final FileInputStream fis =
new FileInputStream(file);
282 final InputStreamReader isr =
new InputStreamReader(fis,
"UTF-8");
284 final LineNumberReader lnr =
new LineNumberReader(isr);
296 }
catch (
final FileNotFoundException ignored) {
298 }
catch (
final IOException ex) {
299 LOG.warn(file +
": " + ex.getMessage());
308 private void loadValues(@NotNull
final LineNumberReader lnr)
throws IOException {
311 final String line2 = lnr.readLine();
316 if (line.startsWith(
"#") || line.isEmpty()) {
320 if (line.startsWith(
"[") && line.endsWith(
"]")) {
321 path = line.substring(1, line.length() - 1);
325 final String[] tmp = PATTERN_EQUAL.split(line, 2);
326 if (tmp.length != 2) {
327 LOG.warn(file +
":" + lnr.getLineNumber() +
": syntax error");
330 final String key = tmp[0];
331 final String value = tmp[1];
347 if (LOG.isDebugEnabled()) {
348 LOG.debug(
"saveValues: " + file);
351 final File tmpFile =
new File(file.getPath() +
".tmp");
352 final FileOutputStream fos =
new FileOutputStream(tmpFile);
354 final OutputStreamWriter osw =
new OutputStreamWriter(fos,
"UTF-8");
356 final BufferedWriter bw =
new BufferedWriter(osw);
358 final Map<String, String> defaultNode = values.get(defaultPath);
359 if (defaultNode != null) {
363 for (
final Map.Entry<String, Map<String, String>> e : values.entrySet()) {
364 if (!e.getKey().equals(defaultPath)) {
365 saveNode(bw, e.getKey(), e.getValue());
380 if (!tmpFile.renameTo(file)) {
381 throw new IOException(
"cannot rename " + tmpFile +
" to " + file);
392 private static void saveNode(@NotNull
final BufferedWriter writer, @Nullable
final String path, @NotNull
final Map<String, String> node)
throws IOException {
393 if (node.isEmpty()) {
397 final ActionBuilder actionBuilder = ActionBuilderFactory.getInstance().getActionBuilder(
"net.sf.gridarta");
407 for (
final Map.Entry<String, String> entry : node.entrySet()) {
410 final String comment = actionBuilder.getString(
"prefs." + PATTERN_IGNORE.matcher(entry.getKey()).replaceAll(
""));
411 if (comment != null) {
413 writer.write(comment);
static String encode(@NotNull final String str)
Encode a string to make it fit into one line.
String [] getKeys(@NotNull final String path)
Return all of the keys that have an associated value in a node.
static String decode(@NotNull final String str)
Decode a string which was encoded by encode(String).
static final Pattern PATTERN_EQUAL
The pattern that matches a single equal sign ("=").
final String defaultPath
The default key name for loading/saving values.
void newNode(@NotNull final String path)
Make sure a node exists.
String getValue(@NotNull final String path, @NotNull final String key)
Return the value associated with the specified key at a node, or.
void removeValue(@NotNull final String path, @NotNull final String key)
Remove the association (if any) for the specified key at a node.
Maintains a set of preference values.
Utility class to encode arbitrary Strings to fit in a single text line.
Storage(@NotNull final String defaultPath, @Nullable final File file)
Create a new instance.
void putValue(@NotNull final String path, @NotNull final String key, @NotNull final String value)
Put the given key-value association into a node.
boolean noSave
If set, do not save changes into file.
static final Pattern PATTERN_IGNORE
Pattern to ignore in path names.
static void saveNode(@NotNull final BufferedWriter writer, @Nullable final String path, @NotNull final Map< String, String > node)
Save one node.
static final Category LOG
The Logger for printing log messages.
String [] childrenNames(@NotNull final String path)
Return the names of the children of a node.
void setChanged()
This function is called whenever the contents of values has changed.
void removeNode(@NotNull final String path)
Remove a preference node including all preferences that it contains.
final Map< String, Map< String, String > > values
The stored values.
void loadValues(@NotNull final LineNumberReader lnr)
Load the values from a LineNumberReader.
void sync(final boolean sync)
Save changes to the underlying file.
void loadValues()
Load the values from the backing file.
void saveValues()
Save the values to the backing file.
final File file
The file for loading/saving values.