From a5d0fc09550e275646361ec860129aa99af04318 Mon Sep 17 00:00:00 2001 From: Jan Eglinger Date: Tue, 17 Dec 2019 18:05:25 +0100 Subject: [PATCH 1/4] Add named object index and improve ObjectService --- .../scijava/object/DefaultObjectService.java | 25 ++++++- .../org/scijava/object/NamedObjectIndex.java | 29 +++++++ .../org/scijava/object/ObjectService.java | 13 ++++ .../scijava/widget/DefaultWidgetModel.java | 6 +- .../scijava/object/NamedObjectIndexTest.java | 43 +++++++++++ .../org/scijava/object/ObjectServiceTest.java | 75 +++++++++++++++++++ 6 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/scijava/object/NamedObjectIndex.java create mode 100644 src/test/java/org/scijava/object/NamedObjectIndexTest.java create mode 100644 src/test/java/org/scijava/object/ObjectServiceTest.java diff --git a/src/main/java/org/scijava/object/DefaultObjectService.java b/src/main/java/org/scijava/object/DefaultObjectService.java index 46f162ab0..4c1855214 100644 --- a/src/main/java/org/scijava/object/DefaultObjectService.java +++ b/src/main/java/org/scijava/object/DefaultObjectService.java @@ -34,6 +34,7 @@ import java.util.List; +import org.scijava.Named; import org.scijava.event.EventHandler; import org.scijava.event.EventService; import org.scijava.object.event.ObjectCreatedEvent; @@ -68,7 +69,7 @@ public final class DefaultObjectService extends AbstractService implements private EventService eventService; /** Index of registered objects. */ - private ObjectIndex objectIndex; + private NamedObjectIndex objectIndex; // -- ObjectService methods -- @@ -92,7 +93,12 @@ public List getObjects(final Class type) { @Override public void addObject(final Object obj) { - objectIndex.add(obj); + addObject(obj, null); + } + + @Override + public void addObject(Object obj, String name) { + objectIndex.add(obj, name); eventService.publish(new ObjectsAddedEvent(obj)); } @@ -102,11 +108,23 @@ public void removeObject(final Object obj) { eventService.publish(new ObjectsRemovedEvent(obj)); } + @Override + public String getName(Object obj) { + String name = objectIndex.getName(obj); + if (name != null) { + return name; + } + if (obj instanceof Named) { + return ((Named) obj).getName(); + } + return obj.toString(); + } + // -- Service methods -- @Override public void initialize() { - objectIndex = new ObjectIndex<>(Object.class); + objectIndex = new NamedObjectIndex<>(Object.class); } // -- Event handlers -- @@ -120,5 +138,4 @@ protected void onEvent(final ObjectCreatedEvent event) { protected void onEvent(final ObjectDeletedEvent event) { removeObject(event.getObject()); } - } diff --git a/src/main/java/org/scijava/object/NamedObjectIndex.java b/src/main/java/org/scijava/object/NamedObjectIndex.java new file mode 100644 index 000000000..3db8f9d51 --- /dev/null +++ b/src/main/java/org/scijava/object/NamedObjectIndex.java @@ -0,0 +1,29 @@ +package org.scijava.object; + +import java.util.WeakHashMap; + +public class NamedObjectIndex extends ObjectIndex { + + private WeakHashMap nameMap; + + public NamedObjectIndex(final Class baseClass) { + super(baseClass); + nameMap = new WeakHashMap<>(); + } + + public boolean add(E object, String name) { + if (name != null) + nameMap.put(object, name); + return add(object); + } + + public boolean add(E object, Class type, String name, boolean batch) { + if (name != null) + nameMap.put(object, name); + return add(object, type, batch); + } + + public String getName(E object) { + return nameMap.get(object); + } +} diff --git a/src/main/java/org/scijava/object/ObjectService.java b/src/main/java/org/scijava/object/ObjectService.java index e24922b21..584c4b335 100644 --- a/src/main/java/org/scijava/object/ObjectService.java +++ b/src/main/java/org/scijava/object/ObjectService.java @@ -34,6 +34,7 @@ import java.util.List; +import org.scijava.Named; import org.scijava.event.EventService; import org.scijava.service.SciJavaService; @@ -54,9 +55,21 @@ default EventService eventService() { /** Gets a list of all registered objects compatible with the given type. */ List getObjects(Class type); + /** + * Gets the name belonging to a given object. + * + * If no explicit name was provided at registration time, the name will be + * derived from {@link Named#getName()} if the object implements {@link Named}, + * or from the {@link Object#toString()} otherwise + **/ + String getName(Object obj); + /** Registers an object with the object service. */ void addObject(Object obj); + /** Registers a named object with the object service. */ + void addObject(Object obj, String name); + /** Deregisters an object with the object service. */ void removeObject(Object obj); diff --git a/src/main/java/org/scijava/widget/DefaultWidgetModel.java b/src/main/java/org/scijava/widget/DefaultWidgetModel.java index aaece9fcc..46e960fd8 100644 --- a/src/main/java/org/scijava/widget/DefaultWidgetModel.java +++ b/src/main/java/org/scijava/widget/DefaultWidgetModel.java @@ -47,6 +47,7 @@ import org.scijava.module.Module; import org.scijava.module.ModuleItem; import org.scijava.module.ModuleService; +import org.scijava.object.ObjectService; import org.scijava.plugin.Parameter; import org.scijava.thread.ThreadService; import org.scijava.util.NumberUtils; @@ -74,6 +75,9 @@ public class DefaultWidgetModel extends AbstractContextual implements WidgetMode @Parameter private ModuleService moduleService; + @Parameter + private ObjectService objectService; + @Parameter(required = false) private LogService log; @@ -227,7 +231,7 @@ public String[] getChoices() { final List choicesList = item.getChoices(); final String[] choices = new String[choicesList.size()]; for (int i = 0; i < choices.length; i++) { - choices[i] = choicesList.get(i).toString(); + choices[i] = objectService.getName(choicesList.get(i)); } return choices; } diff --git a/src/test/java/org/scijava/object/NamedObjectIndexTest.java b/src/test/java/org/scijava/object/NamedObjectIndexTest.java new file mode 100644 index 000000000..3e4783247 --- /dev/null +++ b/src/test/java/org/scijava/object/NamedObjectIndexTest.java @@ -0,0 +1,43 @@ +package org.scijava.object; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Test; + +public class NamedObjectIndexTest { + + @Test + public void testNamedObjects() { + NamedObjectIndex index = new NamedObjectIndex<>(String.class); + String obj1 = "obj1"; + String name1 = "name1"; + String obj2 = "obj1"; + String name2 = "name1"; + assertTrue(index.add(obj1, name1)); + assertTrue(index.add(obj2, String.class, name2, false)); + assertTrue(index.contains(obj1)); + assertTrue(index.contains(obj2)); + assertEquals(name1, index.getName(obj1)); + assertEquals(name2, index.getName(obj2)); + assertTrue(index.remove(obj1)); + assertTrue(index.remove(obj2)); + assertFalse(index.contains(obj1)); + assertFalse(index.contains(obj2)); + } + + @Test + public void testNullNames() { + NamedObjectIndex index = new NamedObjectIndex<>(String.class); + String obj1 = "object1"; + String name1 = null; + String obj2 = "object2"; + String name2 = ""; + assertTrue(index.add(obj1, name1)); + assertTrue(index.add(obj2, name2)); + assertEquals(name1, index.getName(obj1)); + assertEquals(name2, index.getName(obj2)); + } +} diff --git a/src/test/java/org/scijava/object/ObjectServiceTest.java b/src/test/java/org/scijava/object/ObjectServiceTest.java new file mode 100644 index 000000000..4d58a97fe --- /dev/null +++ b/src/test/java/org/scijava/object/ObjectServiceTest.java @@ -0,0 +1,75 @@ +package org.scijava.object; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.scijava.Context; +import org.scijava.plugin.PluginInfo; +import org.scijava.plugin.SciJavaPlugin; + +public class ObjectServiceTest { + + private Context context; + private ObjectService objectService; + + @Before + public void setUp() { + context = new Context(ObjectService.class); + objectService = context.getService(ObjectService.class); + } + + @After + public void tearDown() { + context.dispose(); + } + + @Test + public void testAddRemoveObjects() { + Object obj1 = new Object(); + String name1 = "Object 1"; + Object obj2 = ""; + Object obj3 = new Double(0.3); + PluginInfo obj4 = PluginInfo.create(TestPlugin.class, SciJavaPlugin.class); + obj4.setName("TestPlugin name"); + + objectService.addObject(obj1, name1); + assertEquals("Name of object 1", name1, objectService.getName(obj1)); + objectService.addObject(obj2); + assertEquals("Name of object 2", obj2.toString(), objectService.getName(obj2)); + objectService.addObject(obj3, null); + assertEquals("Name of object 3", obj3.toString(), objectService.getName(obj3)); + objectService.addObject(obj4); + assertNotNull(objectService.getName(obj4)); + assertEquals("Name of object 4", obj4.getName(), objectService.getName(obj4)); + + assertTrue("Object 1 registered", objectService.getObjects(Object.class).contains(obj1)); + assertTrue("Object 2 registered", objectService.getObjects(Object.class).contains(obj2)); + assertTrue("Object 3 registered", objectService.getObjects(Object.class).contains(obj3)); + assertTrue("Object 4 registered", objectService.getObjects(Object.class).contains(obj4)); + + objectService.removeObject(obj1); + objectService.removeObject(obj2); + objectService.removeObject(obj3); + objectService.removeObject(obj4); + + assertFalse("Object 1 removed", objectService.getObjects(Object.class).contains(obj1)); + assertFalse("Object 2 removed", objectService.getObjects(Object.class).contains(obj2)); + assertFalse("Object 3 removed", objectService.getObjects(Object.class).contains(obj3)); + assertFalse("Object 4 removed", objectService.getObjects(Object.class).contains(obj4)); + } + + @Test + public void testNamedObjectIndex() { + ObjectIndex index = objectService.getIndex(); + assertTrue(index instanceof NamedObjectIndex); + } + + private class TestPlugin implements SciJavaPlugin { + + } +} From 765d6ef1506a3daf9af9c7bd18824b2946015beb Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 13 Jan 2020 13:57:27 -0600 Subject: [PATCH 2/4] ObjectService: ensure getName() returns non-null --- .../org/scijava/object/DefaultObjectService.java | 14 ++++++++------ .../java/org/scijava/object/ObjectService.java | 10 ++++++---- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/scijava/object/DefaultObjectService.java b/src/main/java/org/scijava/object/DefaultObjectService.java index 4c1855214..c2cffb8bc 100644 --- a/src/main/java/org/scijava/object/DefaultObjectService.java +++ b/src/main/java/org/scijava/object/DefaultObjectService.java @@ -110,14 +110,16 @@ public void removeObject(final Object obj) { @Override public String getName(Object obj) { - String name = objectIndex.getName(obj); - if (name != null) { - return name; - } + if (obj == null) throw new NullPointerException(); + final String name = objectIndex.getName(obj); + if (name != null) return name; if (obj instanceof Named) { - return ((Named) obj).getName(); + final String n = ((Named) obj).getName(); + if (n != null) return n; } - return obj.toString(); + final String s = obj.toString(); + if (s != null) return s; + return obj.getClass().getName() + "@" + Integer.toHexString(obj.hashCode()); } // -- Service methods -- diff --git a/src/main/java/org/scijava/object/ObjectService.java b/src/main/java/org/scijava/object/ObjectService.java index 584c4b335..286e1da68 100644 --- a/src/main/java/org/scijava/object/ObjectService.java +++ b/src/main/java/org/scijava/object/ObjectService.java @@ -57,11 +57,13 @@ default EventService eventService() { /** * Gets the name belonging to a given object. - * + *

* If no explicit name was provided at registration time, the name will be - * derived from {@link Named#getName()} if the object implements {@link Named}, - * or from the {@link Object#toString()} otherwise - **/ + * derived from {@link Named#getName()} if the object implements + * {@link Named}, or from the {@link Object#toString()} otherwise. It is + * guaranteed that this method will not return {@code null}. + *

+ */ String getName(Object obj); /** Registers an object with the object service. */ From ce965b93d729569c83f4f94d100c8f262b6c1730 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 13 Jan 2020 13:58:26 -0600 Subject: [PATCH 3/4] NamedObjectIndex: add missing javadoc --- src/main/java/org/scijava/object/NamedObjectIndex.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/scijava/object/NamedObjectIndex.java b/src/main/java/org/scijava/object/NamedObjectIndex.java index 3db8f9d51..2b314fa1d 100644 --- a/src/main/java/org/scijava/object/NamedObjectIndex.java +++ b/src/main/java/org/scijava/object/NamedObjectIndex.java @@ -2,6 +2,11 @@ import java.util.WeakHashMap; +/** + * An {@link ObjectIndex} where each object can have an associated name. + * + * @author Jan Eglinger + */ public class NamedObjectIndex extends ObjectIndex { private WeakHashMap nameMap; From d5dfb5d7c6e4425c2c0511c3b77cda37c2b01aca Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 13 Jan 2020 13:59:00 -0600 Subject: [PATCH 4/4] ObjectService: push default impls to interface --- .../scijava/object/DefaultObjectService.java | 46 +------------------ .../org/scijava/object/ObjectService.java | 40 +++++++++++++--- 2 files changed, 34 insertions(+), 52 deletions(-) diff --git a/src/main/java/org/scijava/object/DefaultObjectService.java b/src/main/java/org/scijava/object/DefaultObjectService.java index c2cffb8bc..2b150a997 100644 --- a/src/main/java/org/scijava/object/DefaultObjectService.java +++ b/src/main/java/org/scijava/object/DefaultObjectService.java @@ -32,15 +32,10 @@ package org.scijava.object; -import java.util.List; - -import org.scijava.Named; import org.scijava.event.EventHandler; import org.scijava.event.EventService; import org.scijava.object.event.ObjectCreatedEvent; import org.scijava.object.event.ObjectDeletedEvent; -import org.scijava.object.event.ObjectsAddedEvent; -import org.scijava.object.event.ObjectsRemovedEvent; import org.scijava.plugin.Parameter; import org.scijava.plugin.Plugin; import org.scijava.service.AbstractService; @@ -79,49 +74,10 @@ public EventService eventService() { } @Override - public ObjectIndex getIndex() { + public NamedObjectIndex getIndex() { return objectIndex; } - @Override - public List getObjects(final Class type) { - final List list = objectIndex.get(type); - @SuppressWarnings("unchecked") - final List result = (List) list; - return result; - } - - @Override - public void addObject(final Object obj) { - addObject(obj, null); - } - - @Override - public void addObject(Object obj, String name) { - objectIndex.add(obj, name); - eventService.publish(new ObjectsAddedEvent(obj)); - } - - @Override - public void removeObject(final Object obj) { - objectIndex.remove(obj); - eventService.publish(new ObjectsRemovedEvent(obj)); - } - - @Override - public String getName(Object obj) { - if (obj == null) throw new NullPointerException(); - final String name = objectIndex.getName(obj); - if (name != null) return name; - if (obj instanceof Named) { - final String n = ((Named) obj).getName(); - if (n != null) return n; - } - final String s = obj.toString(); - if (s != null) return s; - return obj.getClass().getName() + "@" + Integer.toHexString(obj.hashCode()); - } - // -- Service methods -- @Override diff --git a/src/main/java/org/scijava/object/ObjectService.java b/src/main/java/org/scijava/object/ObjectService.java index 286e1da68..24e71299e 100644 --- a/src/main/java/org/scijava/object/ObjectService.java +++ b/src/main/java/org/scijava/object/ObjectService.java @@ -36,6 +36,8 @@ import org.scijava.Named; import org.scijava.event.EventService; +import org.scijava.object.event.ObjectsAddedEvent; +import org.scijava.object.event.ObjectsRemovedEvent; import org.scijava.service.SciJavaService; /** @@ -50,10 +52,15 @@ default EventService eventService() { } /** Gets the index of available objects. */ - ObjectIndex getIndex(); + NamedObjectIndex getIndex(); /** Gets a list of all registered objects compatible with the given type. */ - List getObjects(Class type); + default List getObjects(final Class type) { + final List list = getIndex().get(type); + @SuppressWarnings("unchecked") + final List result = (List) list; + return result; + } /** * Gets the name belonging to a given object. @@ -63,17 +70,36 @@ default EventService eventService() { * {@link Named}, or from the {@link Object#toString()} otherwise. It is * guaranteed that this method will not return {@code null}. *

- */ - String getName(Object obj); + **/ + default String getName(final Object obj) { + if (obj == null) throw new NullPointerException(); + final String name = getIndex().getName(obj); + if (name != null) return name; + if (obj instanceof Named) { + final String n = ((Named) obj).getName(); + if (n != null) return n; + } + final String s = obj.toString(); + if (s != null) return s; + return obj.getClass().getName() + "@" + Integer.toHexString(obj.hashCode()); + } /** Registers an object with the object service. */ - void addObject(Object obj); + default void addObject(Object obj) { + addObject(obj, null); + } /** Registers a named object with the object service. */ - void addObject(Object obj, String name); + default void addObject(final Object obj, final String name) { + getIndex().add(obj, name); + eventService().publish(new ObjectsAddedEvent(obj)); + } /** Deregisters an object with the object service. */ - void removeObject(Object obj); + default void removeObject(final Object obj) { + getIndex().remove(obj); + eventService().publish(new ObjectsRemovedEvent(obj)); + } // -- Deprecated methods --