Skip to content

Commit 49d0003

Browse files
committed
Merge pull request #228 from scijava/generalize-input-matching
Generalize compatibility matches
2 parents 04755c9 + 6c2b116 commit 49d0003

File tree

5 files changed

+175
-94
lines changed

5 files changed

+175
-94
lines changed

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

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,46 +32,143 @@
3232
package org.scijava.convert;
3333

3434
import java.lang.reflect.Type;
35+
import java.util.Collection;
36+
import java.util.HashSet;
37+
import java.util.Set;
3538

3639
import org.scijava.plugin.AbstractHandlerService;
3740
import org.scijava.util.ConversionUtils;
3841

3942
/**
40-
* Abstract superclass for {@link ConvertService} implementations. Sets
41-
* this service as the active delegate service in {@link ConversionUtils}.
43+
* Abstract superclass for {@link ConvertService} implementations. Sets this
44+
* service as the active delegate service in {@link ConversionUtils}.
4245
*
4346
* @author Mark Hiner
4447
*/
45-
public abstract class AbstractConvertService extends
46-
AbstractHandlerService<ConversionRequest, Converter<?, ?>> implements
47-
ConvertService
48-
{
48+
public abstract class AbstractConvertService extends AbstractHandlerService<ConversionRequest, Converter<?, ?>>
49+
implements ConvertService {
4950

5051
// -- ConversionService methods --
52+
@SuppressWarnings({ "unchecked", "rawtypes" })
53+
@Override
54+
public Class<Converter<?, ?>> getPluginType() {
55+
return (Class) Converter.class;
56+
}
57+
58+
@Override
59+
public Class<ConversionRequest> getType() {
60+
return ConversionRequest.class;
61+
}
62+
63+
@Override
64+
public Converter<?, ?> getHandler(final Object src, final Class<?> dest) {
65+
return getHandler(new ConversionRequest(src, dest));
66+
}
67+
68+
@Override
69+
public Converter<?, ?> getHandler(final Class<?> src, final Class<?> dest) {
70+
return getHandler(new ConversionRequest(src, dest));
71+
}
72+
73+
@Override
74+
public Converter<?, ?> getHandler(final Object src, final Type dest) {
75+
return getHandler(new ConversionRequest(src, dest));
76+
}
77+
78+
@Override
79+
public Converter<?, ?> getHandler(final Class<?> src, final Type dest) {
80+
return getHandler(new ConversionRequest(src, dest));
81+
}
82+
83+
@Override
84+
public boolean supports(final Object src, final Class<?> dest) {
85+
return supports(new ConversionRequest(src, dest));
86+
}
87+
88+
@Override
89+
public boolean supports(final Class<?> src, final Class<?> dest) {
90+
return supports(new ConversionRequest(src, dest));
91+
}
5192

5293
@Override
53-
public Object convert(Object src, Type dest) {
94+
public boolean supports(final Object src, final Type dest) {
95+
return supports(new ConversionRequest(src, dest));
96+
}
97+
98+
@Override
99+
public boolean supports(final Class<?> src, final Type dest) {
100+
return supports(new ConversionRequest(src, dest));
101+
}
102+
103+
@Override
104+
public Collection<Object> getCompatibleInputs(final Class<?> dest) {
105+
final Set<Object> objects = new HashSet<Object>();
106+
107+
for (final Converter<?, ?> c : getInstances()) {
108+
if (dest.isAssignableFrom(c.getOutputType())) {
109+
c.populateInputCandidates(objects);
110+
}
111+
}
112+
113+
return objects;
114+
}
115+
116+
@Override
117+
public Object convert(final Object src, final Type dest) {
54118
return convert(new ConversionRequest(src, dest));
55119
}
56120

57121
@Override
58-
public <T> T convert(Object src, Class<T> dest) {
122+
public <T> T convert(final Object src, final Class<T> dest) {
59123
// NB: repeated code with convert(ConversionRequest), because the
60124
// handler's convert method respects the T provided
61-
Converter<?, ?> handler = getHandler(src, dest);
125+
final Converter<?, ?> handler = getHandler(src, dest);
62126
return handler == null ? null : handler.convert(src, dest);
63127
}
64128

65129
@Override
66-
public Object convert(ConversionRequest request) {
67-
Converter<?, ?> handler = getHandler(request);
130+
public Object convert(final ConversionRequest request) {
131+
final Converter<?, ?> handler = getHandler(request);
68132
return handler == null ? null : handler.convert(request);
69133
}
70134

135+
@Override
136+
public Collection<Class<?>> getCompatibleInputClasses(final Class<?> dest) {
137+
final Set<Class<?>> compatibleClasses = new HashSet<Class<?>>();
138+
139+
for (final Converter<?, ?> converter : getInstances()) {
140+
addIfMatches(dest, converter.getOutputType(), converter.getInputType(), compatibleClasses);
141+
}
142+
143+
return compatibleClasses;
144+
}
145+
146+
@Override
147+
public Collection<Class<?>> getCompatibleOutputClasses(final Class<?> source) {
148+
final Set<Class<?>> compatibleClasses = new HashSet<Class<?>>();
149+
150+
for (final Converter<?, ?> converter : getInstances()) {
151+
addIfMatches(source, converter.getInputType(), converter.getOutputType(), compatibleClasses);
152+
}
153+
154+
return compatibleClasses;
155+
}
156+
71157
// -- Service methods --
72158

73159
@Override
74160
public void initialize() {
75161
ConversionUtils.setDelegateService(this, getPriority());
76162
}
163+
164+
// -- Helper methods --
165+
166+
/**
167+
* Test two classes; if they match, a third class is added to the provided
168+
* set of classes.
169+
*/
170+
private void addIfMatches(final Class<?> c1, final Class<?> c2, final Class<?> toAdd, final Set<Class<?>> classes) {
171+
if (c1 == c2)
172+
classes.add(toAdd);
173+
}
77174
}

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,24 @@ public interface ConvertService extends
8383
*/
8484
boolean supports(Object src, Type dest);
8585

86+
/**
87+
* @return A collection of instances that could be converted to the
88+
* specified class.
89+
*/
90+
Collection<Object> getCompatibleInputs(Class<?> dest);
91+
92+
/**
93+
* @return A collection of all classes that could potentially be converted
94+
* <b>to</b> the specified class.
95+
*/
96+
Collection<Class<?>> getCompatibleInputClasses(Class<?> dest);
97+
98+
/**
99+
* @return A collection of all classes that could potentially be converted
100+
* <b>from</b> the specified class.
101+
*/
102+
Collection<Class<?>> getCompatibleOutputClasses(Class<?> dest);
103+
86104
// -- Deprecated API --
87105

88106
/**
@@ -112,6 +130,4 @@ public interface ConvertService extends
112130
*/
113131
@Deprecated
114132
boolean supports(Class<?> src, Type dest);
115-
116-
Collection<Object> getCompatibleInputs(Class<?> dest);
117133
}

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

Lines changed: 1 addition & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,6 @@
3131

3232
package org.scijava.convert;
3333

34-
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;
39-
4034
import org.scijava.plugin.Plugin;
4135
import org.scijava.service.Service;
4236

@@ -48,70 +42,5 @@
4842
@Plugin(type = Service.class)
4943
public class DefaultConvertService extends AbstractConvertService
5044
{
51-
52-
@SuppressWarnings({ "unchecked", "rawtypes" })
53-
@Override
54-
public Class<Converter<?, ?>> getPluginType() {
55-
return (Class)Converter.class;
56-
}
57-
58-
@Override
59-
public Class<ConversionRequest> getType() {
60-
return ConversionRequest.class;
61-
}
62-
63-
// -- ConversionService methods --
64-
65-
@Override
66-
public Converter<?, ?> getHandler(final Object src, final Class<?> dest) {
67-
return getHandler(new ConversionRequest(src, dest));
68-
}
69-
70-
@Override
71-
public Converter<?, ?> getHandler(final Class<?> src, final Class<?> dest) {
72-
return getHandler(new ConversionRequest(src, dest));
73-
}
74-
75-
@Override
76-
public Converter<?, ?> getHandler(final Object src, final Type dest) {
77-
return getHandler(new ConversionRequest(src, dest));
78-
}
79-
80-
@Override
81-
public Converter<?, ?> getHandler(final Class<?> src, final Type dest) {
82-
return getHandler(new ConversionRequest(src, dest));
83-
}
84-
85-
@Override
86-
public boolean supports(final Object src, final Class<?> dest) {
87-
return supports(new ConversionRequest(src, dest));
88-
}
89-
90-
@Override
91-
public boolean supports(final Class<?> src, final Class<?> dest) {
92-
return supports(new ConversionRequest(src, dest));
93-
}
94-
95-
@Override
96-
public boolean supports(final Object src, final Type dest) {
97-
return supports(new ConversionRequest(src, dest));
98-
}
99-
100-
@Override
101-
public boolean supports(final Class<?> src, final Type dest) {
102-
return supports(new ConversionRequest(src, dest));
103-
}
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-
}
45+
// Trivial implementation
11746
}

src/main/java/org/scijava/module/DefaultModuleService.java

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333

3434
import java.util.Collection;
3535
import java.util.HashMap;
36+
import java.util.HashSet;
3637
import java.util.List;
3738
import java.util.Map;
39+
import java.util.Set;
3840
import java.util.concurrent.Callable;
3941
import java.util.concurrent.ExecutionException;
4042
import java.util.concurrent.Future;
@@ -252,14 +254,24 @@ public <M extends Module> M waitFor(final Future<M> future) {
252254
public <T> ModuleItem<T> getSingleInput(final Module module,
253255
final Class<T> type)
254256
{
255-
return getSingleItem(module, type, module.getInfo().inputs());
257+
return getTypedSingleItem(module, type, module.getInfo().inputs());
256258
}
257259

258260
@Override
259261
public <T> ModuleItem<T> getSingleOutput(final Module module,
260262
final Class<T> type)
261263
{
262-
return getSingleItem(module, type, module.getInfo().outputs());
264+
return getTypedSingleItem(module, type, module.getInfo().outputs());
265+
}
266+
267+
@Override
268+
public ModuleItem<?> getSingleInput(Module module, Collection<Class<?>> types) {
269+
return getSingleItem(module, types, module.getInfo().inputs());
270+
}
271+
272+
@Override
273+
public ModuleItem<?> getSingleOutput(Module module, Collection<Class<?>> types) {
274+
return getSingleItem(module, types, module.getInfo().outputs());
263275
}
264276

265277
@Override
@@ -431,20 +443,35 @@ private void assignInputs(final Module module,
431443
}
432444
}
433445

434-
private <T> ModuleItem<T> getSingleItem(final Module module,
446+
private <T> ModuleItem<T> getTypedSingleItem(final Module module,
435447
final Class<T> type, final Iterable<ModuleItem<?>> items)
436448
{
437-
ModuleItem<T> result = null;
449+
Set<Class<?>> types = new HashSet<Class<?>>();
450+
types.add(type);
451+
@SuppressWarnings("unchecked")
452+
ModuleItem<T> result = (ModuleItem<T>) getSingleItem(module, types, items);
453+
return result;
454+
}
455+
456+
private ModuleItem<?> getSingleItem(final Module module,
457+
final Collection<Class<?>> types, final Iterable<ModuleItem<?>> items)
458+
{
459+
ModuleItem<?> result = null;
460+
438461
for (final ModuleItem<?> item : items) {
439462
final String name = item.getName();
440463
final boolean resolved = module.isResolved(name);
441464
if (resolved) continue; // skip resolved inputs
442465
if (!item.isAutoFill()) continue; // skip unfillable inputs
443-
if (!type.isAssignableFrom(item.getType())) continue;
444-
if (result != null) return null; // multiple matching items
445-
@SuppressWarnings("unchecked")
446-
final ModuleItem<T> typedItem = (ModuleItem<T>) item;
447-
result = typedItem;
466+
final Class<?> itemType = item.getType();
467+
for (final Class<?> type : types) {
468+
if (type.isAssignableFrom(itemType)) {
469+
if (result != null) return null; // multiple matching module items
470+
result = item;
471+
// This module item matches, so no need to check more classes.
472+
break;
473+
}
474+
}
448475
}
449476
return result;
450477
}

src/main/java/org/scijava/module/ModuleService.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,18 @@ <M extends Module> Future<M> run(M module,
274274
*/
275275
<T> ModuleItem<T> getSingleOutput(Module module, Class<T> type);
276276

277+
/**
278+
* As {@link #getSingleInput(Module, Class)} but will match with a set of
279+
* potential classes, at the cost of generic parameter safety.
280+
*/
281+
ModuleItem<?> getSingleInput(Module module, Collection<Class<?>> types);
282+
283+
/**
284+
* As {@link #getSingleOutput(Module, Class)} but will match with a set of
285+
* potential classes, at the cost of generic parameter safety.
286+
*/
287+
ModuleItem<?> getSingleOutput(Module module, Collection<Class<?>> types);
288+
277289
/**
278290
* Registers the given value for the given {@link ModuleItem} using the
279291
* {@link PrefService}.

0 commit comments

Comments
 (0)