3232import java .lang .reflect .Type ;
3333import java .util .ArrayList ;
3434import java .util .Arrays ;
35+ import java .util .EnumSet ;
3536import java .util .HashMap ;
3637import java .util .Iterator ;
3738import java .util .LinkedList ;
5354import org .scijava .ops .transform .OpTransformationCandidate ;
5455import org .scijava .ops .transform .OpTransformationException ;
5556import org .scijava .ops .transform .OpTransformerService ;
57+ import org .scijava .param .FunctionalMethodType ;
58+ import org .scijava .param .ParameterStructs ;
5659import org .scijava .plugin .Parameter ;
5760import org .scijava .plugin .Plugin ;
5861import org .scijava .plugin .PluginInfo ;
5962import org .scijava .plugin .PluginService ;
6063import org .scijava .service .AbstractService ;
6164import org .scijava .service .SciJavaService ;
6265import org .scijava .service .Service ;
66+ import org .scijava .struct .ItemIO ;
6367import org .scijava .ops .types .Nil ;
6468import org .scijava .util .ClassUtils ;
69+ import org .scijava .util .Types ;
6570
6671/**
6772 * Service to provide a list of available ops structured in a prefix tree and to
@@ -167,16 +172,71 @@ public Iterable<OpInfo> infos(String name) {
167172 public LogService logger () {
168173 return log ;
169174 }
175+
176+ /**
177+ * Attempts to inject {@link OpDependency} annotated fields of the specified object by
178+ * looking for Ops matching the field type and the name specified in the
179+ * annotation. The field type is assumed to be functional.
180+ *
181+ * @param obj
182+ * @throws OpMatchingException
183+ * if the type of the specified object is not functional,
184+ * if the Op matching the functional type and the name could not be found,
185+ * if an exception occurs during injection
186+ */
187+ public void resolveOpDependencies (Object obj ) throws OpMatchingException {
188+ final Class <?> c = obj .getClass ();
189+ final List <Field > opFields = ClassUtils .getAnnotatedFields (c , OpDependency .class );
190+
191+ for (final Field opField : opFields ) {
192+ final String opName = opField .getAnnotation (OpDependency .class ).name ();
193+ final Type fieldType = Types .fieldType (opField , c );
194+
195+ OpRef inferredRef = inferOpRef (fieldType , opName );
196+ if (inferredRef == null ) {
197+ throw new OpMatchingException ("Could not infer functional "
198+ + "method inputs and outputs of Op dependency field: "
199+ + opField );
200+ }
201+
202+ Object matchedOp = null ;
203+ try {
204+ matchedOp = findOpInstance (opName , inferredRef );
205+ } catch (Exception e ) {
206+ throw new OpMatchingException (
207+ "Could not find Op that matches requested Op dependency field:"
208+ + "\n Op class: " + c .getName ()
209+ + "\n Dependency field: " + opField .getName ()
210+ + "\n \n Attempted request:\n "
211+ + inferredRef , e );
212+ }
170213
214+ try {
215+ opField .setAccessible (true );
216+ opField .set (obj , matchedOp );
217+ } catch (IllegalArgumentException | IllegalAccessException e ) {
218+ throw new OpMatchingException (
219+ "Exception trying to inject Op dependency field.\n "
220+ + "\t Op dependency field to resolve: " + opField + "\n "
221+ + "\t Found Op to inject: " + matchedOp .getClass ().getName () + "\n "
222+ + "\t With inferred OpRef: " + inferredRef , e );
223+ }
224+ }
225+ }
226+
171227 @ SuppressWarnings ("unchecked" )
172228 public <T > T findOpInstance (final String opName , final Nil <T > specialType , final Nil <?>[] inTypes ,
173229 final Nil <?>[] outTypes , final Object ... secondaryArgs ) {
174230 final OpRef ref = OpRef .fromTypes (opName , toTypes (specialType ), toTypes (outTypes ), toTypes (inTypes ));
231+ return (T ) findOpInstance (opName , ref , secondaryArgs );
232+ }
175233
234+ public Object findOpInstance (final String opName , final OpRef ref , final Object ... secondaryArgs ) {
235+ Object op = null ;
176236 try {
177237 // Find single match which matches the specified types
178238 OpCandidate match = matcher .findSingleMatch (this , ref );
179- return ( T ) match .createOp (secondaryArgs );
239+ op = match .createOp (secondaryArgs );
180240 } catch (OpMatchingException e ) {
181241 log .debug ("No matching Op for request: " + ref + "\n " );
182242 log .debug ("Attempting Op transformation..." );
@@ -191,13 +251,20 @@ public <T> T findOpInstance(final String opName, final Nil<T> specialType, final
191251 // If we found one, try to do transformation and return transformed op
192252 log .debug ("Matching Op transformation found:\n " + transformation + "\n " );
193253 try {
194- return ( T ) transformation .exceute (this , secondaryArgs );
254+ op = transformation .exceute (this , secondaryArgs );
195255 } catch (OpMatchingException | OpTransformationException e1 ) {
196256 log .debug ("Execution of Op transformatioon failed:\n " );
197257 log .debug (e1 );
198- throw new IllegalArgumentException ( e );
258+
199259 }
200260 }
261+ try {
262+ // Try to resolve annotated OpDependency fields
263+ resolveOpDependencies (op );
264+ } catch (OpMatchingException e ) {
265+ throw new IllegalArgumentException (e );
266+ }
267+ return op ;
201268 }
202269
203270 public <T > T findOp (final String opName , final Nil <T > specialType , final Nil <?>[] inTypes , final Nil <?>[] outTypes ,
@@ -213,6 +280,39 @@ public <T> T findOp(final String opName, final Nil<T> specialType, final Nil<?>[
213280 private Type [] toTypes (Nil <?>... nils ) {
214281 return Arrays .stream (nils ).filter (n -> n != null ).map (n -> n .getType ()).toArray (Type []::new );
215282 }
283+
284+ /**
285+ * Tries to infer a {@link OpRef} from a functional Op type. E.g. the type:
286+ * <pre>Computer<Double[], Double[]></pre>
287+ * Will result in the following {@link OpRef}:
288+ * <pre>
289+ * Name: 'specified name'
290+ * Types: [Computer<Double, Double>]
291+ * InputTypes: [Double[], Double[]]
292+ * OutputTypes: [Double[]]
293+ * </pre>
294+ * Input and output types will be inferred by looking at the signature of the functional
295+ * method of the specified type. Also see {@link ParameterStructs#getFunctionalMethodTypes(Type)}.
296+ *
297+ * @param type
298+ * @param name
299+ * @return null if
300+ * the specified type has no functional method
301+ */
302+ private OpRef inferOpRef (Type type , String name ) {
303+ List <FunctionalMethodType > fmts = ParameterStructs .getFunctionalMethodTypes (type );
304+ if (fmts == null ) return null ;
305+
306+ EnumSet <ItemIO > inIos = EnumSet .of (ItemIO .BOTH , ItemIO .INPUT );
307+ EnumSet <ItemIO > outIos = EnumSet .of (ItemIO .BOTH , ItemIO .OUTPUT );
308+
309+ Type [] inputs = fmts .stream ().filter (fmt -> inIos .contains (fmt .itemIO ()))
310+ .map (fmt -> fmt .type ()).toArray (Type []::new );
311+ Type [] outputs = fmts .stream ().filter (fmt -> outIos .contains (fmt .itemIO ()))
312+ .map (fmt -> fmt .type ()).toArray (Type []::new );
313+
314+ return new OpRef (name , new Type []{type }, outputs , inputs );
315+ }
216316
217317 /**
218318 * Updates alias map using the specified String list. The first String in
0 commit comments