package com.vitco.manager.pref; import com.vitco.Main; import com.vitco.manager.error.ErrorHandlerInterface; import com.vitco.settings.VitcoSettings; import com.vitco.util.file.FileTools; import com.vitco.util.misc.AutoFileCloser; import java.io.*; import java.util.ArrayList; import java.util.HashMap; /** * Stores & Loads preferences (Integer, String, Object) * Storing is executed after the PreDestroy of all the other objects. */ public class Preferences implements PreferencesInterface { // preferences private HashMap<String, Object> map = new HashMap<String, Object>(); // listeners private final HashMap<String, ArrayList<PrefChangeListener>> listeners = new HashMap<String, ArrayList<PrefChangeListener>>(); // Note: notification should take place in the order that the listener were added @Override public void notifyListeners(String key, Object value) { synchronized (VitcoSettings.SYNC) { if (listeners.containsKey(key)) { for (PrefChangeListener pcl : listeners.get(key)) { pcl.onPrefChange(value); } } } } @Override public void addPrefChangeListener(String key, PrefChangeListener pcl) { synchronized (VitcoSettings.SYNC) { if (!listeners.containsKey(key)) { // make sure this is init listeners.put(key, new ArrayList<PrefChangeListener>()); } // add the listener listeners.get(key).add(pcl); // call it if value is known if (map.containsKey(key)) { pcl.onPrefChange(map.get(key)); } } } @Override public boolean contains(String key) { synchronized (VitcoSettings.SYNC) { return map.containsKey(key); } } // var & setter private ErrorHandlerInterface errorHandler; @Override public final void setErrorHandler(ErrorHandlerInterface errorHandler) { synchronized (VitcoSettings.SYNC) { this.errorHandler = errorHandler; } } @Override public final void storeObject(String key, Object value) { synchronized (VitcoSettings.SYNC) { if (!map.containsKey(key) || !map.get(key).equals(value)) { map.put(key, value); notifyListeners(key, value); } } } @Override public Object loadObject(String key) { synchronized (VitcoSettings.SYNC) { return map.containsKey(key) ? map.get(key) : null; } } @Override public void storeBoolean(String key, boolean value) { synchronized (VitcoSettings.SYNC) { storeObject(key, value); } } @Override public void storeInteger(String key, int value) { synchronized (VitcoSettings.SYNC) { storeObject(key, value); } } @Override public void storeString(String key, String value) { synchronized (VitcoSettings.SYNC) { storeObject(key, value); } } @Override public boolean loadBoolean(String key) { synchronized (VitcoSettings.SYNC) { return map.containsKey(key) ? (Boolean)map.get(key) : false; } } @Override public int loadInteger(String key) { synchronized (VitcoSettings.SYNC) { return map.containsKey(key) ? (Integer)map.get(key) : 0; } } @Override public final String loadString(String key) { synchronized (VitcoSettings.SYNC) { return map.containsKey(key) ? (String)map.get(key) : ""; } } private static String getUserDataDirectory() { return System.getProperty("user.home") + File.separator + ".voxelshop" + File.separator; } // var % setter private String storageFileName; @Override public final void setStorageFile(String filename) { synchronized (VitcoSettings.SYNC) { if (Main.isDebugMode()) { storageFileName = filename; } else { // use user directory to store configuration storageFileName = getUserDataDirectory() + File.separator + filename; } } } // "manually" executed after all PreDestroys are called @Override public void save() { synchronized (VitcoSettings.SYNC) { // store the map in file final File dataFile = new File(storageFileName); if (dataFile.getParentFile().exists() || dataFile.getParentFile().mkdirs()) { try { new AutoFileCloser() { @Override protected void doWork() throws Throwable { FileOutputStream fileOut = autoClose(new FileOutputStream(dataFile)); ObjectOutputStream out = autoClose(new ObjectOutputStream(fileOut)); out.writeObject(map); } }; } catch (RuntimeException e) { errorHandler.handle(e); } } } } // executed when initiated (spring "init-method") @Override public void load() { synchronized (VitcoSettings.SYNC) { final File dataFile = new File(storageFileName); if (dataFile.exists()) { try { new AutoFileCloser() { @Override protected void doWork() throws Throwable { try { FileInputStream fileIn = autoClose(new FileInputStream(dataFile)); ObjectInputStream in = autoClose(new ObjectInputStream(fileIn)); map = FileTools.castHash((HashMap) in.readObject(), String.class, Object.class); } catch (InvalidClassException ignored) { // ignore any error, it's "ok" for the pref file to be corrupt } catch (EOFException ignored) { } catch (StreamCorruptedException ignored) { } catch (RuntimeException ignored) {} } }; } catch (RuntimeException e) { errorHandler.handle(e); } } } } }