diff --git a/src/main/java/org/scijava/prefs/DefaultPrefService.java b/src/main/java/org/scijava/prefs/DefaultPrefService.java index f5bb0529b..df189324f 100644 --- a/src/main/java/org/scijava/prefs/DefaultPrefService.java +++ b/src/main/java/org/scijava/prefs/DefaultPrefService.java @@ -378,6 +378,28 @@ public List getList(final Class prefClass) { return getList(preferences); } + @Override + public Iterable getIterable(final String key) { + return getIterable((Class) null, key); + } + + @Override + public Iterable getIterable(final Class prefClass, final String key) { + final Preferences preferences = prefs(prefClass); + return getIterable(preferences.node(key)); + } + + @Override + public void putIterable(final Iterable iterable, final String key) { + putIterable((Class) null, iterable, key); + } + + @Override + public void putIterable(final Class prefClass, final Iterable iterable, final String key) { + final Preferences preferences = prefs(prefClass); + putIterable(preferences.node(key), iterable); + } + // -- Helper methods -- private void clear(final Preferences preferences, final String key) { @@ -460,6 +482,60 @@ private List getList(final Preferences preferences) { return list; } + private void putIterable(final Preferences preferences, + final Iterable iterable) + { + if (preferences == null) { + throw new IllegalArgumentException("Preferences not set."); + } + int index = 0; + for (final String value : iterable) { + preferences.put("" + index++, value == null ? null : value.toString()); + } + } + + private Iterable getIterable(final Preferences preferences) + { + if (preferences == null) { + throw new IllegalArgumentException("Preferences not set."); + } + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + private String value; + private int index; + { + findNext(); + } + + @Override + public String next() { + final String result = value; + findNext(); + return result; + } + + @Override + public boolean hasNext() { + return value != null; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + private void findNext() { + if (index < 0) return; + value = preferences.get("" + index, null); + index = value == null ? -1 : index + 1; + } + }; + } + }; + } + private Preferences prefs(final Class c) { return Preferences.userNodeForPackage(c == null ? PrefService.class : c); } diff --git a/src/main/java/org/scijava/prefs/PrefService.java b/src/main/java/org/scijava/prefs/PrefService.java index 590f8259c..21d4ac537 100644 --- a/src/main/java/org/scijava/prefs/PrefService.java +++ b/src/main/java/org/scijava/prefs/PrefService.java @@ -194,4 +194,24 @@ public interface PrefService extends SciJavaService { * prefs. */ List getList(Class prefClass); + + /** + * Puts an iterable into the preferences. + */ + void putIterable(Iterable iterable, String key); + + /** + * Puts an iterable into the preferences. + */ + void putIterable(Class prefClass, Iterable iterable, String key); + + /** + * Gets an iterable from the preferences. + */ + Iterable getIterable(String key); + + /** + * Gets an iterable from the preferences. + */ + Iterable getIterable(Class prefClass, String key); } diff --git a/src/main/java/org/scijava/script/DefaultScriptInterpreter.java b/src/main/java/org/scijava/script/DefaultScriptInterpreter.java new file mode 100644 index 000000000..6f94198fe --- /dev/null +++ b/src/main/java/org/scijava/script/DefaultScriptInterpreter.java @@ -0,0 +1,102 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2014 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package org.scijava.script; + +import javax.script.ScriptEngine; +import javax.script.ScriptException; + +import org.scijava.prefs.PrefService; + +/** + * The default implementation of a {@link ScriptInterpreter}. + * + * @author Johannes Schindelin + */ +public class DefaultScriptInterpreter implements ScriptInterpreter { + + private final ScriptLanguage language; + private final ScriptEngine engine; + private final History history; + + /** + * Constructs a new {@link DefaultScriptInterpreter}. + * + * @param scriptService the script service + * @param language the script language + */ + public DefaultScriptInterpreter(final PrefService prefs, + final ScriptService scriptService, final ScriptLanguage language) + { + this.language = language; + engine = language.getScriptEngine(); + history = new History(prefs, engine.getClass().getName()); + readHistory(); + } + + @Override + public synchronized void readHistory() { + if (history == null) return; + history.read(); + } + + @Override + public synchronized void writeHistory() { + if (history == null) return; + history.write(); + } + + @Override + public synchronized String walkHistory(final String currentCommand, + final boolean forward) + { + if (history == null) return currentCommand; + history.replace(currentCommand); + return forward ? history.next() : history.previous(); + } + + @Override + public void eval(final String command) throws ScriptException { + if (history != null) history.add(command); + if (engine == null) throw new java.lang.IllegalArgumentException(); + engine.eval(command); + } + + @Override + public ScriptLanguage getLanguage() { + return language; + } + + @Override + public ScriptEngine getEngine() { + return engine; + } + +} diff --git a/src/main/java/org/scijava/script/History.java b/src/main/java/org/scijava/script/History.java new file mode 100644 index 000000000..cb0f181cb --- /dev/null +++ b/src/main/java/org/scijava/script/History.java @@ -0,0 +1,145 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2014 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.script; + +import org.scijava.prefs.PrefService; +import org.scijava.util.LastRecentlyUsed; + +/** + * Container for a script language's interpreter history. + * + * @author Johannes Schindelin + */ +class History { + + protected static final long serialVersionUID = 1L; + + private static final String PREFIX = "History."; + private final int MAX_ENTRIES = 1000; + + private final PrefService prefs; + private final String name; + private final LastRecentlyUsed entries = new LastRecentlyUsed(MAX_ENTRIES); + private String currentCommand = ""; + private int position = -1; + + /** + * Constructs a history object for a given scripting language. + * + * @param name the name of the scripting language + */ + public History(final PrefService prefs, final String name) { + this.prefs = prefs; + this.name = name; + } + + /** + * Read back a persisted history. + */ + public void read() { + entries.clear(); + for (final String item : prefs.getIterable(getClass(), PREFIX + name)) { + entries.addToEnd(item); + }; + } + + /** + * Persist the history. + * + * @see {@link Prefs} + */ + public void write() { + prefs.putIterable(getClass(), entries, PREFIX + name); + } + + /** + * Adds the most recently issued command. + * + * @param command the most recent command to add to the history + */ + public void add(final String command) { + entries.add(command); + position = -1; + currentCommand = ""; + } + + public boolean replace(final String currentCommand) { + if (position < 0) { + this.currentCommand = currentCommand; + return false; + } + return entries.replace(position, currentCommand); + } + + /** + * Navigates to the next (more recent) command. + *

+ * This method wraps around, i.e. it returns {@code null} when there is no + * more-recent command in the history. + *

+ * + * @return the next command + */ + public String next() { + position = entries.next(position); + return position < 0 ? currentCommand : entries.get(position); + } + + /** + * Navigates to the previous (i.e less recent) command. + *

+ * This method wraps around, i.e. it returns {@code null} when there is no + * less-recent command in the history. + *

+ * + * @return the previous command + */ + public String previous() { + position = entries.previous(position); + return position < 0 ? currentCommand : entries.get(position); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + int position = -1; + for (;;) { + position = entries.previous(position); + if (position < 0) break; + if (builder.length() > 0) builder.append(" -> "); + if (this.position == position) builder.append("["); + builder.append(entries.get(position)); + if (this.position == position) builder.append("]"); + } + return builder.toString(); + } +} diff --git a/src/main/java/org/scijava/script/ScriptInterpreter.java b/src/main/java/org/scijava/script/ScriptInterpreter.java new file mode 100644 index 000000000..936f211af --- /dev/null +++ b/src/main/java/org/scijava/script/ScriptInterpreter.java @@ -0,0 +1,83 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2014 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.script; + +import javax.script.ScriptEngine; +import javax.script.ScriptException; + +/** + * The contract for script interpreters. + * + * @author Johannes Schindelin + */ +public interface ScriptInterpreter { + + /** + * Reads the persisted history of the current script interpreter. + */ + void readHistory(); + + /** + * Persists the history of the current script interpreter. + */ + void writeHistory(); + + /** + * Obtains the next/previous command in the command history. + * + * @param currentCommand the current command (will be stored in the history) + * @param forward if true, the next history entry is returned (more recent), + * if false, the previous one + * @return the next/previous command + */ + String walkHistory(String currentCommand, boolean forward); + + /** + * Evaluates a command. + * + * @param command the command to evaluate + * @throws ScriptException + */ + void eval(String command) throws ScriptException; + + /** + * Returns the associated {@link ScriptLanguage}. + */ + ScriptLanguage getLanguage(); + + /** + * Returns the associated {@link ScriptEngine}. + * + * @return the script engine + */ + ScriptEngine getEngine(); +} diff --git a/src/main/java/org/scijava/util/LastRecentlyUsed.java b/src/main/java/org/scijava/util/LastRecentlyUsed.java new file mode 100644 index 000000000..c3fb19e9d --- /dev/null +++ b/src/main/java/org/scijava/util/LastRecentlyUsed.java @@ -0,0 +1,392 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2014 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.util; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A simple container for {@code N} last-recently-used items. + * + * @author Johannes Schindelin + */ +public class LastRecentlyUsed implements Iterable, Collection { + private final Object[] entries; + private final Map map; + /** + * The double-linked list pointers. + *

+ * The {@code top} variable points to the most recently added, the + * {@code bottom} variable to the oldest entry. The {@code next} and + * {@code previous} arrays point to the next newer/next older entry. + *

+ *

+ * For initialization performance, all of {@code next}, {@code previous}, + * {@code top} and {@code bottom} are initialized to {@code 0}, meaning that + * you need to decrement the values by one in order to obtain the entry index. + * Example: the index of the most recently added entry is {@code top -1}, and + * {@code next[top - 1]} is {@code 0} because there is no newer entry than the + * newest entry. + *

+ */ + private final int[] next, previous; + private int top, bottom; + + public LastRecentlyUsed(int size) { + entries = new Object[2 * size]; + next = new int[2 * size]; + previous = new int[2 * size]; + map = new HashMap(); + } + + /** + * Given the index of an entry, returns the index of the next newer entry. + * + * @param index the index of the current entry, or -1 to wrap around to the oldest entry. + * @return the index of the next newer entry, or -1 when there is no such entry. + */ + public int next(int index) { + return index < 0 ? bottom - 1 : next[index] - 1; + } + + /** + * Given the index of an entry, returns the index of the next older entry. + * + * @param index the index of the current entry, or -1 to wrap around to the newest entry. + * @return the index of the next older entry, or -1 when there is no such entry. + */ + public int previous(int index) { + return index < 0 ? top - 1 : previous[index] - 1; + } + + /** + * Returns the entry for the given index. + * + * @param index the index of the entry + * @return the entry + */ + @SuppressWarnings("unchecked") + public T get(int index) { + return (T) entries[index]; + } + + /** + * Looks up the index for a given entry. + * + * @param value the value of the entry to find + * @return the corresponding index, or {@code -1} if the entry was not found + */ + public int lookup(final T value) { + final Integer result = map.get(value); + return result == null ? -1 : (int) result; + } + + /** + * Add a new newest entry. + * + * @param value the value of the entry + * @return whether the entry was added + */ + public boolean add(final T value) { + return add(value, false); + } + + /** + * Add a new oldest entry. + *

+ * This method helps recreating {@link LastRecentlyUsed} instances given the + * entries in the order newest first, oldest last. + *

+ * + * @param value the value of the entry to add + */ + public void addToEnd(final T value) { + add(value, true); + } + + public boolean replace(final int index, T newValue) { + final Object previousValue = get(index); + if (previousValue == null) { + throw new IllegalArgumentException("No current entry at position " + + index); + } + if (newValue.equals(previous)) return false; + map.remove(previousValue); + map.put(newValue, index); + entries[index] = newValue; + return true; + } + + /** + * Empties the data structure. + */ + public void clear() { + top = bottom = 0; + map.clear(); + for (int i = 0; i < entries.length; i++) { + entries[i] = null; + next[i] = previous[i] = 0; + } + } + + @Override + public boolean addAll(final Collection values) { + for (final T value : values) { + add(value); + } + return true; + } + + @Override + public boolean contains(final Object value) { + return map.containsKey(value); + } + + @Override + public boolean containsAll(final Collection values) { + return map.keySet().containsAll(values); + } + + @Override + public boolean isEmpty() { + return top == 0; + } + + @Override + public boolean remove(Object value) { + final Integer index = map.get(value); + if (index == null) return false; + remove(index.intValue()); + return true; + } + + @Override + public boolean removeAll(Collection values) { + boolean result = true; + for (final Object value : values) { + result = remove(value) && result; + } + return result; + } + + @Override + public boolean retainAll(Collection values) { + for (int index = top - 1; index >= 0; ) { + final int prev = previous[index] - 1; + if (!values.contains(get(index))) { + remove(index); + } + index = prev; + } + return containsAll(values); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public Object[] toArray() { + final Object[] result = new Object[size()]; + for (int i = 0, index = top - 1; index >= 0; i++, index = + previous[index] - 1) + { + result[i] = get(index); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + public S[] toArray(final S[] array) { + final int size = size(); + if (array.length >= size) { + for (int i = 0, index = top - 1; index >= 0; i++, index = + previous[index] - 1) + { + array[i] = (S) get(index); + } + return array; + } + final S[] result = + (S[]) Array.newInstance(array.getClass().getComponentType(), size); + for (int i = 0, index = top - 1; index >= 0; i++, index = + previous[index] - 1) + { + result[i] = (S) get(index); + } + return result; + } + + /** + * Returns an {@link Iterator}. + * + * @return the iterator + */ + public Iterator iterator() { + return new Iterator() { + + private int position = top - 1; + + @Override + public boolean hasNext() { + return position >= 0; + } + + @Override + public T next() { + @SuppressWarnings("unchecked") + final T result = (T) entries[position]; + position = previous[position] - 1; + return result; + } + + @Override + public void remove() { + LastRecentlyUsed.this.remove(position == 0 ? top - 1 : next[position] - 1); + } + + }; + } + + // -- private methods + + private void remove(int position) { + assert(entries[position] != null); + map.remove(entries[position]); + entries[position] = null; + if (next[position] == 0) { + top = previous[position]; + } + else { + previous[next[position] - 1] = previous[position]; + } + if (previous[position] == 0) { + bottom = next[position]; + } + else { + next[previous[position] - 1] = next[position]; + } + next[position] = previous[position] = 0; + } + + private boolean add(final T value, boolean addAtEnd) { + final Integer existing = map.get(value); + int insert; + if (existing != null) { + insert = existing; + remove(insert); + } + else if (map.size() == entries.length / 2) { + insert = bottom - 1; + remove(insert); + } + else { + insert = value.hashCode() % entries.length; + if (insert < 0) insert += entries.length; + while (insert < entries.length && entries[insert] != null) insert++; + } + add(insert, value, addAtEnd); + return existing == null; + } + + private void add(int position, T value, boolean atEnd) { + assert(next[position] == 0); + assert(previous[position] == 0); + assert(entries[position] == null); + + map.put(value, position); + entries[position] = value; + if (atEnd) { + next[position] = bottom; + if (bottom > 0) previous[bottom - 1] = position + 1; + bottom = position + 1; + if (top == 0) top = bottom; + } + else { + previous[position] = top; + if (top > 0) { + next[top - 1] = position + 1; + } + top = position + 1; + if (bottom == 0) bottom = top; + } + } + + // For testing + protected void assertConsistency() { + if (top == 0) { + assert(bottom == 0); + assert(map.size() == 0); + for (int i = 0; i < entries.length; i++) { + assert(entries[i] == null); + assert(next[i] == 0); + assert(previous[i] == 0); + } + return; + } + assert(bottom != 0); + final Set indices = new HashSet(map.values()); + assert(indices.size() == map.size()); + for (int i = 0; i < entries.length; i++) { + if (indices.contains(i)) { + assert(entries[i] != null); + assert(map.get(entries[i]) == i); + if (i == top - 1 || top == bottom) { + assert(next[i] == 0); + } + else { + assert(next[i] > 0); + assert(previous[next[i] - 1] == i + 1); + } + if (i == bottom - 1 || top == bottom) { + assert(previous[i] == 0); + } + else { + assert(previous[i] > 0); + assert(next[previous[i] - 1] == i + 1); + } + } + else { + assert(entries[i] == null); + assert(next[i] == 0); + assert(previous[i] == 0); + } + } + } +} diff --git a/src/main/java/org/scijava/util/Prefs.java b/src/main/java/org/scijava/util/Prefs.java index 7dd12f503..5cc620a12 100644 --- a/src/main/java/org/scijava/util/Prefs.java +++ b/src/main/java/org/scijava/util/Prefs.java @@ -223,7 +223,7 @@ public static void remove(final Preferences preferences, final String key) { service().remove(preferences.absolutePath(), key); } - /** Puts a list into the preferences. */ + /** Puts a map into the preferences. */ public static void putMap(final Map map, final String key) { service().putMap(map, key); } @@ -234,7 +234,7 @@ public static void putMap(final Preferences preferences, service().putMap(preferences.absolutePath(), map, key); } - /** Puts a list into the preferences. */ + /** Puts a map into the preferences. */ public static void putMap(final Preferences preferences, final Map map) { diff --git a/src/test/java/org/scijava/options/OptionsTest.java b/src/test/java/org/scijava/options/OptionsTest.java index 6681ac4d3..d70feaac3 100644 --- a/src/test/java/org/scijava/options/OptionsTest.java +++ b/src/test/java/org/scijava/options/OptionsTest.java @@ -94,6 +94,7 @@ public void testPersistence() { final FooOptions fooOptions = optionsService.getOptions(FooOptions.class); // bar should initially be 0 + fooOptions.setBar(0); assertEquals(0, fooOptions.getBar()); // verify that we can set bar to a desired value at all diff --git a/src/test/java/org/scijava/script/ScriptEngineTest.java b/src/test/java/org/scijava/script/ScriptEngineTest.java index 081def361..3c75f1297 100644 --- a/src/test/java/org/scijava/script/ScriptEngineTest.java +++ b/src/test/java/org/scijava/script/ScriptEngineTest.java @@ -138,5 +138,7 @@ public Object eval(Reader reader) throws ScriptException { } } - private static class Rot13Bindings extends HashMap implements Bindings { } + private static class Rot13Bindings extends HashMap implements Bindings { + private static final long serialVersionUID = 1L; + } } diff --git a/src/test/java/org/scijava/util/LastRecentlyUsedTest.java b/src/test/java/org/scijava/util/LastRecentlyUsedTest.java new file mode 100644 index 000000000..50dc5980a --- /dev/null +++ b/src/test/java/org/scijava/util/LastRecentlyUsedTest.java @@ -0,0 +1,104 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2014 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.Iterator; + +import org.junit.Test; + +/** + * Tests the {@link LastRecentlyUsed} data structure. + * + * @author Johannes Schindelin + */ +public class LastRecentlyUsedTest { + + @Test + public void test() { + int count = 3; + final LastRecentlyUsed lru = new LastRecentlyUsed(count); + + for (int i = 1; i <= count; i++) { + lru.add("" + i); + } + + int position = -1; + for (int i = 1; i <= count; i++) { + position = lru.next(position); + assertEquals("" + i, lru.get(position)); + } + position = lru.next(position); + assertEquals(-1, position); + + for (int i = count; i >= 1; i--) { + position = lru.previous(position); + assertEquals("" + i, lru.get(position)); + } + position = lru.previous(position); + assertEquals(-1, position); + } + + @Test + public void testRemove() { + final LastRecentlyUsed lru = new LastRecentlyUsed(3); + lru.add("a"); + lru.add("b"); + lru.add("c"); + + lru.remove("b"); + + Iterator iter = lru.iterator(); + assertEquals("c", iter.next()); + assertEquals("a", iter.next()); + assertFalse(iter.hasNext()); + + lru.remove("a"); + + iter = lru.iterator(); + assertEquals("c", iter.next()); + assertFalse(iter.hasNext()); + + lru.remove("a"); + + iter = lru.iterator(); + assertEquals("c", iter.next()); + assertFalse(iter.hasNext()); + + lru.remove("c"); + + iter = lru.iterator(); + assertFalse(iter.hasNext()); + } +}