Skip to content

Commit 42606da

Browse files
committed
Support OpWrapper extensibility
OpWrappers are now Scijava Plugins, which means that they are now discoverable and extensible.
1 parent 77e46a9 commit 42606da

File tree

10 files changed

+973
-275
lines changed

10 files changed

+973
-275
lines changed

src/main/java/org/scijava/ops/OpService.java

Lines changed: 53 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
package org.scijava.ops;
3030

3131
import java.lang.reflect.Field;
32-
import java.lang.reflect.InvocationTargetException;
3332
import java.lang.reflect.Modifier;
3433
import java.lang.reflect.Type;
3534
import java.lang.reflect.TypeVariable;
@@ -48,7 +47,6 @@
4847
import org.scijava.log.LogService;
4948
import org.scijava.ops.core.Op;
5049
import org.scijava.ops.core.OpCollection;
51-
import org.scijava.ops.function.GenericTypedOp;
5250
import org.scijava.ops.matcher.DefaultOpMatcher;
5351
import org.scijava.ops.matcher.OpCandidate;
5452
import org.scijava.ops.matcher.OpClassInfo;
@@ -66,6 +64,7 @@
6664
import org.scijava.ops.types.Any;
6765
import org.scijava.ops.types.Nil;
6866
import org.scijava.ops.types.TypeService;
67+
import org.scijava.ops.util.OpWrapper;
6968
import org.scijava.param.FunctionalMethodType;
7069
import org.scijava.param.ParameterStructs;
7170
import org.scijava.plugin.Parameter;
@@ -104,7 +103,7 @@ public class OpService extends AbstractService implements SciJavaService, OpEnvi
104103
/**
105104
* Prefix tree to cache and quickly find {@link OpInfo}s.
106105
*/
107-
// private PrefixTree<OpInfo> opCache;
106+
// private PrefixTree<OpInfo> opCache;
108107

109108
/**
110109
* Map to collect all aliases for a specific op. All aliases will map to one
@@ -114,33 +113,9 @@ public class OpService extends AbstractService implements SciJavaService, OpEnvi
114113

115114
private List<OpTransformer> transformerIndex;
116115

117-
private static Map<Class<?>, Class<?>> wrappers = wrappers();
118-
119-
private static Map<Class<?>, Class<?>> wrappers() {
120-
final Map<Class<?>, Class<?>> result = new HashMap<>();
121-
final Class<?>[] wrapperClasses = { //
122-
GenericTypedOp.P.class, //
123-
GenericTypedOp.F1.class, //
124-
GenericTypedOp.F2.class, //
125-
GenericTypedOp.F3.class, //
126-
GenericTypedOp.C0.class, //
127-
GenericTypedOp.C1.class, //
128-
GenericTypedOp.C2.class, //
129-
GenericTypedOp.C3.class, //
130-
GenericTypedOp.IP1.class, //
131-
GenericTypedOp.IP2_1.class, //
132-
GenericTypedOp.IP2_2.class, //
133-
GenericTypedOp.IP3_1.class, //
134-
GenericTypedOp.IP3_2.class, //
135-
GenericTypedOp.IP3_3.class //
136-
};
137-
for (final Class<?> c : wrapperClasses) {
138-
result.put(c.getInterfaces()[0], c);
139-
}
140-
return result;
141-
}
116+
private Map<Class<?>, OpWrapper<?>> wrappers;
142117

143-
public void initOpCache() {
118+
private void initOpCache() {
144119
opCache = new HashMap<>();
145120

146121
// Add regular Ops
@@ -158,7 +133,7 @@ public void initOpCache() {
158133
try {
159134
Class<? extends OpCollection> c = pluginInfo.loadClass();
160135
final List<Field> fields = ClassUtils.getAnnotatedFields(c, OpField.class);
161-
Object instance = null;
136+
Object instance = null;
162137
for (Field field : fields) {
163138
final boolean isStatic = Modifier.isStatic(field.getModifiers());
164139
if (!isStatic && instance == null) {
@@ -173,6 +148,13 @@ public void initOpCache() {
173148
}
174149
}
175150

151+
private void initWrappers() {
152+
wrappers = new HashMap<>();
153+
for (OpWrapper<?> wrapper : pluginService.createInstancesOfType(OpWrapper.class)) {
154+
wrappers.put(wrapper.type(), wrapper);
155+
}
156+
}
157+
176158
private void addToOpIndex(final OpInfo opInfo, final String opNames) {
177159
String[] parsedOpNames = OpUtils.parseOpNames(opNames);
178160
if (parsedOpNames == null || parsedOpNames.length == 0) {
@@ -184,7 +166,7 @@ private void addToOpIndex(final OpInfo opInfo, final String opNames) {
184166
+ opInfo.getValidityException().getMessage());
185167
return;
186168
}
187-
for(String opName: parsedOpNames) {
169+
for (String opName : parsedOpNames) {
188170
if (!opCache.containsKey(opName))
189171
opCache.put(opName, new ArrayList<>());
190172
opCache.get(opName).add(opInfo);
@@ -200,9 +182,7 @@ public Iterable<OpInfo> infos() {
200182
if (opCache == null) {
201183
initOpCache();
202184
}
203-
return opCache.values().stream()
204-
.flatMap(list -> list.stream())
205-
.collect(Collectors.toList());
185+
return opCache.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());
206186
}
207187

208188
@Override
@@ -251,30 +231,23 @@ private OpTransformationMatcher getTransformationMatcher() {
251231
* matching the functional type and the name could not be found, if
252232
* an exception occurs during injection
253233
*/
254-
private List<Object> resolveOpDependencies(OpCandidate op)
255-
throws OpMatchingException
256-
{
234+
private List<Object> resolveOpDependencies(OpCandidate op) throws OpMatchingException {
257235
final List<OpDependencyMember<?>> dependencies = op.opInfo().dependencies();
258-
final List<Object> resolvedDependencies = new ArrayList<>(dependencies
259-
.size());
236+
final List<Object> resolvedDependencies = new ArrayList<>(dependencies.size());
260237
for (final OpDependencyMember<?> dependency : dependencies) {
261238
final String dependencyName = dependency.getDependencyName();
262-
final Type mappedDependencyType = Types.mapVarToTypes(new Type[] {
263-
dependency.getType() }, op.typeVarAssigns())[0];
264-
final OpRef inferredRef = inferOpRef(mappedDependencyType, dependencyName,
265-
op.typeVarAssigns());
239+
final Type mappedDependencyType = Types.mapVarToTypes(new Type[] { dependency.getType() },
240+
op.typeVarAssigns())[0];
241+
final OpRef inferredRef = inferOpRef(mappedDependencyType, dependencyName, op.typeVarAssigns());
266242
if (inferredRef == null) {
267-
throw new OpMatchingException("Could not infer functional " +
268-
"method inputs and outputs of Op dependency field: " + dependency
269-
.getKey());
243+
throw new OpMatchingException("Could not infer functional "
244+
+ "method inputs and outputs of Op dependency field: " + dependency.getKey());
270245
}
271246
try {
272247
resolvedDependencies.add(findOpInstance(dependencyName, inferredRef));
273-
}
274-
catch (final Exception e) {
275-
throw new OpMatchingException(
276-
"Could not find Op that matches requested Op dependency:" +
277-
"\nOp class: " + op.opInfo().implementationName() + //
248+
} catch (final Exception e) {
249+
throw new OpMatchingException("Could not find Op that matches requested Op dependency:" + "\nOp class: "
250+
+ op.opInfo().implementationName() + //
278251
"\nDependency identifier: " + dependency.getKey() + //
279252
"\n\n Attempted request:\n" + inferredRef, e);
280253
}
@@ -285,8 +258,8 @@ private List<Object> resolveOpDependencies(OpCandidate op)
285258
@SuppressWarnings("unchecked")
286259
public <T> T findOpInstance(final String opName, final Nil<T> specialType, final Nil<?>[] inTypes,
287260
final Nil<?> outType) {
288-
final OpRef ref = OpRef.fromTypes(opName, toTypes(specialType), outType != null ? outType.getType() : null, toTypes(
289-
inTypes));
261+
final OpRef ref = OpRef.fromTypes(opName, toTypes(specialType), outType != null ? outType.getType() : null,
262+
toTypes(inTypes));
290263
return (T) findOpInstance(opName, ref);
291264
}
292265

@@ -314,8 +287,7 @@ public Object findOpInstance(final String opName, final OpRef ref) {
314287
// If we found one, try to do transformation and return transformed op
315288
log.debug("Matching Op transformation found:\n" + transformation + "\n");
316289
try {
317-
final List<Object> dependencies = resolveOpDependencies(transformation
318-
.getSourceOp());
290+
final List<Object> dependencies = resolveOpDependencies(transformation.getSourceOp());
319291
op = transformation.exceute(this, dependencies);
320292
} catch (OpMatchingException | OpTransformationException e1) {
321293
throw new IllegalArgumentException("Execution of Op transformatioon failed:\n" + e1);
@@ -349,8 +321,12 @@ else if (transformation != null)
349321
* @return an {@link Op} wrapping of op.
350322
*/
351323
private Object wrapOp(Object op, OpCandidate match, OpTransformationCandidate transformation) {
324+
if (wrappers == null)
325+
initWrappers();
326+
352327
// TODO: we don't want to wrap OpRunners, do we? What is the point?
353-
if(OpRunner.class.isInstance(op)) return op;
328+
if (OpRunner.class.isInstance(op))
329+
return op;
354330

355331
OpInfo opInfo = match == null ? transformation.getSourceOp().opInfo() : match.opInfo();
356332
// FIXME: this type is not necessarily Computer, Function, etc. but often
@@ -367,12 +343,8 @@ private Object wrapOp(Object op, OpCandidate match, OpTransformationCandidate tr
367343
throw new IllegalArgumentException(
368344
"Matched op Type " + type.getClass() + " matches multiple Op types: " + wrappers.toString());
369345
// get the wrapper and wrap up the Op
370-
Class<?> wrapper = wrappers.get(suitableWrappers[0]);
371-
// CTR FIXME: Instead of using reflection, register constructors
372-
// as BiFunction<OP, OpInfo, GenericOp<OP>> via ::new syntax.
373-
return wrapper.getConstructors()[0].newInstance(op, opInfo);
374-
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
375-
| SecurityException exc) {
346+
return wrap(suitableWrappers[0], op, opInfo);
347+
} catch (IllegalArgumentException | SecurityException exc) {
376348
log.error(exc.getMessage() != null ? exc.getMessage() : "Cannot wrap " + op.getClass());
377349
return op;
378350
} catch (NullPointerException e) {
@@ -381,6 +353,12 @@ private Object wrapOp(Object op, OpCandidate match, OpTransformationCandidate tr
381353
}
382354
}
383355

356+
@SuppressWarnings("unchecked")
357+
private <T> T wrap(Class<T> opType, Object op, OpInfo info) {
358+
OpWrapper<T> wrapper = (OpWrapper<T>) wrappers.get(opType);
359+
return wrapper.wrap((T) op, info);
360+
}
361+
384362
public <T> T findOp(final String opName, final Nil<T> specialType, final Nil<?>[] inTypes, final Nil<?> outType) {
385363
return findOpInstance(opName, specialType, inTypes, outType);
386364
}
@@ -392,9 +370,14 @@ private Type[] toTypes(Nil<?>... nils) {
392370
public Object run(final String opName, final Object... args) {
393371

394372
Nil<?>[] inTypes = Arrays.stream(args).map(arg -> Nil.of(typeService.reify(arg))).toArray(Nil[]::new);
395-
Nil<?> outType = new Nil<Any>() { @Override public Type getType() {return new Any();}};
373+
Nil<?> outType = new Nil<Any>() {
374+
@Override
375+
public Type getType() {
376+
return new Any();
377+
}
378+
};
396379

397-
OpRunner op = findOpInstance(opName, new Nil<OpRunner>(){}, inTypes, outType);
380+
OpRunner op = findOpInstance(opName, new Nil<OpRunner>() {}, inTypes, outType);
398381
return op.run(args);
399382
}
400383

@@ -413,6 +396,7 @@ public Object run(final String opName, final Object... args) {
413396
* InputTypes: [Double[], Double[]]
414397
* OutputTypes: [Double[]]
415398
* </pre>
399+
*
416400
* Input and output types will be inferred by looking at the signature of the
417401
* functional method of the specified type. Also see
418402
* {@link ParameterStructs#findFunctionalMethodTypes(Type)}.
@@ -422,8 +406,7 @@ public Object run(final String opName, final Object... args) {
422406
* @return null if the specified type has no functional method
423407
*/
424408
private OpRef inferOpRef(Type type, String name, Map<TypeVariable<?>, Type> typeVarAssigns)
425-
throws OpMatchingException
426-
{
409+
throws OpMatchingException {
427410
List<FunctionalMethodType> fmts = ParameterStructs.findFunctionalMethodTypes(type);
428411
if (fmts == null)
429412
return null;
@@ -444,8 +427,8 @@ private OpRef inferOpRef(Type type, String name, Map<TypeVariable<?>, Type> type
444427
if (numOutputs != 1) {
445428
String error = "Op '" + name + "' of type " + type + " specifies ";
446429
error += numOutputs == 0 //
447-
? "no outputs" //
448-
: "multiple outputs: " + Arrays.toString(outputs);
430+
? "no outputs" //
431+
: "multiple outputs: " + Arrays.toString(outputs);
449432
error += ". This is not supported.";
450433
throw new OpMatchingException(error);
451434
}
@@ -739,8 +722,7 @@ private String nodeToString(PrefixNode<T> node, String nodeName, StringBuilder s
739722
for (T t : node.data) {
740723
sb.append(getIndent(level) + "\t" + t.getClass().getSimpleName() + "\n");
741724
}
742-
if (!node.data.isEmpty()) {
743-
} else {
725+
if (!node.data.isEmpty()) {} else {
744726
sb.delete(sb.length() - 1, sb.length());
745727
sb.append(" <empty>\n");
746728
}
@@ -751,8 +733,7 @@ private String nodeToString(PrefixNode<T> node, String nodeName, StringBuilder s
751733
String sub = nodeToString(e.getValue(), e.getKey(), new StringBuilder(), level + 1);
752734
sb.append(sub + "\n");
753735
}
754-
if (!node.children.isEmpty()) {
755-
} else {
736+
if (!node.children.isEmpty()) {} else {
756737
sb.delete(sb.length() - 1, sb.length());
757738
sb.append(" <empty>\n");
758739
}

0 commit comments

Comments
 (0)