package org.threadly.concurrent; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; /** * Class which is designed to help with determining if a Runnable or Callable is contained at some * point within a chain of {@link CallableContainer} or {@link RunnableContainer}. * * @since 1.0.0 */ public class ContainerHelper { /** * Attempts to remove the provided runnable from the source collection. This uses the * {@link Collection#remove(Object)} function to remove the container if it is found. * * @since 2.0.0 * @param source source collection to search over * @param compareTo Runnable to compare against in search * @return {@code true} if collection was modified */ public static boolean remove(Collection<? extends RunnableContainer> source, Runnable compareTo) { Iterator<? extends RunnableContainer> it = source.iterator(); while (it.hasNext()) { RunnableContainer rc = it.next(); // we use source.remove() instead of it.remove() for usage with concurrent structures if (ContainerHelper.isContained(rc.getContainedRunnable(), compareTo) && source.remove(rc)) { return true; } } return false; } /** * Attempts to remove the provided callable from the source collection. This uses the * {@link Collection#remove(Object)} function to remove the container if it is found. * * @since 2.0.0 * @param source source collection to search over * @param compareTo Callable to compare against in search * @return {@code true} if collection was modified */ public static boolean remove(Collection<? extends RunnableContainer> source, Callable<?> compareTo) { Iterator<? extends RunnableContainer> it = source.iterator(); while (it.hasNext()) { RunnableContainer rc = it.next(); // we use source.remove() instead of it.remove() for usage with concurrent structures if (ContainerHelper.isContained(rc.getContainedRunnable(), compareTo) && source.remove(rc)) { return true; } } return false; } /** * Checks if the start runnable equals the compareTo runnable, or if the compareTo runnable is * contained within a wrapper of the startRunnable. * * @param startRunnable runnable to start search at * @param compareTo runnable to be comparing against * @return {@code true} if they are equivalent, or the compareTo runnable is contained within the start */ public static boolean isContained(Runnable startRunnable, Runnable compareTo) { if (startRunnable.equals(compareTo)) { return true; } boolean containedAsRCCI = false; if (startRunnable instanceof RunnableContainer) { RunnableContainer rci = (RunnableContainer)startRunnable; containedAsRCCI = isContained(rci, compareTo); } if (containedAsRCCI) { return true; } else if (startRunnable instanceof CallableContainer<?>) { CallableContainer<?> cci = (CallableContainer<?>)startRunnable; return isContained(cci, compareTo); } else { return false; } } /** * Checks if the compareTo runnable is contained by the provided * {@link RunnableContainerInterface}. If it's not we check to see if we can continue our * search by looking for another {@link RunnableContainer}, or a {@link CallableContainer}. * * @param rci Container to check contents of * @param compareTo Runnable to compare against * @return {@code true} if the runnable is contained at some point within the container */ private static boolean isContained(RunnableContainer rci, Runnable compareTo) { while (true) { Runnable containedTask = rci.getContainedRunnable(); if (containedTask != null) { if (containedTask.equals(compareTo)) { return true; } else if (containedTask instanceof CallableContainer<?> && isContained((CallableContainer<?>)containedTask, compareTo)) { return true; } else if (containedTask instanceof RunnableContainer) { // loop again rci = (RunnableContainer)containedTask; } else { return false; } } else { return false; } } } /** * Checks if the compareTo runnable is contained by the provided * {@link CallableContainerInterface}. If it's not we check to see if we can continue our * search by looking for another {@link RunnableContainer}, or a {@link CallableContainer}. * * @param cci Container to check contents of * @param compareTo Runnable to compare against * @return {@code true} if the runnable is contained at some point within the container */ private static boolean isContained(CallableContainer<?> cci, Runnable compareTo) { while (true) { Callable<?> containedTask = cci.getContainedCallable(); if (containedTask != null) { if (containedTask instanceof RunnableContainer && isContained((RunnableContainer)containedTask, compareTo)) { return true; } else if (containedTask instanceof CallableContainer<?>) { // loop again cci = (CallableContainer<?>)containedTask; } else { return false; } } else { return false; } } } /** * Checks if the start runnable contains the provided callable. * * @param startRunnable runnable to start search at * @param compareTo callable to be comparing against * @return {@code true} if the compareTo runnable is contained within the runnable */ public static boolean isContained(Runnable startRunnable, Callable<?> compareTo) { /* we have some awkward if/else logic in case we have a runnable the is both a * CallableContainerInterface and a RunnableContainerInterface */ boolean isContainedAsCCI = false; if (startRunnable instanceof CallableContainer<?>) { CallableContainer<?> cci = (CallableContainer<?>)startRunnable; isContainedAsCCI = isContained(cci, compareTo); } if (isContainedAsCCI) { return true; } else if (startRunnable instanceof RunnableContainer) { RunnableContainer rci = (RunnableContainer)startRunnable; return isContained(rci, compareTo); } else { return false; } } private static boolean isContained(RunnableContainer rci, Callable<?> compareTo) { while (true) { Runnable containedTask = rci.getContainedRunnable(); if (containedTask != null) { if (containedTask instanceof CallableContainer<?> && isContained((CallableContainer<?>)containedTask, compareTo)) { return true; } else if (containedTask instanceof RunnableContainer) { // loop again rci = (RunnableContainer)containedTask; } else { return false; } } else { return false; } } } /** * Checks if the compareTo runnable is contained by the provided * {@link CallableContainerInterface}. If it's not we check to see if we can continue our * search by looking for another {@link RunnableContainer}, or a {@link CallableContainer}. * * @param cci Container to check contents of * @param compareTo Callable to compare against * @return {@code true} if the callable is contained at some point within the container */ private static boolean isContained(CallableContainer<?> cci, Callable<?> compareTo) { while (true) { Callable<?> containedTask = cci.getContainedCallable(); if (containedTask != null) { if (containedTask.equals(compareTo)) { return true; } else if (containedTask instanceof RunnableContainer && isContained((RunnableContainer)containedTask, compareTo)) { return true; } else if (containedTask instanceof CallableContainer<?>) { // loop again cci = (CallableContainer<?>)containedTask; } else { return false; } } else { return false; } } } /** * Takes in a list of runnable containers, and instead makes a list of the runnables which are * contained in each item of the list. * * @since 4.0.0 * @param sourceList List of runnable containers to get interior runnable from * @return A list of runnables which were contained in the source list */ public static List<Runnable> getContainedRunnables(List<? extends RunnableContainer> sourceList) { List<Runnable> result = new ArrayList<>(sourceList.size()); Iterator<? extends RunnableContainer> it = sourceList.iterator(); while (it.hasNext()) { result.add(it.next().getContainedRunnable()); } return result; } }