From f5230f6fce88c268072fb4a55d33544cbb12d011 Mon Sep 17 00:00:00 2001 From: hinerm Date: Tue, 10 Sep 2024 15:07:30 -0500 Subject: [PATCH] De-duplicate widget inputs by String comparison When populating potential items for an input widget, we now prefer existing objects of a given input type to potentially convertiable types. Further, for convertibles we now avoid considering them if they share a toString with any other potential input. Candidates are prioritized in order returned by ConvertService.getCompatibleInputs. This mitigates the potential for duplicate entries in input harvesters when multiple input instances are convertible to the same effective output instance. --- .../widget/AbstractInputHarvester.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/scijava/widget/AbstractInputHarvester.java b/src/main/java/org/scijava/widget/AbstractInputHarvester.java index 2c259c400..d1515bd17 100644 --- a/src/main/java/org/scijava/widget/AbstractInputHarvester.java +++ b/src/main/java/org/scijava/widget/AbstractInputHarvester.java @@ -30,9 +30,10 @@ package org.scijava.widget; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import org.scijava.AbstractContextual; import org.scijava.convert.ConvertService; @@ -129,9 +130,24 @@ private WidgetModel addInput(final InputPanel inputPanel, /** Asks the object service and convert service for valid choices */ private List getObjects(final Class type) { - Set compatibleInputs = - new HashSet<>(convertService.getCompatibleInputs(type)); - compatibleInputs.addAll(objectService.getObjects(type)); - return new ArrayList<>(compatibleInputs); + // Start with the known, unconverted objects of the desired type + List objects = new ArrayList<>(objectService.getObjects(type)); + + // Get all the known objects that can be converted to the destination type + Collection compatibleInputs = convertService.getCompatibleInputs(type); + + // HACK: Add each convertible object that doesn't share a name with any other object + // Our goal here is to de-duplicate by avoiding similar inputs that could be converted + // to the same effective output (e.g. an ImageDisplay and a Dataset that map to the same + // ImgPlus) + Set knownNames = objects.stream().map(Object::toString).collect(Collectors.toSet()); + for (Object o : compatibleInputs) { + final String s = o.toString(); + if (!knownNames.contains(s)) { + objects.add(o); + knownNames.add(s); + } + } + return objects; } }