All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scijava.ops.api.OpEnvironment Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * The public API of SciJava Ops.
 * %%
 * Copyright (C) 2021 - 2024 SciJava developers.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */

package org.scijava.ops.api;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.SortedSet;

import org.scijava.discovery.Discoverer;
import org.scijava.priority.Prioritized;
import org.scijava.priority.Priority;
import org.scijava.types.Nil;

/**
 * An op environment is the top-level entry point into op execution. It provides
 * all the built-in functionality of ops in a single place, including:
 * 
    *
  • The pool of available ops, from which candidates are chosen.
  • *
  • Type-safe, built-in method signatures for all op implementations.
  • *
  • Selection (a.k.a. "matching") of op implementations from * {@link OpRequest} descriptors.
  • *
*

* Customizing the {@link OpEnvironment} allows customization of any or all of * the above. Potential use cases include: *

    *
  • Limiting or extending the pool of available op implementations.
  • *
  • Caching op outputs to improve subsequent time performance.
  • *
  • Configuration of environment "hints" to improve performance in time or * space.
  • *
* * @author Curtis Rueden * @author Gabriel Selzer */ public interface OpEnvironment extends Prioritized { /** * Generates an empty {@link OpEnvironment}, which can be populated * with the Ops of the caller's choice. * * @return an empty {@link OpEnvironment} * @see #build() for an {@link OpEnvironment} that is fully populated */ static OpEnvironment buildEmpty() { Optional opsOptional = Discoverer // .using(ServiceLoader::load) // .discoverMax(OpEnvironment.class); return opsOptional.orElseThrow( // () -> new RuntimeException("No OpEnvironment Provided!") // ); } /** * Generates an {@link OpEnvironment} with all available Ops. * * @return an {@link OpEnvironment} with all available Ops. * @see #buildEmpty() for an {@link OpEnvironment} that is empty */ static OpEnvironment build() { OpEnvironment ops = buildEmpty(); ops.discoverEverything(); return ops; } /** * Obtains all Ops in the {@link OpEnvironment}, sorted by priority. * * @return a {@link SortedSet} containing all Ops contained in the * {@link OpEnvironment}. */ default SortedSet infos() { return infos(null, getDefaultHints()); } /** * Obtains all Ops in the {@link OpEnvironment} that are named {@code name}, * sorted by priority. * * @param name the {@link String} of all Ops to be returned. * @return a {@link SortedSet} containing all Ops in the {@link OpEnvironment} * named {@code name} */ SortedSet infos(String name); /** * Obtains all Ops in the {@link OpEnvironment} that match {@code hints}, * sorted by priority * * @param hints the {@link Hints} used to filter available Ops. * @return a {@link SortedSet} containing all Ops in the {@link OpEnvironment} * matching {@code hints} */ SortedSet infos(Hints hints); /** * Obtains all Ops in the {@link OpEnvironment} that are named {@code name} * and match {@code hints}, sorted by priority * * @param name the {@link String} of all Ops to be returned. * @param hints the {@link Hints} used to filter available Ops. * @return a {@link SortedSet} containing all Ops in the {@link OpEnvironment} * named {@code name} and matching {@code hints} */ SortedSet infos(String name, Hints hints); void discoverUsing(Discoverer... d); void discoverEverything(); OpHistory history(); // TODO: Add interface method: OpInfo info(final String opName, final Nil // specialType, final Nil[] inTypes, final Nil outType); /** * Returns an Op fitting the provided arguments. NB implementations of this * method likely depend on the {@link Hints} set by * {@link OpEnvironment#setDefaultHints(Hints)}, which provides no guarantee * of thread-safety. Users interested in parallel Op matching should consider * using {@link OpEnvironment#op(String, Nil, Nil[], Nil, Hints)} instead. * * @param the {@link Type} of the Op * @param opName the name of the Op * @param specialType the generic {@link Type} of the Op * @param inTypes the arguments (inputs) to the Op * @param outType the return of the Op (note that it may also be an argument) * @return an instance of an Op aligning with the search parameters */ default T op( // final String opName, // final Nil specialType, // final Nil[] inTypes, // final Nil outType // ) { return op(opName, specialType, inTypes, outType, getDefaultHints()); } /** * Returns an Op fitting the provided arguments. * * @param the {@link Type} of the Op * @param opName the name of the Op * @param specialType the generic {@link Type} of the Op * @param inTypes the arguments (inputs) to the Op * @param outType the return of the Op (note that it may also be an argument) * @param hints the {@link Hints} that should guide this matching call * @return an instance of an Op aligning with the search parameters */ T op(final String opName, final Nil specialType, final Nil[] inTypes, final Nil outType, Hints hints); default T opFromInfoTree(InfoTree tree, Nil specialType) { return opFromInfoTree(tree, specialType, getDefaultHints()); } T opFromInfoTree(InfoTree tree, Nil specialType, Hints hints); /** * Returns an Op fitting the provided arguments. * * @param the {@link Type} of the Op * @param signature the signature of the Op * @param specialType the generic {@link Type} of the Op * @return an instance of an Op aligning with the search parameters */ default T opFromSignature(final String signature, final Nil specialType) { InfoTree info = treeFromSignature(signature); return opFromInfoTree(info, specialType); } InfoTree treeFromSignature(final String signature); /** *

* Entry point for convenient Op calls, providing a builder-style interface to * walk through the process step-by-step. *

*

* The general order of specification: *

*
    *
  1. The op name (and {@link Hints}, if desired)
  2. *
  3. The number of input(s)
  4. *
  5. The type or value(s) of input(s)
  6. *
  7. One of: *
      *
    • The type or value of the output
    • *
    • Which input should be modified in-place
    • *
    *
  8. *
* The first two steps are required, at a minimum. The choices you make will * determine the type of Op that is matched: *
    *
  • No inputs → Producer or Computer
  • *
  • Inputs with an output valueComputer
  • *
  • Inputs with an output typeComputer or * Function
  • *
  • Inputs with no output → Inplace or * Function with unknown (Object) return
  • *
*

* Examples: {@code OpEnvironment env = new DefaultOpEnvironment();} *

*
    *
  • {@code env.op("create").outType(DoubleType.class).create();} — * run an Op creating an instance of the ImgLib2 {@code DoubleType}
  • *
  • {@code env.op("create").outType(DoubleType.class).create();} — * same as above.
  • *
  • {@code env.op("create", hints).outType(DoubleType.class).create();} * — same as above but matching with the provided {@link Hints}.
  • *
  • {@code env.op("create").outType(Img.class).create();} — run an Op * creating a raw instance of an ImgLib2 {@code Img}.
  • *
  • {@code env.op("create").outType(new * Nil>(){}).create();} — run an Op creating an instance * of an ImgLib2 {@code Img}.
  • *
  • {@code env.op("math.add").inType(Integer.class, Integer.class).function();} * — get an instance of an Op to add two integers together. Return type * will be {@code Object}.
  • *
  • {@code env.op("math.add").inType(Integer.class, Integer.class).function();} * — same as above.
  • *
  • {@code env.op("math.add").input(1, 1).outType(Double.class).apply();} * — run an Op combining two integers. Return type will be * {@code Double}.
  • *
  • {@code env.op("math.add").input(img1, img2).output(result).compute();} * — run an Op combining two images and storing the result in a * pre-allocated image.
  • *
  • {@code env.op("filter.addPoissonNoise").input(img1).mutate();} — * run an Op adding poisson noise to an input image.
  • *
* * @param opName The name of the Op to run * @return The {@link OpBuilder} instance for builder chaining. * @throws OpMatchingException if the Op request cannot be satisfied * @see #op(String, Hints) To specify a Hints instance to use * @see OpBuilder */ default OpBuilder op(final String opName) { return new OpBuilder(this, opName); } /** * As {@link #op(String)} but using a provided {@link Hints}. * * @param opName The name of the Op to run * @param hints The {@code Hints} instance to use for Op matching * @return The {@link OpBuilder} instance for builder chaining. * @throws OpMatchingException if the Op request cannot be satisfied * @see #op(String) To use the default Hints for this OpEnvironment */ default OpBuilder op(final String opName, final Hints hints) { return new OpBuilder(this, opName, hints); } /** Discerns the generic type of an object instance. */ Type genericType(Object obj); /** * Enriches a lambda expression with its generic type. Its usage is necessary * in order to find Ops that could take this lamdba expression as an argument. *

* Suppose, for example, that a user has a lambda expression of type * {@code Function}, and they wish to use the * {@code engine.adapt} Op to transform it into a * {@code Function, Iterable>}. * Naively, they write: *

{@code
	 * Function doubler = x -> 2*x;
	 * Function, Iterable> iterableDoubler =
	 *   ops.op("engine.adapt").input(doubler).outType(new Nil<>() {}).apply();
	 * }
* but it does not work, because the Ops engine cannot know the generic type * of the {@code doubler} object. One workaround is to specify the input * generic type explicitly: *
{@code
	 * Function doubler = x -> 2*x;
	 * Function, Iterable> iterableDoubler =
	 *   ops.op("engine.adapt")
	 *     .inType(new Nil() {})
	 *     .outType(new Nil<>() {}).function().apply(doubler);
	 * }
* Another approach is to use this {@code typeLambda} method as follows: *
{@code
	 * Function doubler = ops.typeLambda(new Nil<>() {}, x -> 2*x);
	 * Function, Iterable> iterableDoubler =
	 *   ops.op("engine.adapt").input(doubler).outType(new Nil<>() {}).apply();
	 * }
* In the above example, the {@code doubler} object returned by * {@code typeLambda} has its generic type information embedded, rather than * being a raw lambda where the generic type information is erased at runtime. *

* Note: {@code typeLambda} does not need to be used with anonymous * subclasses; these already retain their type parameters at runtime. It is * only lambda expressions that need to be passed to this method. * * @param The lambda's functional type * (e.g. {@link java.util.function.Function}{@code }) * @param opType The generic type of the lambda expression to be embedded. * (e.g. {@code new Nil>() {}}) * @param lambda A lambda expression fulfilling the functional interface * contract of the stated {@code opType}. * @return An enriched version of the lambda object that embeds knowledge of * its generic type by implementing the * {@link org.scijava.common3.GenericTyped} interface. */ T typeLambda(Nil opType, T lambda); /** * Creates an {@link OpInfo} from a {@link Class}. * * @param opClass the {@link Class} from which to derive the Op * @param names the name(s) of the Op * @return an {@link OpInfo} which can make instances of {@code opClass} */ default OpInfo opify(Class opClass, String... names) { return opify(opClass, Priority.NORMAL, names); } /** * Creates an {@link OpInfo} from a {@link Class} with the given priority. * * @param opClass the {@link Class} from which to derive the Op * @param priority the assigned priority of the Op. * @param names the name(s) of the Op * @return an {@link OpInfo} which can make instances of {@code opClass} */ OpInfo opify(Class opClass, double priority, String... names); /** * Registers all {@link OpInfo} derived from {@link Object}s within the passed * array. Elements within this {@link Object[]} might be: *

    *
  • {@link OpInfo}s
  • *
  • {@link java.util.Collection}s or arrays of the above
  • *
  • {@link Object}s (such as, but not limited to, {@link Class}es * or {@link Method}s) from which {@link OpInfo}s could be derived *
* * @param objects the {@link Object}s that should be made discoverable to this * {@link OpEnvironment} */ void register(Object... objects); /** * Sets the {@link Hints} for the {@link OpEnvironment}. Every Call to * {@link #op} that does not pass a {@link Hints} will copy the * Hints passed here (to prevent multiple Op calls from accessing/changing the * same {@link Hints}). In the case that no {@link Hints} have been * given to the Environment before {@code op} is called, the * implementation will create a suitable stand-in. *

* Note that this method does not inherently provide any guarantees * pertaining to thread-safety; this API is provided purely for convenience. * Any calls to {@link OpEnvironment#op(String, Nil, Nil[], Nil)} that require * a specific {@link Hints} should not use this method, instead opting * for {@link OpEnvironment#op(String, Nil, Nil[], Nil, Hints)}. * * @param hints the {@link Hints} to be set as default */ void setDefaultHints(Hints hints); /** * Returns a copy of the default {@link Hints} object. * * @return the default {@link Hints} */ Hints getDefaultHints(); /** * Returns the descriptions for all Ops contained within this * {@link OpEnvironment} * * @return a String describing all Ops in the environment matching * {@code name} */ default String help() { return help(new PartialOpRequest()); } /** * Returns the descriptions for all Ops contained within this * {@link OpEnvironment} matching {@code name} * * @param name the {@link String} name to filter on * @return a {@link String} describing all Ops in the environment matching * {@code name} */ default String help(final String name) { return help(new PartialOpRequest(name)); } /** * Returns simple descriptions for all Ops identifiable by a given name within * this {@link OpEnvironment} * * @param request the {@link OpRequest} to filter on * @return a {@link String} containing a (set of) lines for each Op matching * {@code request} */ String help(final OpRequest request); /** * Returns verbose descriptions for all Ops contained within this * {@link OpEnvironment} * * @return a {@link String} containing a (set of) lines for each Op */ default String helpVerbose() { return helpVerbose(new PartialOpRequest()); } /** * Returns verbose descriptions for all Ops contained within this * {@link OpEnvironment} * * @param name the {@link String} name to filter on * @return a {@link String} describing all Ops in the environment matching * {@code name} */ default String helpVerbose(final String name) { return helpVerbose(new PartialOpRequest(name)); } /** * Returns verbose descriptions for all Ops contained within this * {@link OpEnvironment} * * @param request the {@link OpRequest} to filter on * @return a {@link String} containing a (set of) lines for each Op matching * {@code request} */ String helpVerbose(final OpRequest request); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy