diff --git a/src/main/java/org/scijava/ItemPersistence.java b/src/main/java/org/scijava/ItemPersistence.java new file mode 100644 index 000000000..1ffc497a4 --- /dev/null +++ b/src/main/java/org/scijava/ItemPersistence.java @@ -0,0 +1,60 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2016 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; + +import org.scijava.plugin.Parameter; + +/** + * Defines the persistence setting of a parameter. + * + * @author Stefan Helfrich + */ +public enum ItemPersistence { + + /** + * Item is persisted in any case. + */ + YES, + + /** + * Item is never persisted. + */ + NO, + + /** + * Item is persisted unless an additional {@link Parameter#initializer() + * initializer()} method is defined or the value to persist is the defined + * default value. + */ + DEFAULT + +} diff --git a/src/main/java/org/scijava/command/CommandModuleItem.java b/src/main/java/org/scijava/command/CommandModuleItem.java index 9752fad33..02a44235c 100644 --- a/src/main/java/org/scijava/command/CommandModuleItem.java +++ b/src/main/java/org/scijava/command/CommandModuleItem.java @@ -37,6 +37,7 @@ import java.util.List; import org.scijava.ItemIO; +import org.scijava.ItemPersistence; import org.scijava.ItemVisibility; import org.scijava.Optional; import org.scijava.module.AbstractModuleItem; @@ -109,7 +110,7 @@ public boolean isRequired() { } @Override - public boolean isPersisted() { + public ItemPersistence getPersistence() { return getParameter().persist(); } diff --git a/src/main/java/org/scijava/module/AbstractModuleItem.java b/src/main/java/org/scijava/module/AbstractModuleItem.java index c6d84537d..301d00b19 100644 --- a/src/main/java/org/scijava/module/AbstractModuleItem.java +++ b/src/main/java/org/scijava/module/AbstractModuleItem.java @@ -37,6 +37,7 @@ import org.scijava.AbstractBasicDetails; import org.scijava.ItemIO; +import org.scijava.ItemPersistence; import org.scijava.ItemVisibility; import org.scijava.util.ClassUtils; import org.scijava.util.ConversionUtils; @@ -71,7 +72,7 @@ public String toString() { sm.append("description", getDescription()); sm.append("visibility", getVisibility(), ItemVisibility.NORMAL); sm.append("required", isRequired()); - sm.append("persisted", isPersisted()); + sm.append("persisted", getPersistence()); sm.append("persistKey", getPersistKey()); sm.append("callback", getCallback()); sm.append("widgetStyle", getWidgetStyle()); @@ -131,8 +132,8 @@ public boolean isRequired() { } @Override - public boolean isPersisted() { - return true; + public ItemPersistence getPersistence() { + return ItemPersistence.DEFAULT; } @Override @@ -149,7 +150,7 @@ public String getPersistKey() { @Deprecated public T loadValue() { // if there is nothing to load from persistence return nothing - if (!isPersisted()) return null; + if (getPersistence() == ItemPersistence.NO) return null; final String sValue; final String persistKey = getPersistKey(); @@ -169,7 +170,7 @@ public T loadValue() { @Override @Deprecated public void saveValue(final T value) { - if (!isPersisted()) return; + if (getPersistence() == ItemPersistence.NO) return; final String sValue = value == null ? "" : value.toString(); diff --git a/src/main/java/org/scijava/module/DefaultModuleService.java b/src/main/java/org/scijava/module/DefaultModuleService.java index 0d1e26106..abe1fb7f7 100644 --- a/src/main/java/org/scijava/module/DefaultModuleService.java +++ b/src/main/java/org/scijava/module/DefaultModuleService.java @@ -42,6 +42,7 @@ import java.util.concurrent.Future; import org.scijava.Identifiable; +import org.scijava.ItemPersistence; import org.scijava.MenuPath; import org.scijava.Priority; import org.scijava.convert.ConvertService; @@ -289,9 +290,16 @@ public ModuleItem<?> getSingleOutput(Module module, Collection<Class<?>> types) @Override public <T> void save(final ModuleItem<T> item, final T value) { - if (!item.isPersisted()) return; + ItemPersistence persistence = item.getPersistence(); - if (MiscUtils.equal(item.getDefaultValue(), value)) { + if (persistence == ItemPersistence.NO) return; + + // NB: Do not persist values which are computed via an initializer. + if (item.getInitializer() != null && !item.getInitializer().isEmpty() && + persistence == ItemPersistence.DEFAULT) return; + + if (MiscUtils.equal(item.getDefaultValue(), value) && + persistence == ItemPersistence.DEFAULT) { // NB: Do not persist the value if it is the default. // This is nice if the default value might change later, // such as when iteratively developing a script. @@ -315,7 +323,7 @@ public <T> void save(final ModuleItem<T> item, final T value) { @Override public <T> T load(final ModuleItem<T> item) { // if there is nothing to load from persistence return nothing - if (!item.isPersisted()) return null; + if (item.getPersistence() == ItemPersistence.NO) return null; final String sValue; final String persistKey = item.getPersistKey(); diff --git a/src/main/java/org/scijava/module/DefaultMutableModuleItem.java b/src/main/java/org/scijava/module/DefaultMutableModuleItem.java index 4d95ed91e..4d0c64bbc 100644 --- a/src/main/java/org/scijava/module/DefaultMutableModuleItem.java +++ b/src/main/java/org/scijava/module/DefaultMutableModuleItem.java @@ -37,6 +37,7 @@ import java.util.List; import org.scijava.ItemIO; +import org.scijava.ItemPersistence; import org.scijava.ItemVisibility; /** @@ -54,7 +55,7 @@ public class DefaultMutableModuleItem<T> extends AbstractModuleItem<T> private ItemIO ioType; private ItemVisibility visibility; private boolean required; - private boolean persisted; + private ItemPersistence persistence; private String persistKey; private String initializer; private String callback; @@ -87,7 +88,7 @@ public DefaultMutableModuleItem(final ModuleInfo info, final String name, ioType = super.getIOType(); visibility = super.getVisibility(); required = super.isRequired(); - persisted = super.isPersisted(); + persistence = super.getPersistence(); persistKey = super.getPersistKey(); initializer = super.getInitializer(); callback = super.getCallback(); @@ -113,7 +114,7 @@ public DefaultMutableModuleItem(final ModuleInfo info, ioType = item.getIOType(); visibility = item.getVisibility(); required = item.isRequired(); - persisted = item.isPersisted(); + persistence = item.getPersistence(); persistKey = item.getPersistKey(); initializer = item.getInitializer(); callback = item.getCallback(); @@ -148,8 +149,8 @@ public void setRequired(final boolean required) { } @Override - public void setPersisted(final boolean persisted) { - this.persisted = persisted; + public void setPersistence(final ItemPersistence persistence) { + this.persistence = persistence; } @Override @@ -241,8 +242,8 @@ public boolean isRequired() { } @Override - public boolean isPersisted() { - return persisted; + public ItemPersistence getPersistence() { + return persistence; } @Override diff --git a/src/main/java/org/scijava/module/ModuleItem.java b/src/main/java/org/scijava/module/ModuleItem.java index 087a03c8a..dec5504aa 100644 --- a/src/main/java/org/scijava/module/ModuleItem.java +++ b/src/main/java/org/scijava/module/ModuleItem.java @@ -37,6 +37,7 @@ import org.scijava.BasicDetails; import org.scijava.ItemIO; +import org.scijava.ItemPersistence; import org.scijava.ItemVisibility; /** @@ -84,7 +85,7 @@ public interface ModuleItem<T> extends BasicDetails { boolean isRequired(); /** Gets whether to remember the most recent value of the parameter. */ - boolean isPersisted(); + ItemPersistence getPersistence(); /** Gets the key to use for saving the value persistently. */ String getPersistKey(); diff --git a/src/main/java/org/scijava/module/MutableModuleItem.java b/src/main/java/org/scijava/module/MutableModuleItem.java index 3fd6df99d..5206f04dd 100644 --- a/src/main/java/org/scijava/module/MutableModuleItem.java +++ b/src/main/java/org/scijava/module/MutableModuleItem.java @@ -34,6 +34,7 @@ import java.util.List; import org.scijava.ItemIO; +import org.scijava.ItemPersistence; import org.scijava.ItemVisibility; /** @@ -50,7 +51,7 @@ public interface MutableModuleItem<T> extends ModuleItem<T> { void setRequired(boolean required); - void setPersisted(boolean persisted); + void setPersistence(ItemPersistence persistence); void setPersistKey(String persistKey); diff --git a/src/main/java/org/scijava/plugin/Parameter.java b/src/main/java/org/scijava/plugin/Parameter.java index 84e8eddbf..8561a8933 100644 --- a/src/main/java/org/scijava/plugin/Parameter.java +++ b/src/main/java/org/scijava/plugin/Parameter.java @@ -37,6 +37,7 @@ import java.lang.annotation.Target; import org.scijava.ItemIO; +import org.scijava.ItemPersistence; import org.scijava.ItemVisibility; /** @@ -111,12 +112,17 @@ boolean required() default true; /** Defines whether to remember the most recent value of the parameter. */ - boolean persist() default true; + ItemPersistence persist() default ItemPersistence.DEFAULT; /** Defines a key to use for saving the value persistently. */ String persistKey() default ""; - /** Defines a function that is called to initialize the parameter. */ + /** + * Defines a function that is called to initialize the parameter. If an + * initializer is defined, it takes precedence over {@link #persist()}, i.e. + * the parameter is not persisted even if {@link #persist()} returns + * {@link ItemPersistence#YES}. + */ String initializer() default ""; /** diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index bd17bfc34..cd961577b 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -49,6 +49,7 @@ import org.scijava.Context; import org.scijava.Contextual; import org.scijava.ItemIO; +import org.scijava.ItemPersistence; import org.scijava.ItemVisibility; import org.scijava.NullContextException; import org.scijava.command.Command; @@ -427,7 +428,7 @@ private <T> void assignAttribute(final DefaultMutableModuleItem<T> item, else if (is(k, "max")) item.setMaximumValue(as(v, item.getType())); else if (is(k, "min")) item.setMinimumValue(as(v, item.getType())); else if (is(k, "name")) item.setName(as(v, String.class)); - else if (is(k, "persist")) item.setPersisted(as(v, boolean.class)); + else if (is(k, "persist")) item.setPersistence(as(v, ItemPersistence.class)); else if (is(k, "persistKey")) item.setPersistKey(as(v, String.class)); else if (is(k, "required")) item.setRequired(as(v, boolean.class)); else if (is(k, "softMax")) item.setSoftMaximum(as(v, item.getType())); diff --git a/src/test/java/org/scijava/module/ModuleServiceTest.java b/src/test/java/org/scijava/module/ModuleServiceTest.java index a1276f915..c165e22dc 100644 --- a/src/test/java/org/scijava/module/ModuleServiceTest.java +++ b/src/test/java/org/scijava/module/ModuleServiceTest.java @@ -31,11 +31,13 @@ package org.scijava.module; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import org.junit.Test; import org.scijava.Context; +import org.scijava.prefs.PrefService; /** * Tests {@link ModuleService}. @@ -74,6 +76,38 @@ public void testGetSingleInput() throws ModuleException { assertSame(info.getInput("double2"), singleDouble); } + @SuppressWarnings("unchecked") + @Test + public void testPersistingWithInitialize() { + final Context context = new Context(ModuleService.class, PrefService.class); + final ModuleService moduleService = context.getService(ModuleService.class); + + // reset the PrefService entries for the test + final PrefService prefService = context.getService(PrefService.class); + prefService.clear("persistInteger"); + prefService.clear("persistDouble"); + + final ModuleInfo info = new FooModuleInfo(); + final ModuleItem<Double> doubleItem = (ModuleItem<Double>) info.getInput( + "double1"); + final ModuleItem<Integer> integerItem = (ModuleItem<Integer>) info.getInput( + "integer1"); + + // save ModuleItem for which getInitializer() returns "testInitializer" + moduleService.save(doubleItem, 5d); + + // verify that the item is not persisted + String persistKey = doubleItem.getPersistKey(); + assertNull(prefService.get(persistKey)); + + // save ModuleItem for which getInitializer() returns null + moduleService.save(integerItem, 5); + + // verify that the item is persisted + persistKey = integerItem.getPersistKey(); + assertEquals(5, prefService.getInt(persistKey, 0)); + } + /** A sample module for testing the module service. */ public static class FooModule extends AbstractModule { @@ -115,16 +149,16 @@ public Module createModule() throws ModuleException { @Override protected void parseParameters() { - addInput("string", String.class, true); - addInput("float", Float.class, false); - addInput("integer1", Integer.class, true); - addInput("integer2", Integer.class, true); - addInput("double1", Double.class, false); - addInput("double2", Double.class, true); + addInput("string", String.class, true, null, null); + addInput("float", Float.class, false, null, null); + addInput("integer1", Integer.class, true, "persistInteger", null); + addInput("integer2", Integer.class, true, null, null); + addInput("double1", Double.class, false, "persistDouble", "testInitializer"); + addInput("double2", Double.class, true, null, null); } private <T> void addInput(final String name, final Class<T> type, - final boolean autoFill) + final boolean autoFill, final String persistKey, final String initializer) { registerInput(new AbstractModuleItem<T>(this) { @@ -143,6 +177,16 @@ public boolean isAutoFill() { return autoFill; } + @Override + public String getPersistKey() { + return persistKey; + } + + @Override + public String getInitializer() { + return initializer; + } + }); } diff --git a/src/test/java/org/scijava/script/ScriptInfoTest.java b/src/test/java/org/scijava/script/ScriptInfoTest.java index 53f9ed659..cca129bff 100644 --- a/src/test/java/org/scijava/script/ScriptInfoTest.java +++ b/src/test/java/org/scijava/script/ScriptInfoTest.java @@ -56,6 +56,7 @@ import org.junit.Test; import org.scijava.Context; import org.scijava.ItemIO; +import org.scijava.ItemPersistence; import org.scijava.log.LogService; import org.scijava.module.ModuleItem; import org.scijava.plugin.Plugin; @@ -151,27 +152,32 @@ public void testParameters() { final List<?> noChoices = Collections.emptyList(); final ModuleItem<?> log = info.getInput("log"); - assertItem("log", LogService.class, null, ItemIO.INPUT, false, true, null, - null, null, null, null, null, null, null, noChoices, log); + assertItem("log", LogService.class, null, ItemIO.INPUT, false, + ItemPersistence.DEFAULT, null, null, null, null, null, null, null, null, + noChoices, log); final ModuleItem<?> sliderValue = info.getInput("sliderValue"); assertItem("sliderValue", int.class, "Slider Value", ItemIO.INPUT, true, - true, null, "slider", 11, null, null, 5, 15, 3.0, noChoices, sliderValue); + ItemPersistence.DEFAULT, null, "slider", 11, null, null, 5, 15, 3.0, + noChoices, sliderValue); final ModuleItem<?> animal = info.getInput("animal"); final List<String> animalChoices = // Arrays.asList("quick brown fox", "lazy dog"); - assertItem("animal", String.class, null, ItemIO.INPUT, true, false, - null, null, null, null, null, null, null, null, animalChoices, animal); + assertItem("animal", String.class, null, ItemIO.INPUT, true, + ItemPersistence.DEFAULT, null, null, null, null, null, null, null, null, + animalChoices, animal); assertEquals(animal.get("family"), "Carnivora"); // test custom attribute final ModuleItem<?> buffer = info.getOutput("buffer"); - assertItem("buffer", StringBuilder.class, null, ItemIO.BOTH, true, true, - null, null, null, null, null, null, null, null, noChoices, buffer); + assertItem("buffer", StringBuilder.class, null, ItemIO.BOTH, true, + ItemPersistence.DEFAULT, null, null, null, null, null, null, null, null, + noChoices, buffer); final ModuleItem<?> result = info.getOutput("result"); - assertItem("result", Object.class, null, ItemIO.OUTPUT, true, true, null, - null, null, null, null, null, null, null, noChoices, result); + assertItem("result", Object.class, null, ItemIO.OUTPUT, true, + ItemPersistence.DEFAULT, null, null, null, null, null, null, null, null, + noChoices, result); int inputCount = 0; final ModuleItem<?>[] inputs = { log, sliderValue, animal, buffer }; @@ -188,7 +194,7 @@ public void testParameters() { private void assertItem(final String name, final Class<?> type, final String label, final ItemIO ioType, final boolean required, - final boolean persist, final String persistKey, final String style, + final ItemPersistence persist, final String persistKey, final String style, final Object value, final Object min, final Object max, final Object softMin, final Object softMax, final Number stepSize, final List<?> choices, final ModuleItem<?> item) @@ -198,7 +204,7 @@ private void assertItem(final String name, final Class<?> type, assertEquals(label, item.getLabel()); assertSame(ioType, item.getIOType()); assertEquals(required, item.isRequired()); - assertEquals(persist, item.isPersisted()); + assertEquals(persist, item.getPersistence()); assertEquals(persistKey, item.getPersistKey()); assertEquals(style, item.getWidgetStyle()); assertEquals(value, item.getDefaultValue());