package net.sourceforge.tuned; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; public class PreferencesMap implements Map { private final Preferences prefs; private final Adapter adapter; public PreferencesMap(Preferences prefs, Adapter adapter) { this.prefs = prefs; this.adapter = adapter; } @Override public T get(Object key) { return adapter.get(prefs, key.toString()); } @Override public T put(String key, T value) { adapter.put(prefs, key, value); // don't know previous entry return null; } @Override public T remove(Object key) { adapter.remove(prefs, key.toString()); // don't know removed entry return null; } public String[] keys() { try { return adapter.keys(prefs); } catch (BackingStoreException e) { throw new RuntimeException(e); } } @Override public void clear() { for (String key : keys()) { adapter.remove(prefs, key); } } @Override public boolean containsKey(Object key) { if (key instanceof String) { return Arrays.asList(keys()).contains(key); } return false; } @Override public boolean containsValue(Object value) { for (String key : keys()) { if (value.equals(get(key))) return true; } return false; } @Override public Set> entrySet() { Set> entries = new LinkedHashSet>(); for (String key : keys()) { entries.add(new PreferencesEntry(prefs, key, adapter)); } return entries; } @Override public boolean isEmpty() { return size() == 0; } @Override public Set keySet() { return new LinkedHashSet(Arrays.asList(keys())); } @Override public void putAll(Map map) { for (Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } @Override public int size() { return keys().length; } @Override public Collection values() { List values = new ArrayList(); for (String key : keys()) { values.add(get(key)); } return values; } public static PreferencesMap map(Preferences prefs) { return map(prefs, new StringAdapter()); } public static PreferencesMap map(Preferences prefs, Adapter adapter) { return new PreferencesMap(prefs, adapter); } public static interface Adapter { public String[] keys(Preferences prefs) throws BackingStoreException; public T get(Preferences prefs, String key); public void put(Preferences prefs, String key, T value); public void remove(Preferences prefs, String key); } public static abstract class AbstractAdapter implements Adapter { @Override public abstract T get(Preferences prefs, String key); @Override public abstract void put(Preferences prefs, String key, T value); @Override public String[] keys(Preferences prefs) throws BackingStoreException { return prefs.keys(); } @Override public void remove(Preferences prefs, String key) { prefs.remove(key); } } public static class StringAdapter extends AbstractAdapter { @Override public String get(Preferences prefs, String key) { return prefs.get(key, null); } @Override public void put(Preferences prefs, String key, String value) { prefs.put(key, value); } } public static class SimpleAdapter extends AbstractAdapter { private final Constructor constructor; public SimpleAdapter(Class type) { try { constructor = type.getConstructor(String.class); } catch (Exception e) { throw new IllegalArgumentException(e); } } @Override public T get(Preferences prefs, String key) { String value = prefs.get(key, null); if (value != null) { try { return constructor.newInstance(value); } catch (InvocationTargetException e) { // try to throw the cause directly, e.g. NumberFormatException throw ExceptionUtilities.asRuntimeException(e.getCause()); } catch (Exception e) { throw new RuntimeException(e); } } return null; } @Override public void put(Preferences prefs, String key, T value) { prefs.put(key, value.toString()); } public static SimpleAdapter forClass(Class type) { return new SimpleAdapter(type); } } public static class SerializableAdapter extends AbstractAdapter { @SuppressWarnings("unchecked") @Override public T get(Preferences prefs, String key) { byte[] bytes = prefs.getByteArray(key, null); if (bytes == null) return null; try { ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes)); Object object = in.readObject(); in.close(); return (T) object; } catch (Exception e) { throw new RuntimeException(e); } } @Override public void put(Preferences prefs, String key, T value) { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); try { ObjectOutputStream out = new ObjectOutputStream(buffer); out.writeObject(value); out.close(); prefs.putByteArray(key, buffer.toByteArray()); } catch (IOException e) { throw new RuntimeException(e); } } } public static class PreferencesEntry implements Entry { private final String key; private final Preferences prefs; private final Adapter adapter; public PreferencesEntry(Preferences prefs, String key, Adapter adapter) { this.key = key; this.prefs = prefs; this.adapter = adapter; } @Override public String getKey() { return key; } @Override public T getValue() { return adapter.get(prefs, key); } @Override public T setValue(T value) { adapter.put(prefs, key, value); return null; } public PreferencesEntry defaultValue(T value) { try { // check if value valid and not null getValue().getClass(); } catch (Exception e) { // illegal value or null, just override setValue(value); } return this; } public void remove() { adapter.remove(prefs, key); } } }