// 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 org.apache.tapestry5.func; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; /** * Functional operations on collections with generics support. The core interface is {@link Flow} to * which operations * and transformations * (in terms of {@link Predicate}s, {@link Mapper}s and {@link Reducer}s) to create new Flows. Flows * are initially * created * using {@link #flow(Collection)} and {@link #flow(Object...)}. * * F will be used a bit, thus it has a short name (for those who don't like static imports). It provides a base set of * Predicate, Mapper and Reducer factories. A good development pattern for applications is to provide a similar, * application-specific, set of such factories. * * @since 5.2.0 */ @SuppressWarnings("all") public class F { final static Flow<?> EMPTY_FLOW = new EmptyFlow(); @SuppressWarnings("unchecked") static <T> Flow<T> emptyFlow() { return (Flow<T>) EMPTY_FLOW; } /** * A Predicate factory for equality of an element from a flow against a specified * value. */ public static <T> Predicate<T> eql(final T value) { return new Predicate<T>() { @Override public boolean accept(T element) { return element.equals(value); } }; } /** * Predicate that returns true if the provided string is blank (null or all whitespace). */ public static Predicate<String> IS_BLANK = new Predicate<String>() { @Override public boolean accept(String element) { return element == null || element.trim().length() == 0; } }; /** * A Predicate factory for comparison of a Comparable element from a flow against a fixed value. */ public static <T extends Comparable<T>> Predicate<T> eq(final T value) { return new Predicate<T>() { @Override public boolean accept(T element) { return element.compareTo(value) == 0; } }; } /** * A Predicate factory for comparison of a Comparable element against a fixed value. */ public static <T extends Comparable<T>> Predicate<T> neq(final T value) { return new Predicate<T>() { @Override public boolean accept(T object) { return object.compareTo(value) != 0; } }; } /** * A Predicate factory for comparison of a Comparable against a fixed value; true * if the flow element is greater than the provided value. */ public static <T extends Comparable<T>> Predicate<T> gt(final T value) { return new Predicate<T>() { @Override public boolean accept(T element) { return element.compareTo(value) > 0; } }; } /** * A Predicate factory for comparison of a Comparable against a fixed value; true * if the flow element is greater than or equal to the value. */ public static <T extends Comparable<T>> Predicate<T> gteq(final T value) { return new Predicate<T>() { @Override public boolean accept(T element) { return element.compareTo(value) >= 0; } }; } /** * A Predicate factory for comparison of a Comparable against a fixed value; true * if the element is less than the value. */ public static <T extends Comparable<T>> Predicate<T> lt(T value) { return not(gteq(value)); } /** * A Predicate factory for comparison of a Comprable element against a fixed value; true * if the element is less than or equal to the value. */ public static <T extends Comparable<T>> Predicate<T> lteq(T value) { return not(gt(value)); } /** * A Predicate factory; returns true if the value from the Flow is null. */ public static <T> Predicate<T> isNull() { return new Predicate<T>() { @Override public boolean accept(T element) { return element == null; } }; } /** * A Predicate factory; returns true if the value from the Flow is not null. */ public static <T> Predicate<T> notNull() { return not(isNull()); } /** * A Mapper factory that gets the string value of the flow value using {@link String#valueOf(Object)}. */ public static <T> Mapper<T, String> stringValueOf() { return new Mapper<T, String>() { @Override public String map(T value) { return String.valueOf(value); } }; } /** * A Mapper factory; the returned Mapper ignores its input value and always returns a * predetermined result. */ public static <S, T> Mapper<S, T> always(final T fixedResult) { return new Mapper<S, T>() { @Override public T map(S input) { return fixedResult; } }; } /** * A Mapper factory that combines a Predicate with two {@link Mapper}s; evaluating the predicate * selects one of the two mappers. * * @param predicate * evaluated to selected a coercion * @param ifAccepted * used when predicate evaluates to true * @param ifRejected * used when predicate evaluates to false */ public static <S, T> Mapper<S, T> select(final Predicate<? super S> predicate, final Mapper<S, T> ifAccepted, final Mapper<S, T> ifRejected) { assert predicate != null; assert ifAccepted != null; assert ifRejected != null; return new Mapper<S, T>() { @Override public T map(S input) { Mapper<S, T> active = predicate.accept(input) ? ifAccepted : ifRejected; return active.map(input); } }; } /** * Override of {@link #select(Predicate, Mapper, Mapper)} where rejected values are replaced * with null. */ public static <S, T> Mapper<S, T> select(Predicate<? super S> predicate, Mapper<S, T> ifAccepted) { return select(predicate, ifAccepted, (T) null); } /** * Override of {@link #select(Predicate, Mapper)} where rejected values are replaced with a * fixed value. */ public static <S, T> Mapper<S, T> select(Predicate<? super S> predicate, Mapper<S, T> ifAccepted, T ifRejectedValue) { Mapper<S, T> rejectedMapper = always(ifRejectedValue); return select(predicate, ifAccepted, rejectedMapper); } /** * A Mapper factory; the Mapper returns the the flow value unchanged. */ public static <S> Mapper<S, S> identity() { return new Mapper<S, S>() { @Override public S map(S input) { return input; } }; } /** * Allows a Mapper that maps to boolean to be used as a Predicate. */ public static <S> Predicate<S> toPredicate(final Mapper<S, Boolean> mapper) { assert mapper != null; return new Predicate<S>() { @Override public boolean accept(S object) { return mapper.map(object); } }; } /** * A Reducer that operates on a Flow of Integers and is used to sum the values. */ public static Reducer<Integer, Integer> SUM_INTS = new Reducer<Integer, Integer>() { @Override public Integer reduce(Integer accumulator, Integer value) { return accumulator + value; } }; /** * A two-input Mapper used to add the values from two Flows of Integers into a Flow of Integer * sums. */ public static Mapper2<Integer, Integer, Integer> ADD_INTS = new Mapper2<Integer, Integer, Integer>() { @Override public Integer map(Integer first, Integer second) { return first + second; } }; /** * Extracts the values from the collection to form a {@link Flow}. The Collection * may change after the Flow is created without affecting the Flow. */ public static <T> Flow<T> flow(Collection<T> values) { assert values != null; if (values.isEmpty()) return emptyFlow(); return new ArrayFlow<T>(values); } /** * Creates a new Flow from the values. You should not change the values array * after invoking this method (i.e., no defensive copy of the values is made). */ public static <T> Flow<T> flow(T... values) { if (values.length == 0) return emptyFlow(); return new ArrayFlow<T>(values); } /** * Creates a lazy Flow from the {@link Iterator} obtained from the iterable. The Flow * will be threadsafe as long as the iterable yields a new Iterator on each invocation <em>and</em> the underlying * iterable object is not modified while the Flow is evaluating. * In other words, not extremely threadsafe. */ public static <T> Flow<T> flow(Iterable<T> iterable) { assert iterable != null; return flow(iterable.iterator()); } /** * Creates a lazy Flow from the {@link Iterator}. The Flow will be threadsafe as long as the underlying iterable * object is not modified while the Flow is evaluating. In other words, not extremely threadsafe. * * @since 5.3 */ public static <T> Flow<T> flow(Iterator<T> iterator) { return lazy(new LazyIterator<T>(iterator)); } /** * Creates a ZippedFlow from the provided map; the order of the tuples in the ZippedFlow is defined * by the iteration order of the map entries. * * @param <A> * type of key and first tuple value * @param <B> * type of value and second tuple value * @param map * source of tuples * @return zipped flow created from map * @since 5.3 */ public static <A, B> ZippedFlow<A, B> zippedFlow(Map<A, B> map) { assert map != null; Flow<Tuple<A, B>> tuples = F.flow(map.entrySet()).map(new Mapper<Map.Entry<A, B>, Tuple<A, B>>() { @Override public Tuple<A, B> map(Entry<A, B> element) { return Tuple.create(element.getKey(), element.getValue()); } }); return ZippedFlowImpl.create(tuples); } /** * Creates a lazy Flow that returns integers in the given range. The range starts * with the lower value and counts by 1 up to the upper range (which is not part of * the Flow). If lower equals upper, the Flow is empty. If upper is less than lower, * the Flow counts down instead. * * @param lower * start of range (inclusive) * @param upper * end of range (exclusive) */ public static Flow<Integer> range(int lower, int upper) { if (lower == upper) return F.emptyFlow(); if (lower < upper) return lazy(new LazyRange(lower, upper, 1)); return lazy(new LazyRange(lower, upper, -1)); } /** * Creates a {@link Flow} from a {@linkplain LazyFunction lazy function}. */ public static <T> Flow<T> lazy(LazyFunction<T> function) { assert function != null; return new LazyFlow<T>(function); } private static <T> LazyFunction<T> toLazyFunction(final T currentValue, final Mapper<T, T> function) { return new LazyFunction<T>() { @Override public LazyContinuation<T> next() { final T nextValue = function.map(currentValue); return new LazyContinuation<T>(nextValue, toLazyFunction(nextValue, function)); } }; } /** * Creates an infinite lazy flow from an initial value and a function to map from the current value to the * next value. * * @param initial * initial value in flow * @param function * maps from current value in flow to next value in flow * @return lazy flow */ public static <T> Flow<T> iterate(final T initial, final Mapper<T, T> function) { LazyFunction<T> head = new LazyFunction<T>() { @Override public LazyContinuation<T> next() { return new LazyContinuation<T>(initial, toLazyFunction(initial, function)); } }; return lazy(head); } /** * Creates an <em>infinite</em> series of numbers. * * Attempting to get the {@linkplain Flow#count()} of the series will form an infinite loop. */ public static Flow<Integer> series(int start, final int delta) { return iterate(start, new Mapper<Integer, Integer>() { @Override public Integer map(Integer element) { return element + delta; } }); } /** * A Worker factory; the returnedWorker adds the values to a provided collection. */ public static <T> Worker<T> addToCollection(final Collection<T> coll) { return new Worker<T>() { @Override public void work(T value) { coll.add(value); } }; } /** * A Predicate factory for matching String elements with a given prefix. * * @since 5.3 */ public static Predicate<String> startsWith(String prefix) { return startsWith(prefix, false); } /** * As {@link #startsWith(String)}, but ignores case. * * @since 5.3 */ public static Predicate<String> startsWithIgnoringCase(String prefix) { return startsWith(prefix, true); } /** * @since 5.3 */ private static Predicate<String> startsWith(final String prefix, final boolean ignoreCase) { return new Predicate<String>() { @Override public boolean accept(String element) { return element.regionMatches(ignoreCase, 0, prefix, 0, prefix.length()); } }; } /** * A Predicate factory for matching String elements with a given suffix. * * @since 5.3 */ public static Predicate<String> endsWith(String suffix) { return endsWith(suffix, false); } /** * As with {@link #endsWith(String)} but ignores case. * * @since 5.3 */ public static Predicate<String> endsWithIgnoringCase(String suffix) { return endsWith(suffix, true); } /** * @since 5.3 */ private static Predicate<String> endsWith(final String suffix, final boolean ignoreCase) { return new Predicate<String>() { @Override public boolean accept(String element) { return element .regionMatches(ignoreCase, element.length() - suffix.length(), suffix, 0, suffix.length()); } }; } /** * Creates a Comparator for the Tuples of a {@link ZippedFlow} that sorts the Tuple elements based on the first * value in the Tuple. * * @since 5.3 */ public static <A extends Comparable<A>, B> Comparator<Tuple<A, B>> orderByFirst() { return new Comparator<Tuple<A, B>>() { @Override public int compare(Tuple<A, B> o1, Tuple<A, B> o2) { return o1.first.compareTo(o2.first); } }; } /** * Creates a Comparator for the Tuples of a {@link ZippedFlow} that sorts the Tuple elements based on the first * value in the Tuple. * * @since 5.3 */ public static <A, B extends Comparable<B>> Comparator<Tuple<A, B>> orderBySecond() { return new Comparator<Tuple<A, B>>() { @Override public int compare(Tuple<A, B> o1, Tuple<A, B> o2) { return o1.second.compareTo(o2.second); } }; } /** * Inverts a predicate. * * @param delegate * the predicate to invert * @return a new predicate that is inverse to the existing predicate * @since 5.3 */ public static <T> Predicate<T> not(final Predicate<? super T> delegate) { assert delegate != null; return new Predicate<T>() { @Override public boolean accept(T element) { return !delegate.accept(element); } }; } /** * Combines two mappers into a composite mapping from type A to type C via type B. * * @param abMapper * maps from A to B * @param bcMapper * maps from B to C * @return mapper from A to C */ public static <A, B, C> Mapper<A, C> combine(final Mapper<A, B> abMapper, final Mapper<B, C> bcMapper) { assert abMapper != null; assert bcMapper != null; return new Mapper<A, C>() { @Override public C map(A aElement) { B bElement = abMapper.map(aElement); return bcMapper.map(bElement); } }; } /** * Combines any number of delegates as a logical and operation. Evaluation terminates * with the first delegate predicate that returns false. * * @param delegates * to evaluate * @return combined delegate * @since 5.3 */ public static <T> Predicate<T> and(final Predicate<? super T>... delegates) { return new Predicate<T>() { @Override public boolean accept(T element) { for (Predicate<? super T> delegate : delegates) { if (!delegate.accept(element)) return false; } return true; } }; } /** * Combines any number of delegates as a logical or operation. Evaluation terminates * with the first delegate predicate that returns true. * * @param delegates * to evaluate * @return combined delegate * @since 5.3 */ public static <T> Predicate<T> or(final Predicate<? super T>... delegates) { return new Predicate<T>() { @Override public boolean accept(T element) { for (Predicate<? super T> delegate : delegates) { if (delegate.accept(element)) return true; } return false; } }; } /** * Combines several compatible workers together into a composite. * * @since 5.3 */ public static <T> Worker<T> combine(final Worker<? super T>... delegates) { assert delegates.length > 0; return new Worker<T>() { @Override public void work(T value) { for (Worker<? super T> delegate : delegates) { delegate.work(value); } } }; } }