Skip to content

Commit 35cb358

Browse files
committed
Allow only one search action per name
This can remove confusion with "equivalent" search actions. With "equivalent" search actions, we mean actions of the same name. The actions that is kept is the action coming from the highest priority SearchActionFactory
1 parent 9f39739 commit 35cb358

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

src/main/java/org/scijava/search/SearchService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929

3030
package org.scijava.search;
3131

32+
import java.util.HashMap;
3233
import java.util.List;
34+
import java.util.Map;
3335
import java.util.stream.Collectors;
3436

3537
import org.scijava.plugin.SingletonService;
@@ -65,9 +67,13 @@ default SearchOperation search(final SearchListener... callbacks) {
6567
* @return A list of actions which could possibly be executed for the result.
6668
*/
6769
default List<SearchAction> actions(final SearchResult result) {
70+
// Create a map used to track whether a name has been seen
71+
final Map<Object, Boolean> seenLabels = new HashMap<>();
6872
return getInstances().stream() //
6973
.filter(factory -> factory.supports(result)) //
7074
.map(factory -> factory.create(result)) //
75+
// NB The following line skip actions with duplicate labels
76+
.filter(t -> seenLabels.putIfAbsent(t.toString(), Boolean.TRUE) == null) //
7177
.collect(Collectors.toList());
7278
}
7379

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
2+
package org.scijava.search.module;
3+
4+
import java.util.List;
5+
6+
import org.junit.Assert;
7+
import org.junit.Before;
8+
import org.junit.Test;
9+
import org.scijava.Context;
10+
import org.scijava.Priority;
11+
import org.scijava.plugin.Plugin;
12+
import org.scijava.search.DefaultSearchAction;
13+
import org.scijava.search.SearchAction;
14+
import org.scijava.search.SearchActionFactory;
15+
import org.scijava.search.SearchResult;
16+
import org.scijava.search.SearchService;
17+
import org.scijava.search.classes.ClassSearchResult;
18+
19+
/**
20+
* Tests that duplicate labels are removed with the {@link SearchService}
21+
*
22+
* @author Gabriel Selzer
23+
*/
24+
public class DuplicateLabelsTest {
25+
26+
/**
27+
* Used to allow the search actions to change state. Run actions can't return
28+
* anything, so we need another way to track that they run.
29+
*/
30+
public static Integer state = 0;
31+
32+
private SearchService searchService;
33+
34+
@Before
35+
public void init() {
36+
Context context = new Context();
37+
searchService = context.getService(SearchService.class);
38+
}
39+
40+
@Test
41+
public void testDuplicateLabelRemoval() {
42+
SearchResult dummyResult = new ClassSearchResult(this.getClass(), "");
43+
List<SearchAction> actions = searchService.actions(dummyResult);
44+
actions.removeIf(a -> !a.toString().equals("test"));
45+
Assert.assertEquals(1, actions.size());
46+
actions.get(0).run();
47+
assert DuplicateLabelsTest.state == 1;
48+
}
49+
50+
@Plugin(type = SearchActionFactory.class, priority = Priority.HIGH)
51+
public static class TestSearchActionFactoryHigh implements
52+
SearchActionFactory
53+
{
54+
55+
@Override
56+
public SearchAction create(SearchResult data) {
57+
return new DefaultSearchAction("test", //
58+
() -> DuplicateLabelsTest.state = 1 //
59+
);
60+
}
61+
}
62+
63+
@Plugin(type = SearchActionFactory.class, priority = Priority.LOW)
64+
public static class TestSearchActionFactoryLow implements
65+
SearchActionFactory
66+
{
67+
68+
@Override
69+
public SearchAction create(SearchResult data) {
70+
return new DefaultSearchAction("test", //
71+
() -> DuplicateLabelsTest.state = 2 //
72+
);
73+
}
74+
75+
}
76+
77+
}

0 commit comments

Comments
 (0)