3333import java .lang .reflect .Type ;
3434import java .lang .reflect .TypeVariable ;
3535import java .util .*;
36- import java .util .function .Consumer ;
37- import java .util .function .Function ;
36+ import java .util .function .BiConsumer ;
3837import java .util .stream .Collectors ;
3938
4039import org .scijava .Priority ;
4140import org .scijava .discovery .Discoverer ;
4241import org .scijava .log2 .Logger ;
4342import org .scijava .ops .api .*;
44- import org .scijava .ops .api .OpCandidate .StatusCode ;
4543import org .scijava .ops .api .features .*;
4644import org .scijava .ops .api .features .BaseOpHints .Adaptation ;
4745import org .scijava .ops .api .features .BaseOpHints .DependencyMatching ;
4846import org .scijava .ops .api .features .BaseOpHints .History ;
4947import org .scijava .ops .api .features .BaseOpHints .Simplification ;
5048import org .scijava .ops .engine .hint .DefaultHints ;
51- import org .scijava .ops .engine .matcher .impl .*;
52- import org .scijava .ops .engine .simplify .SimplifiedOpInfo ;
49+ import org .scijava .ops .engine .matcher .impl .DefaultOpMatcher ;
50+ import org .scijava .ops .engine .matcher .impl .DefaultOpRef ;
51+ import org .scijava .ops .engine .matcher .impl .InfoMatchingOpRef ;
52+ import org .scijava .ops .engine .matcher .impl .OpClassInfo ;
5353import org .scijava .ops .engine .struct .FunctionalParameters ;
5454import org .scijava .ops .spi .Op ;
5555import org .scijava .ops .spi .OpCollection ;
5959import org .scijava .types .Nil ;
6060import org .scijava .types .TypeReifier ;
6161import org .scijava .types .Types ;
62- import org .scijava .types .inference .GenericAssignability ;
6362import org .scijava .util .VersionUtils ;
6463
6564/**
@@ -72,18 +71,13 @@ public class DefaultOpEnvironment implements OpEnvironment {
7271
7372 private final List <Discoverer > discoverers ;
7473
75- private OpMatcher matcher ;
74+ private final OpMatcher matcher ;
7675
77- private Logger log ;
76+ private final Logger log ;
7877
79- private TypeReifier typeService ;
78+ private final TypeReifier typeService ;
8079
81- private OpHistory history ;
82-
83- /**
84- * The {@link OpInfoGenerator}s providing {@link OpInfo}s to this environment
85- */
86- private List <OpInfoGenerator > infoGenerators ;
80+ private final OpHistory history ;
8781
8882 /**
8983 * Data structure storing all known Ops, grouped by name. This reduces the
@@ -99,7 +93,7 @@ public class DefaultOpEnvironment implements OpEnvironment {
9993
10094 /**
10195 * Map containing pairs of {@link MatchingConditions} (i.e. the {@link OpRef}
102- * and {@Hints} used to find an Op) and the {@link OpInstance} (wrapping an Op
96+ * and {@link Hints} used to find an Op) and the {@link OpInstance} (wrapping an Op
10397 * with its backing {@link OpInfo}) that matched those requests. Used to
10498 * quickly return Ops when the matching conditions are identical to those of a
10599 * previous call.
@@ -152,7 +146,7 @@ public static List<MatchingRoutine> getMatchingRoutines(
152146 @ Override
153147 public Set <OpInfo > infos () {
154148 if (opDirectory == null ) initOpDirectory ();
155- return opDirectory .values ().stream ().flatMap (list -> list . stream () ).collect (
149+ return opDirectory .values ().stream ().flatMap (Collection :: stream ).collect (
156150 Collectors .toSet ());
157151 }
158152
@@ -291,7 +285,15 @@ public <T> T bakeLambdaType(T op, Type type) {
291285 @ Override
292286 public void register (final OpInfo info ) {
293287 if (opDirectory == null ) initOpDirectory ();
294- addToOpIndex .accept (info );
288+ addToOpIndex .accept (info , log );
289+ }
290+
291+ @ Override
292+ public void registerInfosFrom (Object o ) {
293+ List <OpInfo > infos = discoverers .parallelStream () //
294+ .flatMap (d -> d .discover (OpInfoGenerator .class ).stream ()) //
295+ .flatMap (g -> g .generateInfosFrom (o ).stream ()).collect (Collectors .toList ());
296+ infos .forEach (this ::register );
295297 }
296298
297299 @ SuppressWarnings ("unchecked" )
@@ -313,17 +315,11 @@ private <T> OpInstance<T> findOp(final OpInfo info, final Nil<T> specialType,
313315 return (OpInstance <T >) getInstance (conditions );
314316 }
315317
316- @ SuppressWarnings ("unchecked" )
317- private <T > RichOp <T > findRichOp (final OpInfo info , final Nil <T > specialType ,
318- Hints hints ) throws OpMatchingException
319- {
320- OpInstance <T > instance = findOp (info , specialType , hints );
321- return (RichOp <T >) wrap (instance , hints );
322- }
323-
324318 private Type [] toTypes (Nil <?>... nils ) {
325- return Arrays .stream (nils ).filter (n -> n != null ).map (n -> n .getType ())
326- .toArray (Type []::new );
319+ return Arrays .stream (nils ) //
320+ .filter (Objects ::nonNull ) //
321+ .map (Nil ::getType ) //
322+ .toArray (Type []::new );
327323 }
328324
329325 /**
@@ -338,7 +334,6 @@ private Type[] toTypes(Nil<?>... nils) {
338334 * conditions1
339335 * @return the {@link MatchingConditions} that will return the Op described by
340336 * {@code info} from the op cache
341- * @throws OpMatchingException
342337 */
343338 private MatchingConditions insertCacheHit (final OpRef ref , final Hints hints ,
344339 final OpInfo info )
@@ -360,8 +355,7 @@ private RichOp<?> wrapViaCache(MatchingConditions conditions) {
360355 }
361356
362357 private RichOp <?> wrap (OpInstance <?> instance , Hints hints ) {
363- RichOp <?> wrappedOp = wrapOp (instance , hints );
364- return wrappedOp ;
358+ return wrapOp (instance , hints );
365359 }
366360
367361 /**
@@ -421,7 +415,8 @@ private OpCandidate findOpCandidate(OpRef ref, Hints hints) {
421415 * Creates an instance of the Op from the {@link OpCandidate} <b>with its
422416 * required {@link OpDependency} fields</b>.
423417 *
424- * @param candidate
418+ * @param candidate the {@link OpCandidate} to be instantiated
419+ * @param hints the {@link Hints} to use in instantiation
425420 * @return an Op with all needed dependencies
426421 */
427422 private OpInstance <?> instantiateOp (final OpCandidate candidate ,
@@ -480,10 +475,10 @@ private Class<?> getWrapperClass(Object op, OpInfo info) {
480475 " does not match a wrappable Op type." );
481476 if (filteredWrappers .size () > 1 ) throw new IllegalArgumentException (
482477 "Matched op Type " + info .opType ().getClass () +
483- " matches multiple Op types: " + filteredWrappers . toString () );
478+ " matches multiple Op types: " + filteredWrappers );
484479 if (!Types .isAssignable (Types .raw (info .opType ()), filteredWrappers .get (0 )))
485480 throw new IllegalArgumentException (Types .raw (info .opType ()) +
486- "cannot be wrapped as a " + filteredWrappers .get (0 ). getClass () );
481+ "cannot be wrapped as a " + filteredWrappers .get (0 ));
487482 return filteredWrappers .get (0 );
488483 }
489484
@@ -563,125 +558,14 @@ private List<RichOp<?>> resolveOpDependencies(OpInfo info,
563558 return dependencyChains ;
564559 }
565560
566- /**
567- * Adapts an Op with the name of ref into a type that can be SAFELY cast to
568- * ref.
569- * <p>
570- * NB This method <b>cannot</b> use the {@link OpMatcher} to find a suitable
571- * {@code adapt} Op. The premise of adaptation depends on the ability to
572- * examine the applicability of all {@code adapt} Ops with the correct output
573- * type. We need to check all of them because we do not know whether:
574- * <ul>
575- * <li>The dependencies will exist for a particular {@code adapt} Op
576- * <li>The Op we want exists with the correct type for the input of the
577- * {@code adapt} Op.
578- * </ul>
579- *
580- * @param ref - the type of Op that we are looking to adapt to.
581- * @return {@link OpCandidate} - an Op that has been adapted to conform the
582- * the ref type (if one exists).
583- */
584- private OpCandidate adaptOp (OpRef ref , Hints hints ) {
585-
586- List <DependencyMatchingException > depExceptions = new ArrayList <>();
587- for (final OpInfo adaptor : infos ("adapt" )) {
588- Type adaptTo = adaptor .output ().getType ();
589- Map <TypeVariable <?>, Type > map = new HashMap <>();
590- // make sure that the adaptor outputs the correct type
591- if (!adaptOpOutputSatisfiesRefTypes (adaptTo , map , ref )) continue ;
592- // make sure that the adaptor is a Function (so we can cast it later)
593- if (Types .isInstance (adaptor .opType (), Function .class )) {
594- log .debug (adaptor + " is an illegal adaptor Op: must be a Function" );
595- continue ;
596- }
597-
598- if (adaptor instanceof SimplifiedOpInfo ) {
599- log .debug (adaptor + " has been simplified. This is likely a typo." );
600- }
601-
602- try {
603- // resolve adaptor dependencies
604- final List <RichOp <?>> dependencies = resolveOpDependencies (adaptor , map ,
605- hints );
606- InfoChain adaptorChain = new DependencyRichOpInfoChain (adaptor ,
607- dependencies );
608-
609- // grab the first type parameter from the OpInfo and search for
610- // an Op that will then be adapted (this will be the only input of the
611- // adaptor since we know it is a Function)
612- Type srcOpType = Types .substituteTypeVariables (adaptor .inputs ().get (0 )
613- .getType (), map );
614- final OpRef srcOpRef = inferOpRef (srcOpType , ref .getName (), map );
615- final OpCandidate srcCandidate = findAdaptationCandidate (srcOpRef ,
616- hints );
617- map .putAll (srcCandidate .typeVarAssigns ());
618- Type adapterOpType = Types .substituteTypeVariables (adaptor .output ()
619- .getType (), map );
620- OpAdaptationInfo adaptedInfo = new OpAdaptationInfo (srcCandidate
621- .opInfo (), adapterOpType , adaptorChain );
622- OpCandidate adaptedCandidate = new OpCandidate (this , ref , adaptedInfo ,
623- map );
624- adaptedCandidate .setStatus (StatusCode .MATCH );
625- return adaptedCandidate ;
626- }
627- catch (DependencyMatchingException d ) {
628- depExceptions .add (d );
629- }
630- catch (OpMatchingException e1 ) {
631- log .trace (e1 );
632- }
633- }
634-
635- // no adaptors available.
636- if (depExceptions .size () == 0 ) {
637- throw new OpMatchingException (
638- "Op adaptation failed: no adaptable Ops of type " + ref .getName ());
639- }
640- StringBuilder sb = new StringBuilder ();
641- for (DependencyMatchingException d : depExceptions ) {
642- sb .append ("\n \n " + d .getMessage ());
643- }
644- throw new DependencyMatchingException (sb .toString ());
645- }
646-
647- private OpCandidate findAdaptationCandidate (final OpRef srcOpRef ,
648- final Hints hints )
649- {
650- Hints adaptationHints = hints .plus (Adaptation .IN_PROGRESS );
651- final OpCandidate srcCandidate = findOpCandidate (srcOpRef , adaptationHints );
652- return srcCandidate ;
653- }
654-
655- private boolean adaptOpOutputSatisfiesRefTypes (Type adaptTo ,
656- Map <TypeVariable <?>, Type > map , OpRef ref )
657- {
658- Type opType = ref .getType ();
659- // TODO: clean this logic -- can this just be ref.typesMatch() ?
660- if (opType instanceof ParameterizedType ) {
661- if (!GenericAssignability .checkGenericAssignability (adaptTo ,
662- (ParameterizedType ) opType , map , true ))
663- {
664- return false ;
665- }
666- }
667- else if (!Types .isAssignable (opType , adaptTo , map )) {
668- return false ;
669- }
670- return true ;
671- }
672-
673561 private OpRef inferOpRef (OpDependencyMember <?> dependency ,
674562 Map <TypeVariable <?>, Type > typeVarAssigns )
675563 {
676564 final Type mappedDependencyType = Types .mapVarToTypes (new Type [] {
677565 dependency .getType () }, typeVarAssigns )[0 ];
678566 final String dependencyName = dependency .getDependencyName ();
679- final OpRef inferredRef = inferOpRef (mappedDependencyType , dependencyName ,
567+ return inferOpRef (mappedDependencyType , dependencyName ,
680568 typeVarAssigns );
681- if (inferredRef != null ) return inferredRef ;
682- throw new OpMatchingException ("Could not infer functional " +
683- "method inputs and outputs of Op dependency field: " + dependency
684- .getKey ());
685569 }
686570
687571 /**
@@ -704,23 +588,25 @@ private OpRef inferOpRef(OpDependencyMember<?> dependency,
704588 * functional method of the specified type. Also see
705589 * {@link FunctionalParameters#findFunctionalMethodTypes(Type)}.
706590 *
707- * @param type
708- * @param name
591+ * @param type the functional {@link Type} of the {@code op} we're looking for
592+ * @param name the name of the {@code op} we're looking for
593+ * @param typeVarAssigns the mappings of {@link TypeVariable}s to {@link Type}s
709594 * @return null if the specified type has no functional method
710595 */
711- private OpRef inferOpRef (Type type , String name , Map <TypeVariable <?>, Type > typeVarAssigns )
712- {
596+ private OpRef inferOpRef (Type type , String name , Map <TypeVariable <?>, Type > typeVarAssigns ) {
713597 List <FunctionalMethodType > fmts = FunctionalParameters .findFunctionalMethodTypes (type );
714- if (fmts == null )
715- return null ;
716598
717599 EnumSet <ItemIO > inIos = EnumSet .of (ItemIO .INPUT , ItemIO .CONTAINER , ItemIO .MUTABLE );
718600 EnumSet <ItemIO > outIos = EnumSet .of (ItemIO .OUTPUT , ItemIO .CONTAINER , ItemIO .MUTABLE );
719601
720- Type [] inputs = fmts .stream ().filter (fmt -> inIos .contains (fmt .itemIO ())).map (fmt -> fmt .type ())
602+ Type [] inputs = fmts .stream () //
603+ .filter (fmt -> inIos .contains (fmt .itemIO ())) //
604+ .map (FunctionalMethodType ::type ) //
721605 .toArray (Type []::new );
722606
723- Type [] outputs = fmts .stream ().filter (fmt -> outIos .contains (fmt .itemIO ())).map (fmt -> fmt .type ())
607+ Type [] outputs = fmts .stream () //
608+ .filter (fmt -> outIos .contains (fmt .itemIO ())) //
609+ .map (FunctionalMethodType ::type ) //
724610 .toArray (Type []::new );
725611
726612 Type [] mappedInputs = Types .mapVarToTypes (inputs , typeVarAssigns );
@@ -742,7 +628,7 @@ private synchronized void initOpDirectory() {
742628 if (opDirectory != null ) return ;
743629 opDirectory = new HashMap <>();
744630 // add all OpInfos that are directly discoverable
745- discoverers .stream ().flatMap (d -> d .discover (OpInfo .class ).stream ()).forEach (addToOpIndex );
631+ discoverers .stream ().flatMap (d -> d .discover (OpInfo .class ).stream ()).forEach (info -> addToOpIndex . accept ( info , log ) );
746632 List <OpInfoGenerator > generators = infoGenerators ();
747633 discoverers .stream ().flatMap (d -> d .discover (Op .class ).stream ()).forEach (o -> registerOpsFrom (o , generators ));
748634 discoverers .stream ().flatMap (d -> d .discover (OpCollection .class ).stream ()).forEach (o -> registerOpsFrom (o , generators ));
@@ -762,7 +648,7 @@ private List<OpInfo> opsFromObject(Object o, List<OpInfoGenerator> generators) {
762648 }
763649
764650 private void registerOpsFrom (Object o , List <OpInfoGenerator > generators ) {
765- opsFromObject (o , generators ).stream (). forEach (addToOpIndex );
651+ opsFromObject (o , generators ).forEach (info -> addToOpIndex . accept ( info , log ) );
766652 }
767653
768654 private List <OpInfoGenerator > infoGenerators () {
@@ -776,12 +662,12 @@ private synchronized void initIdDirectory() {
776662 idDirectory = new HashMap <>();
777663 if (opDirectory == null ) initOpDirectory ();
778664
779- opDirectory .values ().stream (). flatMap ( c -> c . stream ()). forEach ( info -> {
780- idDirectory . put ( info . id (), info );
781- } );
665+ opDirectory .values ().stream () //
666+ . flatMap ( Collection :: stream ) //
667+ . forEach ( info -> idDirectory . put ( info . id (), info ) );
782668 }
783669
784- private final Consumer <OpInfo > addToOpIndex = (final OpInfo opInfo ) -> {
670+ private final BiConsumer <OpInfo , Logger > addToOpIndex = (final OpInfo opInfo , final Logger log ) -> {
785671 if (opInfo .names () == null || opInfo .names ().size () == 0 ) {
786672 log .error ("Skipping Op " + opInfo .implementationName () + ":\n " +
787673 "Op implementation must provide name." );
@@ -806,13 +692,13 @@ private Set<OpInfo> opsOfName(final String name) {
806692 }
807693
808694 /**
809- * Sets the default {@Hints} used for finding Ops.
695+ * Sets the default {@link Hints} used for finding Ops.
810696 * <p>
811697 * Note that this method is <b>not</b> thread safe and is provided for
812- * convenience. If the user wishes to use {@Hints} in a thread-safe manner,
698+ * convenience. If the user wishes to use {@link Hints} in a thread-safe manner,
813699 * they should use
814700 * {@link DefaultOpEnvironment#op(String, Nil, Nil[], Nil, Hints)} if using
815- * different {@Hint}s for different calls. Alternatively, this method can be
701+ * different {@link Hints} for different calls. Alternatively, this method can be
816702 * called before all Ops called in parallel without issues.
817703 */
818704 @ Override
0 commit comments