Skip to content

Commit 718bacb

Browse files
gselzerctrueden
authored andcommitted
Allow OpDependencies to be found in OpRunners
1 parent 593d7a4 commit 718bacb

File tree

5 files changed

+230
-118
lines changed

5 files changed

+230
-118
lines changed

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

Lines changed: 90 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public class OpService extends AbstractService implements SciJavaService, OpEnvi
9191

9292
@Parameter
9393
private OpTransformerService transformer;
94-
94+
9595
@Parameter
9696
private TypeService typeService;
9797

@@ -123,8 +123,7 @@ public void initOpCache() {
123123
// Add Ops contained in an OpCollection
124124
for (final PluginInfo<OpCollection> pluginInfo : pluginService.getPluginsOfType(OpCollection.class)) {
125125
try {
126-
final List<Field> fields = ClassUtils.getAnnotatedFields(pluginInfo.loadClass(),
127-
OpField.class);
126+
final List<Field> fields = ClassUtils.getAnnotatedFields(pluginInfo.loadClass(), OpField.class);
128127
for (Field field : fields) {
129128
OpInfo opInfo = new OpFieldInfo(field);
130129
addToCache(opInfo, field.getAnnotation(OpField.class).names());
@@ -138,8 +137,7 @@ public void initOpCache() {
138137
private void addToCache(OpInfo opInfo, String opNames) {
139138
String[] parsedOpNames = OpUtils.parseOpNames(opNames);
140139
if (parsedOpNames == null || parsedOpNames.length == 0) {
141-
log.error("Skipping Op " + opInfo.implementationName() + ":\n"
142-
+ "Op implementation must provide name.");
140+
log.error("Skipping Op " + opInfo.implementationName() + ":\n" + "Op implementation must provide name.");
143141
return;
144142
}
145143
if (!opInfo.isValid()) {
@@ -180,53 +178,53 @@ public LogService logger() {
180178
}
181179

182180
/**
183-
* Attempts to inject {@link OpDependency} annotated fields of the specified object by
184-
* looking for Ops matching the field type and the name specified in the
185-
* annotation. The field type is assumed to be functional.
181+
* Attempts to inject {@link OpDependency} annotated fields of the specified
182+
* object by looking for Ops matching the field type and the name specified in
183+
* the annotation. The field type is assumed to be functional.
186184
*
187185
* @param obj
188186
* @throws OpMatchingException
189-
* if the type of the specified object is not functional,
190-
* if the Op matching the functional type and the name could not be found,
191-
* if an exception occurs during injection
187+
* if the type of the specified object is not functional, if the Op
188+
* matching the functional type and the name could not be found, if
189+
* an exception occurs during injection
192190
*/
193-
public void resolveOpDependencies(Object obj, OpCandidate parentOp) throws OpMatchingException {
194-
final Class<?> c = obj.getClass();
195-
final List<Field> opFields = ClassUtils.getAnnotatedFields(c, OpDependency.class);
191+
private void resolveOpDependencies(Object obj, OpCandidate parentOp) throws OpMatchingException {
192+
Object op = obj;
193+
// HACK: Only works with Op instances and OpRunner, not extensible.
194+
// Consider extensible ways to achieve something similar e.g. extending OpInfo
195+
// to support OpDependencies.
196+
if (obj instanceof OpRunner) {
197+
op = ((OpRunner) obj).getAdaptedOp();
198+
}
199+
final List<Field> opFields = ClassUtils.getAnnotatedFields(op.getClass(), OpDependency.class);
196200

197201
for (final Field opField : opFields) {
198202
final String opName = opField.getAnnotation(OpDependency.class).name();
199-
final Type fieldType = Types.fieldType(opField, c);
200-
final Type mappedFieldType = Types.mapVarToTypes(new Type[] {fieldType}, parentOp.typeVarAssigns())[0];
203+
final Type fieldType = Types.fieldType(opField, op.getClass());
204+
final Type mappedFieldType = Types.mapVarToTypes(new Type[] { fieldType }, parentOp.typeVarAssigns())[0];
201205

202206
OpRef inferredRef = inferOpRef(mappedFieldType, opName, parentOp.typeVarAssigns());
203207
if (inferredRef == null) {
204-
throw new OpMatchingException("Could not infer functional "
205-
+ "method inputs and outputs of Op dependency field: "
206-
+ opField);
208+
throw new OpMatchingException(
209+
"Could not infer functional " + "method inputs and outputs of Op dependency field: " + opField);
207210
}
208211

209212
Object matchedOp = null;
210213
try {
211214
matchedOp = findOpInstance(opName, inferredRef);
212215
} catch (Exception e) {
213-
throw new OpMatchingException(
214-
"Could not find Op that matches requested Op dependency field:"
215-
+ "\nOp class: " + c.getName()
216-
+ "\nDependency field: " + opField.getName()
217-
+ "\n\n Attempted request:\n"
218-
+ inferredRef, e);
216+
throw new OpMatchingException("Could not find Op that matches requested Op dependency field:"
217+
+ "\nOp class: " + op.getClass().getName() + "\nDependency field: " + opField.getName()
218+
+ "\n\n Attempted request:\n" + inferredRef, e);
219219
}
220220

221221
try {
222222
opField.setAccessible(true);
223-
opField.set(obj, matchedOp);
223+
opField.set(op, matchedOp);
224224
} catch (IllegalArgumentException | IllegalAccessException e) {
225-
throw new OpMatchingException(
226-
"Exception trying to inject Op dependency field.\n"
227-
+ "\tOp dependency field to resolve: " + opField + "\n"
228-
+ "\tFound Op to inject: " + matchedOp.getClass().getName() + "\n"
229-
+ "\tWith inferred OpRef: " + inferredRef, e);
225+
throw new OpMatchingException("Exception trying to inject Op dependency field.\n"
226+
+ "\tOp dependency field to resolve: " + opField + "\n" + "\tFound Op to inject: "
227+
+ matchedOp.getClass().getName() + "\n" + "\tWith inferred OpRef: " + inferredRef, e);
230228
}
231229
}
232230
}
@@ -241,6 +239,7 @@ public <T> T findOpInstance(final String opName, final Nil<T> specialType, final
241239
public Object findOpInstance(final String opName, final OpRef ref, final Object... secondaryArgs) {
242240
Object op = null;
243241
OpCandidate match = null;
242+
OpTransformationCandidate transformation = null;
244243
try {
245244
// Find single match which matches the specified types
246245
match = matcher.findSingleMatch(this, ref);
@@ -249,8 +248,9 @@ public Object findOpInstance(final String opName, final OpRef ref, final Object.
249248
log.debug("No matching Op for request: " + ref + "\n");
250249
log.debug("Attempting Op transformation...");
251250

252-
// If we can't find an op matching the original request, we try to find a transformation
253-
OpTransformationCandidate transformation = transformer.findTransfromation(this, ref);
251+
// If we can't find an op matching the original request, we try to find a
252+
// transformation
253+
transformation = transformer.findTransfromation(this, ref);
254254
if (transformation == null) {
255255
log.debug("No matching Op transformation found");
256256
throw new IllegalArgumentException(e);
@@ -268,7 +268,10 @@ public Object findOpInstance(final String opName, final OpRef ref, final Object.
268268
}
269269
try {
270270
// Try to resolve annotated OpDependency fields
271-
resolveOpDependencies(op, match);
271+
if (match != null)
272+
resolveOpDependencies(op, match);
273+
else if (transformation != null)
274+
resolveOpDependencies(op, transformation.getSourceOp());
272275
} catch (OpMatchingException e) {
273276
throw new IllegalArgumentException(e);
274277
}
@@ -280,69 +283,75 @@ public <T> T findOp(final String opName, final Nil<T> specialType, final Nil<?>[
280283
return findOpInstance(opName, specialType, inTypes, outTypes, secondaryArgs);
281284
}
282285

283-
public <T> T findOp(final String opName, final Nil<T> specialType, final Nil<?>[] inTypes,
284-
final Nil<?> outType, final Object... secondaryArgs) {
286+
public <T> T findOp(final String opName, final Nil<T> specialType, final Nil<?>[] inTypes, final Nil<?> outType,
287+
final Object... secondaryArgs) {
285288
return findOpInstance(opName, specialType, inTypes, new Nil[] { outType }, secondaryArgs);
286289
}
287290

288291
private Type[] toTypes(Nil<?>... nils) {
289292
return Arrays.stream(nils).filter(n -> n != null).map(n -> n.getType()).toArray(Type[]::new);
290293
}
291-
292-
294+
293295
public Object run(final String opName, final Object... args) {
294-
296+
295297
Nil<?>[] inTypes = Arrays.stream(args).map(arg -> Nil.of(typeService.reify(arg))).toArray(Nil[]::new);
296-
Nil<?>[] outTypes = new Nil<?>[] {new Nil<Object>() {}};
297-
298-
OpRunner<Object> op = findOpInstance(opName, new Nil<OpRunner<Object>>() {}, inTypes, outTypes);
299-
300-
//TODO change
298+
Nil<?>[] outTypes = new Nil<?>[] { new Nil<Object>() {
299+
} };
300+
301+
OpRunner<Object> op = findOpInstance(opName, new Nil<OpRunner<Object>>() {
302+
}, inTypes, outTypes);
303+
304+
// TODO change
301305
return op.run(args);
302306
}
303307

304308
/**
305309
* Tries to infer a {@link OpRef} from a functional Op type. E.g. the type:
306-
* <pre>Computer&lt;Double[], Double[]&gt</pre>
310+
*
311+
* <pre>
312+
* Computer&lt;Double[], Double[]&gt
313+
* </pre>
314+
*
307315
* Will result in the following {@link OpRef}:
316+
*
308317
* <pre>
309318
* Name: 'specified name'
310319
* Types: [Computer&lt;Double, Double&gt]
311320
* InputTypes: [Double[], Double[]]
312321
* OutputTypes: [Double[]]
313322
* </pre>
314-
* Input and output types will be inferred by looking at the signature of the functional
315-
* method of the specified type. Also see {@link ParameterStructs#getFunctionalMethodTypes(Type)}.
323+
*
324+
* Input and output types will be inferred by looking at the signature of the
325+
* functional method of the specified type. Also see
326+
* {@link ParameterStructs#getFunctionalMethodTypes(Type)}.
316327
*
317328
* @param type
318329
* @param name
319-
* @return null if
320-
* the specified type has no functional method
330+
* @return null if the specified type has no functional method
321331
*/
322332
private OpRef inferOpRef(Type type, String name, Map<TypeVariable<?>, Type> typeVarAssigns) {
323333
List<FunctionalMethodType> fmts = ParameterStructs.getFunctionalMethodTypes(type);
324-
if (fmts == null) return null;
334+
if (fmts == null)
335+
return null;
325336

326337
EnumSet<ItemIO> inIos = EnumSet.of(ItemIO.BOTH, ItemIO.INPUT);
327338
EnumSet<ItemIO> outIos = EnumSet.of(ItemIO.BOTH, ItemIO.OUTPUT);
328339

329-
Type[] inputs = fmts.stream().filter(fmt -> inIos.contains(fmt.itemIO()))
330-
.map(fmt -> fmt.type()).toArray(Type[]::new);
340+
Type[] inputs = fmts.stream().filter(fmt -> inIos.contains(fmt.itemIO())).map(fmt -> fmt.type())
341+
.toArray(Type[]::new);
331342

332-
Type[] outputs = fmts.stream().filter(fmt -> outIos.contains(fmt.itemIO()))
333-
.map(fmt -> fmt.type()).toArray(Type[]::new);
343+
Type[] outputs = fmts.stream().filter(fmt -> outIos.contains(fmt.itemIO())).map(fmt -> fmt.type())
344+
.toArray(Type[]::new);
334345

335346
Type[] mappedInputs = Types.mapVarToTypes(inputs, typeVarAssigns);
336347
Type[] mappedOutputs = Types.mapVarToTypes(outputs, typeVarAssigns);
337348

338-
return new OpRef(name, new Type[]{type}, mappedOutputs, mappedInputs);
349+
return new OpRef(name, new Type[] { type }, mappedOutputs, mappedInputs);
339350
}
340351

341-
342-
343352
/**
344-
* Updates alias map using the specified String list. The first String in
345-
* the list is assumed to be the canonical name of the op. After this method
353+
* Updates alias map using the specified String list. The first String in the
354+
* list is assumed to be the canonical name of the op. After this method
346355
* returns, all String in the specified list will map to the canonical name.
347356
*
348357
* @param opNames
@@ -355,8 +364,8 @@ private void addAliases(String[] opNames, String opImpl) {
355364
}
356365
if (opAliases.containsKey(alias)) {
357366
if (!opAliases.get(alias).equals(opName)) {
358-
log.warn("Possible naming clash for op '" + opImpl + "' detected. Attempting to add alias '"
359-
+ alias + "' for op name '" + opName + "'. However the alias '" + alias + "' is already "
367+
log.warn("Possible naming clash for op '" + opImpl + "' detected. Attempting to add alias '" + alias
368+
+ "' for op name '" + opName + "'. However the alias '" + alias + "' is already "
360369
+ "associated with op name '" + opAliases.get(alias) + "'.");
361370
}
362371
continue;
@@ -381,9 +390,9 @@ private static String getIndent(int numOfTabs) {
381390

382391
/**
383392
* Class to represent a query for a {@link PrefixTree}. Prefixes must be
384-
* separated by dots ('.'). E.g. 'math.add'. These queries are used in order
385-
* to specify the level where elements should be inserted into or retrieved
386-
* from the tree.
393+
* separated by dots ('.'). E.g. 'math.add'. These queries are used in order to
394+
* specify the level where elements should be inserted into or retrieved from
395+
* the tree.
387396
*/
388397
private static class PrefixQuery {
389398
String cachedToString;
@@ -395,8 +404,8 @@ public static PrefixQuery all() {
395404
}
396405

397406
/**
398-
* Construct a new query from the specified string. Prefixes must be
399-
* separated by dots.
407+
* Construct a new query from the specified string. Prefixes must be separated
408+
* by dots.
400409
*
401410
* @param query
402411
* the string to use as query
@@ -447,8 +456,8 @@ public String toString() {
447456
}
448457

449458
/**
450-
* Data structure to group elements which share common prefixes. E.g. adding
451-
* the following elements:
459+
* Data structure to group elements which share common prefixes. E.g. adding the
460+
* following elements:
452461
*
453462
* <pre>
454463
* Prefix: Elem:
@@ -483,8 +492,8 @@ public PrefixTree() {
483492
}
484493

485494
/**
486-
* Adds the specified element on the level represented by the specified
487-
* query. This method is in O(#number of prefixes in query)
495+
* Adds the specified element on the level represented by the specified query.
496+
* This method is in O(#number of prefixes in query)
488497
*
489498
* @param query
490499
* @param node
@@ -505,13 +514,12 @@ private void add(PrefixQuery query, PrefixNode<T> node, T data) {
505514
}
506515

507516
/**
508-
* Collects all elements of the level specified by the query and below.
509-
* E.g. using the query 'math' on the example tree from the javadoc of
510-
* this class would return all elements contained in the tree except for
511-
* 'obj5'. 'math.add' would only return 'obj1' and 'obj2'. This method
512-
* returns an iterable over these elements in O(# number of all nodes
513-
* below query). The number of nodes is the number of distinct prefixes
514-
* below the specified query.
517+
* Collects all elements of the level specified by the query and below. E.g.
518+
* using the query 'math' on the example tree from the javadoc of this class
519+
* would return all elements contained in the tree except for 'obj5'. 'math.add'
520+
* would only return 'obj1' and 'obj2'. This method returns an iterable over
521+
* these elements in O(# number of all nodes below query). The number of nodes
522+
* is the number of distinct prefixes below the specified query.
515523
*
516524
* @param query
517525
* @return
@@ -548,11 +556,11 @@ private void collectAll(PrefixNode<T> node, LinkedLinkedLists list) {
548556
}
549557

550558
/**
551-
* Wrapper for {@link ArrayList}s providing O(1) concatenation of lists
552-
* if only an iterator over theses lists is required. The order of lists
553-
* will be retained. Added lists will be simply saved in a super
554-
* LinkedList. If the iterator reaches the end of one list, it will
555-
* switch to the next if available.
559+
* Wrapper for {@link ArrayList}s providing O(1) concatenation of lists if only
560+
* an iterator over theses lists is required. The order of lists will be
561+
* retained. Added lists will be simply saved in a super LinkedList. If the
562+
* iterator reaches the end of one list, it will switch to the next if
563+
* available.
556564
*
557565
* @author David Kolb
558566
*/

0 commit comments

Comments
 (0)