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> types) @Override public void save(final ModuleItem 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 void save(final ModuleItem item, final T value) { @Override public T load(final ModuleItem 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 extends AbstractModuleItem 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 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 extends ModuleItem { 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 void assignAttribute(final DefaultMutableModuleItem 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 doubleItem = (ModuleItem) info.getInput( + "double1"); + final ModuleItem integerItem = (ModuleItem) 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 void addInput(final String name, final Class type, - final boolean autoFill) + final boolean autoFill, final String persistKey, final String initializer) { registerInput(new AbstractModuleItem(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 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());