DeviceAlgorithm.java

package neureka.backend.api;

import neureka.backend.api.template.algorithms.FunDeviceAlgorithm;
import neureka.common.utility.LogUtil;
import neureka.devices.Device;

/**
 *  A {@link DeviceAlgorithm} is an advanced form of {@link Algorithm} which
 *  delegates the execution to implementations of {@link ImplementationFor} specific {@link Device} types.
 *
 * @param <C> The type parameter defining the concrete type of implementations of this.
 */
public interface DeviceAlgorithm<C extends DeviceAlgorithm<C>> extends Algorithm
{
    /**
     * This is a factory method for creating a new instance of this {@link FunDeviceAlgorithm} class.
     *
     * @param name The name of the functional algorithm.
     * @return A new {@link FunDeviceAlgorithm} with the provided name.
     */
    static FunDeviceAlgorithm withName(String name ) {
        LogUtil.nullArgCheck( name, "name", String.class );
        return new FunDeviceAlgorithm( name );
    }

    /**
     * Implementations of the {@link DeviceAlgorithm} interface ought to express a compositional design pattern. <br>
     * This means that concrete implementations of an algorithm for a device are not extending
     * an Algorithm, they are components of it instead. <br>
     * These components can be stored on an Algorithm by passing
     * a Device class as key and an ImplementationFor instance as value.
     *
     * @param deviceClass    The class of the {@link Device} for which an implementation should be set.
     * @param implementation The {@link ImplementationFor} the provided {@link Device} type.
     * @param <D>            The type parameter of the {@link Device} type for which
     *                       an implementation should be set in this {@link Device}.
     * @param <I>            The type of the {@link ImplementationFor} the provided {@link Device} type.
     * @return This very {@link Algorithm} instance to allow for method chaining.
     */
    <D extends Device<?>, I extends ImplementationFor<D>> C setImplementationFor(Class<D> deviceClass, I implementation );

    /**
     * An {@link ImplementationFor} a specific {@link Device} can be accessed by passing the class of
     * the {@link Device} for which an implementation should be returned.
     * An Algorithm instance ought to contain a collection of these {@link Device} specific
     * implementations...
     *
     * @param deviceClass The class of the device for which the stored algorithm implementation should be returned.
     * @param <D>         The type parameter which has to be a class extending the Device interface.
     * @return The implementation for the passed device type class.
     */
    <D extends Device<?>> ImplementationFor<D> getImplementationFor( Class<D> deviceClass );

    /**
     * An {@link ImplementationFor} a specific {@link Device} can be accessed by passing
     * the {@link Device} for which an implementation should be returned.
     * An Algorithm instance ought to contain a collection of these {@link Device} specific
     * implementations...
     *
     * @param device The device for which the stored algorithm implementation should be returned.
     * @param <D>    type parameter which has to be a class extending the Device interface.
     * @return The implementation for the passed device type class.
     */
    default <D extends Device<?>> ImplementationFor<D> getImplementationFor( D device ) {
        return (ImplementationFor<D>) getImplementationFor(device.getClass());
    }

    default <D extends Device<?>> boolean hasImplementationFor( D device ) {
        return getImplementationFor(device.getClass()) != null;
    }

}