package games.strategy.util; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * A utilty for seeing which elements in a collection satisfy a given condition. * * <p> * An instance of match allows you to test that an object matches some condition. * </p> * * <p> * Static utility methods allow you to find what elements in a collection satisfy a match, * count the number of matches, see if any elements match etc. * </p> */ public abstract class Match<T> { public static <T> Match<T> getAlwaysMatch() { return new AlwaysMatch<>(); } public static <T> Match<T> getNeverMatch() { return new NeverMatch<>(); } /** * Returns the elements of the collection that match. */ public static <T> List<T> getMatches(final Collection<T> collection, final Match<T> aMatch) { final List<T> matches = new ArrayList<>(); for (final T current : collection) { if (aMatch.match(current)) { matches.add(current); } } return matches; } /** * Only returns the first n matches. * If n matches cannot be found will return all matches that * can be found. */ public static <T> List<T> getNMatches(final Collection<T> collection, final int max, final Match<T> aMatch) { if (max == 0 || collection.isEmpty()) { return Collections.emptyList(); } if (max < 0) { throw new IllegalArgumentException("max must be positive, instead its:" + max); } final List<T> matches = new ArrayList<>(Math.min(max, collection.size())); for (final T current : collection) { if (aMatch.match(current)) { matches.add(current); } if (matches.size() == max) { return matches; } } return matches; } /** * returns true if all elements in the collection match. */ public static <T> boolean allMatch(final Collection<T> collection, final Match<T> aMatch) { if (collection.isEmpty()) { return false; } for (final T current : collection) { if (!aMatch.match(current)) { return false; } } return true; } /** * Returns true if any matches could be found. */ public static <T> boolean someMatch(final Collection<T> collection, final Match<T> aMatch) { if (collection.isEmpty()) { return false; } for (final T current : collection) { if (aMatch.match(current)) { return true; } } return false; } /** * Returns true if no matches could be found. */ public static <T> boolean noneMatch(final Collection<T> collection, final Match<T> aMatch) { return !someMatch(collection, aMatch); } /** * Returns the number of matches found. */ public static <T> int countMatches(final Collection<T> collection, final Match<T> aMatch) { int count = 0; for (final T current : collection) { if (aMatch.match(current)) { count++; } } return count; } /** * return the keys where the value keyed by the key matches valueMatch. */ public static <K, V> Set<K> getKeysWhereValueMatch(final Map<K, V> aMap, final Match<V> valueMatch) { final Set<K> rVal = new HashSet<>(); final Iterator<K> keys = aMap.keySet().iterator(); while (keys.hasNext()) { final K key = keys.next(); final V value = aMap.get(key); if (valueMatch.match(value)) { rVal.add(key); } } return rVal; } /** * Subclasses must override this method. * Returns true if the object matches some condition. */ public abstract boolean match(T o); public final Match<T> invert() { return new InverseMatch<>(this); } } class NeverMatch<T> extends Match<T> { @Override public boolean match(final T o) { return false; } } class AlwaysMatch<T> extends Match<T> { @Override public boolean match(final T o) { return true; } }