diff --git a/source/net/sourceforge/tuned/ExceptionUtil.java b/source/net/sourceforge/tuned/ExceptionUtil.java new file mode 100644 index 00000000..c227c9e3 --- /dev/null +++ b/source/net/sourceforge/tuned/ExceptionUtil.java @@ -0,0 +1,24 @@ + +package net.sourceforge.tuned; + + +public class ExceptionUtil { + + public static Throwable getRootCause(Throwable t) { + while (t.getCause() != null) { + t = t.getCause(); + } + + return t; + } + + + public static RuntimeException asRuntimeException(Throwable t) { + if (t instanceof RuntimeException) { + return (RuntimeException) t; + } + + return new RuntimeException(t); + } + +} diff --git a/source/net/sourceforge/tuned/FunctionIterator.java b/source/net/sourceforge/tuned/FunctionIterator.java new file mode 100644 index 00000000..924ff777 --- /dev/null +++ b/source/net/sourceforge/tuned/FunctionIterator.java @@ -0,0 +1,101 @@ + +package net.sourceforge.tuned; + + +import java.util.Iterator; +import java.util.NoSuchElementException; + + +public class FunctionIterator implements Iterator { + + /** + * A Function transforms one Object into another. + * + * @param type of source Objects + * @param type Objects are transformed into + */ + public static interface Function { + + /** + * Transform the given sourceValue into any kind of Object. + * + * @param sourceValue - the Object to transform + * @return the transformed version of the object + */ + public T evaluate(S sourceValue); + } + + private final Iterator sourceIterator; + private final Function function; + + + public FunctionIterator(Iterable source, Function function) { + this(source.iterator(), function); + } + + + public FunctionIterator(Iterator iterator, Function function) { + this.sourceIterator = iterator; + this.function = function; + } + + + @Override + public boolean hasNext() { + try { + return peekNext() != null; + } catch (Exception e) { + return true; + } + } + + + @Override + public T next() { + if (!hasNext()) + throw new NoSuchElementException(); + + try { + return peekNext(); + } finally { + cache = null; + currentException = null; + } + } + + private T cache = null; + private RuntimeException currentException = null; + + + private T peekNext() { + while (cache == null && (sourceIterator.hasNext() || currentException != null)) { + if (currentException != null) + throw currentException; + + try { + cache = transform(sourceIterator.next()); + } catch (RuntimeException e) { + currentException = e; + } + } + + return cache; + } + + + private T transform(S sourceValue) { + return function.evaluate(sourceValue); + } + + + /** + * The remove operation is not supported by this implementation of Iterator. + * + * @throws UnsupportedOperationException if this method is invoked. + * @see java.util.Iterator + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/source/net/sourceforge/tuned/PreferencesList.java b/source/net/sourceforge/tuned/PreferencesList.java new file mode 100644 index 00000000..c57cc7dc --- /dev/null +++ b/source/net/sourceforge/tuned/PreferencesList.java @@ -0,0 +1,96 @@ + +package net.sourceforge.tuned; + + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.List; +import java.util.prefs.Preferences; + +import net.sourceforge.tuned.PreferencesMap.Adapter; + + +public class PreferencesList extends AbstractList { + + private final PreferencesMap prefs; + + + public PreferencesList(PreferencesMap preferencesMap) { + this.prefs = preferencesMap; + } + + + @Override + public T get(int index) { + return prefs.get(key(index)); + } + + + private String key(int index) { + return Integer.toString(index); + } + + + @Override + public int size() { + return prefs.size(); + } + + + @Override + public boolean add(T e) { + prefs.put(key(size()), e); + return true; + } + + + @Override + public T remove(int index) { + + int lastIndex = size() - 1; + + List shiftList = new ArrayList(subList(index, lastIndex + 1)); + + T value = shiftList.remove(0); + + prefs.remove(key(lastIndex)); + + for (T element : shiftList) { + set(index, element); + index++; + } + + return value; + } + + + @Override + public T set(int index, T element) { + if (index < 0 || index >= size()) + throw new IndexOutOfBoundsException(); + + return prefs.put(key(index), element); + } + + + @Override + public void clear() { + prefs.clear(); + } + + + public void set(List data) { + clear(); + addAll(data); + } + + + public static PreferencesList map(Preferences prefs, Class type) { + return new PreferencesList(PreferencesMap.map(prefs, type)); + } + + + public static PreferencesList map(Preferences prefs, Adapter adapter) { + return new PreferencesList(PreferencesMap.map(prefs, adapter)); + } +} diff --git a/source/net/sourceforge/tuned/PreferencesMap.java b/source/net/sourceforge/tuned/PreferencesMap.java new file mode 100644 index 00000000..aeff1b96 --- /dev/null +++ b/source/net/sourceforge/tuned/PreferencesMap.java @@ -0,0 +1,316 @@ + +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.HashSet; +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) { + if (key instanceof String) { + return adapter.get(prefs, (String) key); + } + + throw new IllegalArgumentException("Key must be a String"); + } + + + @Override + public T put(String key, T value) { + adapter.put(prefs, key, value); + + return value; + } + + + /** + * @return always null + */ + @Override + public T remove(Object key) { + if (key instanceof String) { + prefs.remove((String) key); + } + + return null; + } + + + public String[] keys() { + try { + return prefs.keys(); + } catch (BackingStoreException e) { + throw new RuntimeException(e); + } + } + + + @Override + public void clear() { + try { + prefs.clear(); + } catch (BackingStoreException e) { + throw new RuntimeException(e); + } + } + + + public void set(Map data) { + clear(); + putAll(data); + } + + + @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 Entry(key)); + } + + return entries; + } + + + @Override + public boolean isEmpty() { + return size() == 0; + } + + + @Override + public Set keySet() { + return new HashSet(Arrays.asList(keys())); + } + + + @Override + public void putAll(Map map) { + for (String key : map.keySet()) { + put(key, map.get(key)); + } + } + + + @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; + } + + + private class Entry implements Map.Entry { + + private final String key; + + + public Entry(String key) { + this.key = key; + } + + + @Override + public String getKey() { + return key; + } + + + @Override + public T getValue() { + return get(key); + } + + + @Override + public T setValue(T value) { + return put(key, value); + } + + } + + + @SuppressWarnings("unchecked") + public static PreferencesMap map(Preferences prefs, Class type) { + Adapter adapter; + + if (type == String.class) { + // prefer StringAdapter, because SimpleAdapter would use the copy constructor of String, instead of returning the values directly + adapter = (Adapter) new StringAdapter(); + } else { + adapter = new SimpleAdapter(type); + } + + return map(prefs, adapter); + } + + + public static PreferencesMap map(Preferences prefs, Adapter adapter) { + return new PreferencesMap(prefs, adapter); + } + + + public static interface Adapter { + + public T get(Preferences prefs, String key); + + + public void put(Preferences prefs, String key, T value); + } + + + public static class StringAdapter implements Adapter { + + @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 implements Adapter { + + 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 stringValue = prefs.get(key, null); + + if (stringValue == null) + return null; + + try { + return constructor.newInstance(stringValue); + } catch (InvocationTargetException e) { + throw ExceptionUtil.asRuntimeException(e.getCause()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + @Override + public void put(Preferences prefs, String key, T value) { + prefs.put(key, value.toString()); + } + + } + + + public static class SerializableAdapter implements Adapter { + + @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); + } + } + } + +}