Skip to content

Commit 618e250

Browse files
committed
Merge pull request #123 from scijava/convertable-inputs
Converters can now populate candidate input lists when their output types are requested.
2 parents dc87299 + 6593fcd commit 618e250

File tree

10 files changed

+239
-32
lines changed

10 files changed

+239
-32
lines changed

src/main/java/org/scijava/convert/AbstractConvertService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
* @author Mark Hiner
4444
*/
4545
public abstract class AbstractConvertService extends
46-
AbstractHandlerService<ConversionRequest, Converter> implements
46+
AbstractHandlerService<ConversionRequest, Converter<?, ?>> implements
4747
ConvertService
4848
{
4949

@@ -58,13 +58,13 @@ public Object convert(Object src, Type dest) {
5858
public <T> T convert(Object src, Class<T> dest) {
5959
// NB: repeated code with convert(ConversionRequest), because the
6060
// handler's convert method respects the T provided
61-
Converter handler = getHandler(src, dest);
61+
Converter<?, ?> handler = getHandler(src, dest);
6262
return handler == null ? null : handler.convert(src, dest);
6363
}
6464

6565
@Override
6666
public Object convert(ConversionRequest request) {
67-
Converter handler = getHandler(request);
67+
Converter<?, ?> handler = getHandler(request);
6868
return handler == null ? null : handler.convert(request);
6969
}
7070

src/main/java/org/scijava/convert/AbstractConverter.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,30 @@
3131

3232
package org.scijava.convert;
3333

34+
import java.util.Collection;
35+
3436
import org.scijava.plugin.AbstractHandlerPlugin;
3537

3638
/**
37-
* Abstract superclass for {@link Converter} plugins. Performs
38-
* appropriate dispatching of {@link #canConvert(ConversionRequest)} and
39+
* Abstract superclass for {@link Converter} plugins. Performs appropriate
40+
* dispatching of {@link #canConvert(ConversionRequest)} and
3941
* {@link #convert(ConversionRequest)} calls based on the actual state of the
4042
* given {@link ConversionRequest}.
4143
* <p>
4244
* Note that the {@link #supports(ConversionRequest)} method is overridden as
4345
* well, to delegate to the appropriate {@link #canConvert}.
4446
* </p>
47+
* <p>
48+
* NB: by default, the {@link #populateInputCandidates(Collection)} method has a
49+
* dummy implementation. Effectively, this is opt-in behavior. If a converter
50+
* implementation would like to suggest candidates for conversion, this method
51+
* can be overridden.
52+
* </p>
4553
*
4654
* @author Mark Hiner
4755
*/
48-
public abstract class AbstractConverter extends
49-
AbstractHandlerPlugin<ConversionRequest> implements Converter
56+
public abstract class AbstractConverter<I, O> extends
57+
AbstractHandlerPlugin<ConversionRequest> implements Converter<I, O>
5058
{
5159

5260
// -- ConversionHandler methods --
@@ -74,6 +82,11 @@ public Object convert(final ConversionRequest request) {
7482
return null;
7583
}
7684

85+
@Override
86+
public void populateInputCandidates(final Collection<Object> objects) {
87+
// No-op
88+
}
89+
7790
// -- Typed methods --
7891

7992
@Override

src/main/java/org/scijava/convert/ConvertService.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
package org.scijava.convert;
3333

3434
import java.lang.reflect.Type;
35+
import java.util.Collection;
3536

3637
import org.scijava.plugin.HandlerService;
3738

@@ -45,7 +46,7 @@
4546
* @author Mark Hiner
4647
*/
4748
public interface ConvertService extends
48-
HandlerService<ConversionRequest, Converter>
49+
HandlerService<ConversionRequest, Converter<?, ?>>
4950
{
5051
/**
5152
* @see Converter#convert(Object, Type)
@@ -65,22 +66,22 @@ public interface ConvertService extends
6566
/**
6667
* @see #getHandler(ConversionRequest)
6768
*/
68-
Converter getHandler(Object src, Class<?> dest);
69+
Converter<?, ?> getHandler(Object src, Class<?> dest);
6970

7071
/**
7172
* @see #getHandler(ConversionRequest)
7273
*/
73-
Converter getHandler(Class<?> src, Class<?> dest);
74+
Converter<?, ?> getHandler(Class<?> src, Class<?> dest);
7475

7576
/**
7677
* @see #getHandler(ConversionRequest)
7778
*/
78-
Converter getHandler(Object src, Type dest);
79+
Converter<?, ?> getHandler(Object src, Type dest);
7980

8081
/**
8182
* @see #getHandler(ConversionRequest)
8283
*/
83-
Converter getHandler(Class<?> src, Type dest);
84+
Converter<?, ?> getHandler(Class<?> src, Type dest);
8485

8586
/**
8687
* @see #supports(ConversionRequest)
@@ -101,4 +102,6 @@ public interface ConvertService extends
101102
* @see #supports(ConversionRequest)
102103
*/
103104
boolean supports(Class<?> src, Type dest);
105+
106+
Collection<Object> getCompatibleInputs(Class<?> dest);
104107
}

src/main/java/org/scijava/convert/Converter.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.List;
3737
import java.util.Set;
3838

39+
import org.scijava.object.ObjectService;
3940
import org.scijava.plugin.HandlerPlugin;
4041
import org.scijava.plugin.Plugin;
4142

@@ -46,7 +47,8 @@
4647
* @see ConversionRequest
4748
* @author Mark Hiner
4849
*/
49-
public interface Converter extends HandlerPlugin<ConversionRequest> {
50+
public interface Converter<I, O> extends HandlerPlugin<ConversionRequest>
51+
{
5052

5153
/**
5254
* Checks whether a given {@ConversionRequest} can be
@@ -155,4 +157,36 @@ public interface Converter extends HandlerPlugin<ConversionRequest> {
155157
* @return The conversion output
156158
*/
157159
Object convert(ConversionRequest request);
160+
161+
/**
162+
* Populates the given collection with objects which are known to exist, and
163+
* which are usable as inputs for this converter.
164+
* <p>
165+
* That is: each such object added to the collection would return {@code true}
166+
* if queried with {@code converter.canConvert(object)}, and hence would
167+
* produce an output of type {@link #getOutputType()} if passed to
168+
* {@code converter.convert(object)}.
169+
* </p>
170+
* <p>
171+
* The means by which "known objects" are determined is implementation
172+
* dependent, although the most typical use case is to query the
173+
* {@link ObjectService} for known objects of type {@link #getInputType()},
174+
* and return those. But other behaviors are possible, depending on the
175+
* converter implementation.
176+
* </p>
177+
*
178+
* @param objects an initialized collection into which appropriate objects
179+
* will be inserted.
180+
*/
181+
void populateInputCandidates(Collection<Object> objects);
182+
183+
/**
184+
* @return The base {@code Class} this {@code Converter} produces as output.
185+
*/
186+
Class<O> getOutputType();
187+
188+
/**
189+
* @return The base {@code Class} this {@code Converter} accepts as input.
190+
*/
191+
Class<I> getInputType();
158192
}

src/main/java/org/scijava/convert/DefaultConvertService.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
package org.scijava.convert;
3333

3434
import java.lang.reflect.Type;
35+
import java.util.ArrayList;
36+
import java.util.Collection;
37+
import java.util.LinkedHashSet;
38+
import java.util.Set;
3539

3640
import org.scijava.plugin.Plugin;
3741
import org.scijava.service.Service;
@@ -45,9 +49,10 @@
4549
public class DefaultConvertService extends AbstractConvertService
4650
{
4751

52+
@SuppressWarnings({ "unchecked", "rawtypes" })
4853
@Override
49-
public Class<Converter> getPluginType() {
50-
return Converter.class;
54+
public Class<Converter<?, ?>> getPluginType() {
55+
return (Class)Converter.class;
5156
}
5257

5358
@Override
@@ -58,22 +63,22 @@ public Class<ConversionRequest> getType() {
5863
// -- ConversionService methods --
5964

6065
@Override
61-
public Converter getHandler(final Object src, final Class<?> dest) {
66+
public Converter<?, ?> getHandler(final Object src, final Class<?> dest) {
6267
return getHandler(new ConversionRequest(src, dest));
6368
}
6469

6570
@Override
66-
public Converter getHandler(final Class<?> src, final Class<?> dest) {
71+
public Converter<?, ?> getHandler(final Class<?> src, final Class<?> dest) {
6772
return getHandler(new ConversionRequest(src, dest));
6873
}
6974

7075
@Override
71-
public Converter getHandler(final Object src, final Type dest) {
76+
public Converter<?, ?> getHandler(final Object src, final Type dest) {
7277
return getHandler(new ConversionRequest(src, dest));
7378
}
7479

7580
@Override
76-
public Converter getHandler(final Class<?> src, final Type dest) {
81+
public Converter<?, ?> getHandler(final Class<?> src, final Type dest) {
7782
return getHandler(new ConversionRequest(src, dest));
7883
}
7984

@@ -96,4 +101,17 @@ public boolean supports(final Object src, final Type dest) {
96101
public boolean supports(final Class<?> src, final Type dest) {
97102
return supports(new ConversionRequest(src, dest));
98103
}
104+
105+
@Override
106+
public Collection<Object> getCompatibleInputs(Class<?> dest) {
107+
Set<Object> objects = new LinkedHashSet<Object>();
108+
109+
for (final Converter<?, ?> c : getInstances()) {
110+
if (dest.isAssignableFrom(c.getOutputType())) {
111+
c.populateInputCandidates(objects);
112+
}
113+
}
114+
115+
return new ArrayList<Object>(objects);
116+
}
99117
}

src/main/java/org/scijava/convert/DefaultConverter.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
* @author Mark Hiner
5656
*/
5757
@Plugin(type = Converter.class)
58-
public class DefaultConverter extends AbstractConverter {
58+
public class DefaultConverter extends AbstractConverter<Object, Object> {
5959

6060
// -- ConversionHandler methods --
6161

@@ -265,6 +265,16 @@ public <T> T convert(final Object src, final Class<T> dest) {
265265
}
266266
}
267267

268+
@Override
269+
public Class<Object> getOutputType() {
270+
return Object.class;
271+
}
272+
273+
@Override
274+
public Class<Object> getInputType() {
275+
return Object.class;
276+
}
277+
268278
// -- Helper methods --
269279

270280
private Constructor<?> getConstructor(final Class<?> type,

src/main/java/org/scijava/util/ConversionUtils.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class ConversionUtils {
5151

5252
private static ConvertService convertService;
5353

54-
private static Converter converterNoContext;
54+
private static Converter<?, ?> converterNoContext;
5555

5656
private static double servicePriority = 0.0;
5757

@@ -195,7 +195,7 @@ public static void setDelegateService(final ConvertService convertService,
195195
*/
196196
@Deprecated
197197
public static Object convert(final Object src, final Type dest) {
198-
final Converter handler = handler(new ConversionRequest(src, dest));
198+
final Converter<?, ?> handler = handler(new ConversionRequest(src, dest));
199199
return (handler == null ? null : handler.convert(src, dest));
200200
}
201201

@@ -205,7 +205,7 @@ public static Object convert(final Object src, final Type dest) {
205205
*/
206206
@Deprecated
207207
public static <T> T convert(final Object src, final Class<T> dest) {
208-
final Converter handler = handler(new ConversionRequest(src, dest));
208+
final Converter<?, ?> handler = handler(new ConversionRequest(src, dest));
209209
return (handler == null ? null : handler.convert(src, dest));
210210
}
211211

@@ -215,7 +215,7 @@ public static <T> T convert(final Object src, final Class<T> dest) {
215215
*/
216216
@Deprecated
217217
public static boolean canConvert(final Class<?> src, final Type dest) {
218-
final Converter handler = handler(new ConversionRequest(src, dest));
218+
final Converter<?, ?> handler = handler(new ConversionRequest(src, dest));
219219
return (handler == null ? false : handler.canConvert(src, dest));
220220
}
221221

@@ -225,7 +225,7 @@ public static boolean canConvert(final Class<?> src, final Type dest) {
225225
*/
226226
@Deprecated
227227
public static boolean canConvert(final Class<?> src, final Class<?> dest) {
228-
final Converter handler = handler(new ConversionRequest(src, dest));
228+
final Converter<?, ?> handler = handler(new ConversionRequest(src, dest));
229229
return (handler == null ? false : handler.canConvert(src, dest));
230230
}
231231

@@ -235,7 +235,7 @@ public static boolean canConvert(final Class<?> src, final Class<?> dest) {
235235
*/
236236
@Deprecated
237237
public static boolean canConvert(final Object src, final Type dest) {
238-
final Converter handler = handler(new ConversionRequest(src, dest));
238+
final Converter<?, ?> handler = handler(new ConversionRequest(src, dest));
239239
return (handler == null ? false : handler.canConvert(src, dest));
240240
}
241241

@@ -245,7 +245,7 @@ public static boolean canConvert(final Object src, final Type dest) {
245245
*/
246246
@Deprecated
247247
public static boolean canConvert(final Object src, final Class<?> dest) {
248-
final Converter handler = handler(new ConversionRequest(src, dest));
248+
final Converter<?, ?> handler = handler(new ConversionRequest(src, dest));
249249
return (handler == null ? false : handler.canConvert(src, dest));
250250
}
251251

@@ -270,7 +270,7 @@ public static Class<?> getComponentClass(final Type type) {
270270
*
271271
* @return The {@link Converter} to use for handling the given request.
272272
*/
273-
private static Converter handler(final ConversionRequest data) {
273+
private static Converter<?, ?> handler(final ConversionRequest data) {
274274
if (convertService != null) return convertService.getHandler(data);
275275

276276
if (converterNoContext == null) converterNoContext = new DefaultConverter();

src/main/java/org/scijava/widget/AbstractInputHarvester.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.List;
3636

3737
import org.scijava.AbstractContextual;
38+
import org.scijava.convert.ConvertService;
3839
import org.scijava.module.Module;
3940
import org.scijava.module.ModuleCanceledException;
4041
import org.scijava.module.ModuleException;
@@ -63,6 +64,9 @@ public abstract class AbstractInputHarvester<P, W> extends AbstractContextual
6364
@Parameter
6465
private ObjectService objectService;
6566

67+
@Parameter
68+
private ConvertService convertService;
69+
6670
// -- InputHarvester methods --
6771

6872
@Override
@@ -142,9 +146,14 @@ private <T> WidgetModel addInput(final InputPanel<P, W> inputPanel,
142146
return null;
143147
}
144148

145-
/** Asks the object service for valid choices */
146-
private <T> List<T> getObjects(final Class<T> type) {
147-
return objectService.getObjects(type);
149+
/** Asks the object service and convert service for valid choices */
150+
@SuppressWarnings("unchecked")
151+
private List<?> getObjects(final Class<?> type) {
152+
@SuppressWarnings("rawtypes")
153+
List compatibleInputs =
154+
new ArrayList(convertService.getCompatibleInputs(type));
155+
compatibleInputs.addAll(objectService.getObjects(type));
156+
return compatibleInputs;
148157
}
149158

150159
}

0 commit comments

Comments
 (0)