3434import java .lang .reflect .TypeVariable ;
3535import java .util .ArrayList ;
3636import java .util .Arrays ;
37+ import java .util .Comparator ;
3738import java .util .EnumSet ;
3839import java .util .HashMap ;
3940import java .util .Iterator ;
4041import java .util .LinkedList ;
4142import java .util .List ;
4243import java .util .Map ;
4344import java .util .Map .Entry ;
45+ import java .util .PriorityQueue ;
46+ import java .util .Queue ;
47+ import java .util .function .Function ;
4448import java .util .stream .Collectors ;
4549
4650import org .scijava .InstantiableException ;
5559import org .scijava .ops .matcher .OpMatcher ;
5660import org .scijava .ops .matcher .OpMatchingException ;
5761import org .scijava .ops .matcher .OpRef ;
58- import org .scijava .ops .transform .DefaultOpTransformationMatcher ;
62+ import org .scijava .ops .transform .AdaptedOp ;
5963import org .scijava .ops .transform .OpRunner ;
60- import org .scijava .ops .transform .OpTransformationCandidate ;
61- import org .scijava .ops .transform .OpTransformationException ;
6264import org .scijava .ops .transform .OpTransformationMatcher ;
6365import org .scijava .ops .transform .OpTransformer ;
6466import org .scijava .ops .types .Nil ;
@@ -212,12 +214,12 @@ private synchronized List<OpTransformer> getTransformerIndex() {
212214 return transformerIndex ;
213215 }
214216
215- private OpTransformationMatcher getTransformationMatcher () {
216- if (transformationMatcher == null ) {
217- transformationMatcher = new DefaultOpTransformationMatcher (getOpMatcher ());
218- }
219- return transformationMatcher ;
220- }
217+ // private OpTransformationMatcher getTransformationMatcher() {
218+ // if (transformationMatcher == null) {
219+ // transformationMatcher = new DefaultOpTransformationMatcher(getOpMatcher());
220+ // }
221+ // return transformationMatcher;
222+ // }
221223
222224 /**
223225 * Attempts to inject {@link OpDependency} annotated fields of the specified
@@ -265,49 +267,115 @@ public <T> T findOpInstance(final String opName, final Nil<T> specialType, final
265267 public Object findOpInstance (final String opName , final OpRef ref , boolean adaptable ) {
266268 Object op = null ;
267269 OpCandidate match = null ;
268- OpTransformationCandidate transformation = null ;
270+ AdaptedOp adaptation = null ;
269271 try {
270272 // Find single match which matches the specified types
271273 match = getOpMatcher ().findSingleMatch (this , ref );
272274 final List <Object > dependencies = resolveOpDependencies (match );
273275 op = match .createOp (dependencies );
274276 } catch (OpMatchingException e ) {
275277 log .debug ("No matching Op for request: " + ref + "\n " );
276- if (adaptable == false ) {
278+ if (adaptable == false ) {
277279 throw new IllegalArgumentException (opName + " cannot be adapted (adaptation is disabled)" );
278280 }
279- log .debug ("Attempting Op transformation..." );
280-
281- // If we can't find an op matching the original request, we try to find a
282- // transformation
283- transformation = getTransformationMatcher ().findTransformation (this , getTransformerIndex (), ref );
284- if (transformation == null ) {
285- log .debug ("No matching Op transformation found" );
286- throw new IllegalArgumentException (e );
287- }
288-
289- // If we found one, try to do transformation and return transformed op
290- log .debug ("Matching Op transformation found:\n " + transformation + "\n " );
281+ log .debug ("Attempting Op adaptation..." );
291282 try {
292- final List <Object > dependencies = resolveOpDependencies (transformation .getSourceOp ());
293- op = transformation .exceute (this , dependencies );
294- } catch (OpMatchingException | OpTransformationException e1 ) {
295- throw new IllegalArgumentException ("Execution of Op transformatioon failed:\n " + e1 );
283+ adaptation = adaptOp (opName , ref );
284+ op = adaptation .op ();
285+ } catch (OpMatchingException e1 ) {
286+ log .debug ("No suitable Op adaptation found" );
287+ throw new IllegalArgumentException (e1 );
296288 }
289+
297290 }
298291 try {
299292 // Try to resolve annotated OpDependency fields
300293 if (match != null )
301294 resolveOpDependencies (match );
302- else if (transformation != null )
303- resolveOpDependencies (transformation .getSourceOp ());
304295 } catch (OpMatchingException e ) {
305296 throw new IllegalArgumentException (e );
306297 }
307- Object wrappedOp = wrapOp (op , match , transformation );
298+ OpInfo adaptedInfo = adaptation == null ? null : adaptation .srcInfo ();
299+ Object wrappedOp = wrapOp (op , match , adaptedInfo );
308300 return wrappedOp ;
309301 }
310302
303+ public AdaptedOp adaptOp (String opName , OpRef ref ) throws OpMatchingException {
304+
305+ // TODO: support all types of ref
306+ // TODO: multi-stage adaptations (do we need this if we are not doing OpRunners
307+ // anymore?)
308+ // TODO: prevent searching for Op types that have already been searched for
309+ // TODO: export code to helper method.
310+ Type opType = ref .getTypes ()[0 ];
311+ List <OpInfo > adaptors = opCache .get ("adapt" );
312+
313+ // create a priority queue to store suitable transformations.
314+ Comparator <OpCandidate > comp = (OpCandidate i1 ,
315+ OpCandidate i2 ) -> i1 .opInfo ().priority () < i2 .opInfo ().priority () ? -1
316+ : i1 .opInfo ().priority () == i2 .opInfo ().priority () ? 0 : 1 ;
317+ Queue <OpCandidate > suitableAdaptors = new PriorityQueue <>(comp );
318+
319+ // create an OpCandidate list of suitable adaptors
320+ for (OpInfo adaptor : adaptors ) {
321+ Type adaptTo = adaptor .output ().getType ();
322+ Map <TypeVariable <?>, Type > map = new HashMap <>();
323+ // make sure that the adaptor outputs the correct type
324+ if (!Types .isAssignable (opType , adaptTo , map ))
325+ continue ;
326+ // make sure that the adaptor is a Function (so we can cast it later)
327+ if (Types .isInstance (adaptor .opType (), Function .class )) {
328+ log .debug (adaptor + " is an illegal adaptor Op: must be a Function" );
329+ continue ;
330+ }
331+ // build the type of fromOp (we know there must be one input because the adaptor
332+ // is a Function)
333+ Type adaptFrom = adaptor .inputs ().get (0 ).getType ();
334+ Type refAdaptTo = Types .substituteTypeVariables (adaptTo , map );
335+ Type refAdaptFrom = Types .substituteTypeVariables (adaptFrom , map );
336+
337+ // build the OpRef of the adaptor.
338+ Type refType = Types .parameterize (Function .class , new Type [] { refAdaptFrom , refAdaptTo });
339+ OpRef adaptorRef = new OpRef ("adapt" , new Type [] { refType }, refAdaptTo , new Type [] { refAdaptFrom });
340+
341+ // make an OpCandidate
342+ suitableAdaptors .add (new OpCandidate (this , log , adaptorRef , adaptor , map ));
343+ }
344+
345+ while (suitableAdaptors .size () > 0 ) {
346+ OpCandidate adaptor = suitableAdaptors .remove ();
347+ try {
348+ // resolve adaptor dependencies and get the adaptor (as a function) //TODO
349+ final List <Object > dependencies = resolveOpDependencies (adaptor );
350+ // adaptor.setStatus(StatusCode.MATCH);
351+ Object adaptorOp = adaptor .opInfo ().createOpInstance (dependencies ).object ();
352+
353+ // grab the first type parameter (from the OpCandidate?) and search for an Op
354+ // that will then be adapted (this will be the first (only) type in the args of
355+ // the adaptor)
356+ Type adaptFrom = adaptor .paddedArgs ()[0 ];
357+ final OpRef inferredRef = inferOpRef (adaptFrom , opName , adaptor .typeVarAssigns ());
358+ // TODO: export this to another function (also done in findOpInstance).
359+ // We need this here because we need to grab the OpInfo.
360+ // TODO: is there a better way to do this?
361+ final OpCandidate srcCandidate = getOpMatcher ().findSingleMatch (this , inferredRef );
362+ final List <Object > srcDependencies = resolveOpDependencies (srcCandidate );
363+ final Object fromOp = srcCandidate .opInfo ().createOpInstance (srcDependencies ).object ();
364+
365+ // get adapted Op by applying adaptor on unadapted Op, then return
366+ // TODO: can we make this safer?
367+ @ SuppressWarnings ("unchecked" )
368+ Object toOp = ((Function <Object , Object >) adaptorOp ).apply (fromOp );
369+ return new AdaptedOp (toOp , srcCandidate .opInfo (), adaptor .opInfo ());
370+ } catch (OpMatchingException e1 ) {
371+ continue ;
372+ }
373+
374+ }
375+ // no adaptors available.
376+ throw new OpMatchingException ("Op adaptation failed: no adaptable Ops of type " + opName );
377+ }
378+
311379 /**
312380 * Wraps the matched op into an {@link Op} that knows its generic typing and
313381 * {@link OpInfo}.
@@ -322,15 +390,15 @@ else if (transformation != null)
322390 * needed.
323391 * @return an {@link Op} wrapping of op.
324392 */
325- private Object wrapOp (Object op , OpCandidate match , OpTransformationCandidate transformation ) {
393+ private Object wrapOp (Object op , OpCandidate match , OpInfo adaptationSrcInfo ) {
326394 if (wrappers == null )
327395 initWrappers ();
328396
329397 // TODO: we don't want to wrap OpRunners, do we? What is the point?
330398 if (OpRunner .class .isInstance (op ))
331399 return op ;
332400
333- OpInfo opInfo = match == null ? transformation . getSourceOp (). opInfo () : match .opInfo ();
401+ OpInfo opInfo = match == null ? adaptationSrcInfo : match .opInfo ();
334402 // FIXME: this type is not necessarily Computer, Function, etc. but often
335403 // something more specific (like the class of an Op).
336404 Type type = opInfo .opType ();
0 commit comments