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

com.ocadotechnology.utils.TargetDispatcher Maven / Gradle / Ivy

There is a newer version: 16.6.21
Show newest version
/*
 * Copyright © 2017-2023 Ocado (Ocava)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ocadotechnology.utils;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

import javax.annotation.CheckForNull;
import javax.annotation.ParametersAreNonnullByDefault;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

/**
 * Provides dynamic method invocation across registered instances of a particular class. Target instances are registered
 * with some corresponding identifier that is subsequently used to select the instance upon which to invoke a particular
 * method.
 *
 * @param  Type of the identifiers which uniquely specify the target instances.
 * @param  Type of the target instances upon which method calls will be dynamically invoked.
 */
@ParametersAreNonnullByDefault
public class TargetDispatcher {
    private final Map targetsById;

    /**
     * Create a TargetDispatcher by providing your own map to start with.
     * The constructor will accept immutable maps but many of the methods in this class will not support them.
     * @param targetsById the initial map to use.
     */
    public TargetDispatcher(Map targetsById) {
        this.targetsById = targetsById;
    }

    /**
     * Creates a TargetDispatcher with an initial empty HashMap.
     */
    public TargetDispatcher() {
        this(new HashMap<>());
    }

    /**
     * Adds a new target to the map if there does not already exist one with the provided ID.
     * @param id the ID of the target
     * @param member the value of the target
     * @return true if the target was added successfully.
     */
    public final boolean registerTarget(I id, T member) {
        T existingTarget = targetsById.putIfAbsent(id, member);
        return existingTarget == null;
    }

    /**
     * Updates the target in the map with the provided ID, or adds a new target if the ID is not in the map.
     * @param id the ID of the target
     * @param member the new value to put in the map
     * @return true if there was no existing value to update
     */
    public final boolean updateTarget(I id, T member) {
        T existingTarget = targetsById.put(id, member);
        return existingTarget == null;
    }

    /**
     * Removes a target from the map.
     * @param id the ID of the target to remove
     * @return the removed target
     */
    public final T deregisterTarget(I id) {
        return targetsById.remove(id);
    }

    /**
     * Removes all targets from the map.
     */
    public final void deregisterAllTargets() {
        targetsById.clear();
    }

    /**
     * Checks if a target exists in the map.
     * @param id the ID to check
     * @return true if the target exists in the map.
     */
    public final boolean hasTarget(I id) {
        return targetsById.containsKey(id);
    }

    /**
     * Gets a target from the map.
     * @param id the ID of the target to get.
     * @return the target registered with that ID.
     * @throws UnknownTargetException if no target is currently registered with the specified ID.
     */
    public T getTarget(@CheckForNull I id) {
        if (id == null) {
            throw new IllegalArgumentException("Target ID cannot be NULL");
        }
        T target = targetsById.get(id);
        if (target == null) {
            throw new UnknownTargetException(id);
        }
        return target;
    }

    /**
     * Optionally returns the target from the map.
     * @param id the ID of the target to get.
     * @return an optional containing the target, or empty if no target in the map has that ID.
     */
    public Optional maybeGetTarget(I id) {
        return Optional.ofNullable(targetsById.get(id));
    }

    /**
     * Performs a provided action on the target registered with the provided ID.
     * @param id the ID of the target to get.
     * @param action an action to perform on the target.
     * @throws IllegalArgumentException if the specified ID is NULL.
     * @throws UnknownTargetException if no target is currently registered with the specified ID.
     * @throws NullPointerException if the specified action is NULL.
     */
    public final void doTargetAction(I id, Consumer action) {
        action.accept(getTarget(id));
    }

    /**
     * Performs a provided action on the target registered with the provided ID if one exists.
     * @param id the ID of the target to get.
     * @param action an action to perform on the target.
     * @throws NullPointerException if the specified action is NULL.
     */
    public final void doTargetActionIfRegistered(I id, Consumer action) {
        T target = targetsById.get(id);
        if (target != null) {
            action.accept(target);
        }
    }

    /**
     * Performs a function on the target registered with the provided ID.
     * @param id the ID of the target to get.
     * @param function the function to apply to the target.
     * @param  the return type of the function.
     * @return the result of applying the function to the target.
     * @throws IllegalArgumentException if the specified ID is NULL.
     * @throws UnknownTargetException if no target is currently registered with the specified ID.
     * @throws NullPointerException if the specified function is NULL.
     */
    public final  R doTargetFunction(I id, Function function) {
        return function.apply(getTarget(id));
    }

    /**
     * Makes a copy (in case action causes deregistration), and calls the action for all targets.
     * Use deregisterAllTargets to deregister without performing an action.
     * @param action the action to apply.
     */
    public final void doForAllTargets(Consumer action) {
        Collection targets = ImmutableList.copyOf(targetsById.values());
        targets.forEach(action);
    }

    /**
     * Makes a copy (in case action causes deregistration), and calls the action for all targets matching the filter.
     * Use deregisterAllTargets to deregister without performing an action.
     * @param filter a predicate to filter targets to apply the action to.
     * @param action the action to apply.
     */
    public final void doForAllTargetsWhere(Predicate filter, Consumer action) {
        Collection targets = ImmutableList.copyOf(targetsById.values());
        targets.stream().filter(filter).forEach(action);
    }

    /**
     * Makes a copy (in case action causes deregistration), and performs a function on all targets.
     * @param mapper the function to apply to all targets.
     * @param  the return type of the function.
     * @return a stream containing the results of applying the function.
     */
    public final  Stream mapAllTargets(Function mapper) {
        Collection targets = ImmutableList.copyOf(targetsById.values());
        return targets.stream().map(mapper);
    }

    /**
     * Gets an immutable copy of all targets in the map.
     * @return all targets in the map.
     */
    public final Map getAllTargets() {
        return ImmutableMap.copyOf(targetsById);
    }

    /**
     * Makes a copy of all targets and streams them.
     * @return a stream containing the targets.
     */
    public final Stream streamAllTargets() {
        return ImmutableList.copyOf(targetsById.values()).stream();
    }

    /**
     * Makes a copy (in case filter causes deregistration), and filters all targets using the provided predicate.
     * @param filter the filter to apply.
     * @return a stream containing the filtered targets.
     */
    public final Stream filterAllTargets(Predicate filter) {
        Collection targets = ImmutableList.copyOf(targetsById.values());
        return targets.stream().filter(filter);
    }

    /**
     * Makes a copy (in case predicate causes deregistration), and checks if any target matches the provided predicate.
     * @param predicate the predicate to check.
     * @return true if any target matches the predicate.
     */
    public final boolean anyTargetMatches(Predicate predicate) {
        Collection targets = ImmutableList.copyOf(targetsById.values());
        return targets.stream().anyMatch(predicate);
    }

    /**
     * Exception thrown when an attempt to get a target from the map is made but that ID does not exist in the map.
     */
    public static final class UnknownTargetException extends RuntimeException {
        private UnknownTargetException(Object targetId) {
            super("No target registered with ID [" + targetId + "]");
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy