2929package org .scijava .ops ;
3030
3131import java .lang .reflect .Field ;
32+ import java .lang .reflect .InvocationTargetException ;
33+ import java .lang .reflect .Modifier ;
3234import java .lang .reflect .Type ;
3335import java .lang .reflect .TypeVariable ;
3436import java .util .ArrayList ;
4547import org .scijava .log .LogService ;
4648import org .scijava .ops .core .Op ;
4749import org .scijava .ops .core .OpCollection ;
50+ import org .scijava .ops .core .computer .ComputerOps .BiComputerOp ;
51+ import org .scijava .ops .core .computer .ComputerOps .Computer3Op ;
52+ import org .scijava .ops .core .computer .ComputerOps .Computer4Op ;
53+ import org .scijava .ops .core .computer .ComputerOps .Computer5Op ;
54+ import org .scijava .ops .core .computer .ComputerOps .Computer6Op ;
55+ import org .scijava .ops .core .computer .ComputerOps .Computer7Op ;
56+ import org .scijava .ops .core .computer .ComputerOps .Computer8Op ;
57+ import org .scijava .ops .core .computer .ComputerOps .ComputerOp ;
58+ import org .scijava .ops .core .function .FunctionOps .BiFunctionOp ;
59+ import org .scijava .ops .core .function .FunctionOps .Function3Op ;
60+ import org .scijava .ops .core .function .FunctionOps .Function4Op ;
61+ import org .scijava .ops .core .function .FunctionOps .Function5Op ;
62+ import org .scijava .ops .core .function .FunctionOps .Function6Op ;
63+ import org .scijava .ops .core .function .FunctionOps .Function7Op ;
64+ import org .scijava .ops .core .function .FunctionOps .Function8Op ;
65+ import org .scijava .ops .core .function .FunctionOps .Function9Op ;
66+ import org .scijava .ops .core .function .FunctionOps .FunctionOp ;
67+ import org .scijava .ops .core .function .SourceOp ;
68+ import org .scijava .ops .core .inplace .InplaceOps .BiInplaceFirstOp ;
69+ import org .scijava .ops .core .inplace .InplaceOps .BiInplaceSecondOp ;
70+ import org .scijava .ops .core .inplace .InplaceOps .Inplace3FirstOp ;
71+ import org .scijava .ops .core .inplace .InplaceOps .Inplace3SecondOp ;
72+ import org .scijava .ops .core .inplace .InplaceOps .Inplace3ThirdOp ;
73+ import org .scijava .ops .core .inplace .InplaceOps .Inplace4FirstOp ;
74+ import org .scijava .ops .core .inplace .InplaceOps .Inplace5FirstOp ;
75+ import org .scijava .ops .core .inplace .InplaceOps .Inplace6FirstOp ;
76+ import org .scijava .ops .core .inplace .InplaceOps .Inplace7SecondOp ;
77+ import org .scijava .ops .core .inplace .InplaceOps .InplaceOp ;
4878import org .scijava .ops .matcher .OpCandidate ;
4979import org .scijava .ops .matcher .OpClassInfo ;
5080import org .scijava .ops .matcher .OpFieldInfo ;
@@ -107,6 +137,46 @@ public class OpService extends AbstractService implements SciJavaService, OpEnvi
107137 */
108138 private Map <String , String > opAliases = new HashMap <>();
109139
140+ private static Map <Class <?>, Class <?>> wrappers = wrappers ();
141+
142+ private static Map <Class <?>, Class <?>> wrappers () {
143+ final Map <Class <?>, Class <?>> result = new HashMap <>();
144+ final Class <?>[] wrapperClasses = { //
145+ FunctionOp .class , //
146+ BiFunctionOp .class , //
147+ Function3Op .class , //
148+ Function4Op .class , //
149+ Function5Op .class , //
150+ Function6Op .class , //
151+ Function7Op .class , //
152+ Function8Op .class , //
153+ Function9Op .class , //
154+ ComputerOp .class , //
155+ BiComputerOp .class , //
156+ Computer3Op .class , //
157+ Computer4Op .class , //
158+ Computer5Op .class , //
159+ Computer6Op .class , //
160+ Computer7Op .class , //
161+ Computer8Op .class , //
162+ InplaceOp .class , //
163+ BiInplaceFirstOp .class , //
164+ BiInplaceSecondOp .class , //
165+ Inplace3FirstOp .class , //
166+ Inplace3SecondOp .class , //
167+ Inplace3ThirdOp .class , //
168+ Inplace4FirstOp .class , //
169+ Inplace5FirstOp .class , //
170+ Inplace6FirstOp .class , //
171+ Inplace7SecondOp .class , //
172+ SourceOp .class
173+ };
174+ for (final Class <?> c : wrapperClasses ) {
175+ result .put (c .getInterfaces ()[0 ], c );
176+ }
177+ return result ;
178+ }
179+
110180 public void initOpCache () {
111181 opCache = new PrefixTree <>();
112182
@@ -124,12 +194,18 @@ public void initOpCache() {
124194 // Add Ops contained in an OpCollection
125195 for (final PluginInfo <OpCollection > pluginInfo : pluginService .getPluginsOfType (OpCollection .class )) {
126196 try {
127- final List <Field > fields = ClassUtils .getAnnotatedFields (pluginInfo .loadClass (), OpField .class );
197+ Class <? extends OpCollection > c = pluginInfo .loadClass ();
198+ final List <Field > fields = ClassUtils .getAnnotatedFields (c , OpField .class );
199+ Object instance = null ;
128200 for (Field field : fields ) {
129- OpInfo opInfo = new OpFieldInfo (field );
201+ final boolean isStatic = Modifier .isStatic (field .getModifiers ());
202+ if (!isStatic && instance == null ) {
203+ instance = field .getDeclaringClass ().newInstance ();
204+ }
205+ OpInfo opInfo = new OpFieldInfo (isStatic ? null : instance , field );
130206 addToCache (opInfo , field .getAnnotation (OpField .class ).names ());
131207 }
132- } catch (InstantiableException exc ) {
208+ } catch (InstantiableException | InstantiationException | IllegalAccessException exc ) {
133209 log .error ("Can't load class from plugin info: " + pluginInfo .toString (), exc );
134210 }
135211 }
@@ -268,7 +344,53 @@ else if (transformation != null)
268344 } catch (OpMatchingException e ) {
269345 throw new IllegalArgumentException (e );
270346 }
271- return op ;
347+ Object wrappedOp = wrapOp (op , match , transformation );
348+ return wrappedOp ;
349+ }
350+
351+ /**
352+ * Wraps the matched op into an {@link Op} that knows its generic typing and
353+ * {@link OpInfo}.
354+ *
355+ * @param op
356+ * - the matched op to wrap.
357+ * @param match
358+ * - where to retrieve the {@link OpInfo} if no transformation is
359+ * needed.
360+ * @param transformation
361+ * - where to retrieve the {@link OpInfo} if a transformation is
362+ * needed.
363+ * @return an {@link Op} wrapping of op.
364+ */
365+ private Object wrapOp (Object op , OpCandidate match , OpTransformationCandidate transformation ) {
366+ // TODO: we don't want to wrap OpRunners, do we? What is the point?
367+ if (OpRunner .class .isInstance (op )) return op ;
368+
369+ OpInfo opInfo = match == null ? transformation .getSourceOp ().opInfo () : match .opInfo ();
370+ // FIXME: this type is not necessarily Computer, Function, etc. but often
371+ // something more specific (like the class of an Op).
372+ Type type = opInfo .opType ();
373+ try {
374+ // determine the Op wrappers that could wrap the matched Op
375+ Class <?>[] suitableWrappers = wrappers .keySet ().stream ().filter (wrapper -> wrapper .isInstance (op ))
376+ .toArray (Class []::new );
377+ if (suitableWrappers .length == 0 )
378+ throw new IllegalArgumentException (opInfo .implementationName () + ": matched op Type " + type .getClass ()
379+ + " does not match a wrappable Op type." );
380+ if (suitableWrappers .length > 1 )
381+ throw new IllegalArgumentException (
382+ "Matched op Type " + type .getClass () + " matches multiple Op types: " + wrappers .toString ());
383+ // get the wrapper and wrap up the Op
384+ Class <?> wrapper = wrappers .get (suitableWrappers [0 ]);
385+ return wrapper .getConstructors ()[0 ].newInstance (op , type , opInfo );
386+ } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
387+ | SecurityException exc ) {
388+ log .error (exc .getMessage () != null ? exc .getMessage () : "Cannot wrap " + op .getClass ());
389+ return op ;
390+ } catch (NullPointerException e ) {
391+ log .error ("No wrapper exists for " + Types .raw (type ).toString () + "." );
392+ return op ;
393+ }
272394 }
273395
274396 public <T > T findOp (final String opName , final Nil <T > specialType , final Nil <?>[] inTypes , final Nil <?> outType ) {
@@ -358,9 +480,9 @@ private void addAliases(String[] opNames, String opImpl) {
358480 }
359481 if (opAliases .containsKey (alias )) {
360482 if (!opAliases .get (alias ).equals (opName )) {
361- log .warn ("Possible naming clash for op '" + opImpl + "' detected. Attempting to add alias '" + alias
362- + "' for op name '" + opName + "'. However the alias '" + alias + "' is already "
363- + "associated with op name '" + opAliases .get (alias ) + "'." );
483+ // log.warn("Possible naming clash for op '" + opImpl + "' detected. Attempting to add alias '" + alias
484+ // + "' for op name '" + opName + "'. However the alias '" + alias + "' is already "
485+ // + "associated with op name '" + opAliases.get(alias) + "'.");
364486 }
365487 continue ;
366488 }
0 commit comments