/* * #%L * Nazgul Project: nazgul-core-algorithms-api * %% * Copyright (C) 2010 - 2017 jGuru Europe AB * %% * Licensed under the jGuru Europe AB license (the "License"), based * on Apache License, Version 2.0; you may not use this file except * in compliance with the License. * * You may obtain a copy of the License at * * http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt * * 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. * #L% * */ package se.jguru.nazgul.core.algorithms.api.collections; import se.jguru.nazgul.core.algorithms.api.Validate; import se.jguru.nazgul.core.algorithms.api.collections.predicate.Filter; import se.jguru.nazgul.core.algorithms.api.collections.predicate.Transformer; import se.jguru.nazgul.core.algorithms.api.collections.predicate.Tuple; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; /** * A suite of frequently used functional algorithms. * * @author <a href="mailto:lj@jguru.se">Lennart Jörelid</a>, jGuru Europe AB */ public abstract class CollectionAlgorithms { /** * Filters the provided collection using the given selector. * * @param source The non-null source Collection holding T instances. * @param selector The T-type filter * @param <T> The type to filter. * @param <C> The Collection type which should be retrieved. * @return A new Collection holding the unique instances from the * source list accepted by the GenericFilter (shallow * copy, not deep-clone'd elements). */ public static <T, C extends Collection<T>> C filter(final C source, final Filter<T> selector) { // Check sanity Validate.notNull(source, "source"); Validate.notNull(selector, "selector"); // Create a return instance C toReturn = cloneEmpty(source); for (T current : source) { if (selector.accept(current) && !toReturn.contains(current)) { toReturn.add(current); } } return toReturn; } /** * Filters the provided Map using the given selector. * * @param source The non-null source Map holding Entries of [K, V] instances. * @param selector The Filter selector for the provided Map * @param <K> The key type of the Map. * @param <V> The value type of the Map. * @return A new Map holding the instances from the * source map accepted by the GeneriFilter (shallow * copy, not deep-clone'd elements). */ public static <K, V> Map<K, V> filter(final Map<K, V> source, final Filter<Tuple<K, V>> selector) { // Check sanity Validate.notNull(source, "source"); Validate.notNull(selector, "selector"); // Create a return instance final Map<K, V> toReturn = new HashMap<>(); for (Map.Entry<K, V> currentEntry : source.entrySet()) { if (selector.accept(new Tuple<>(currentEntry.getKey(), currentEntry.getValue()))) { toReturn.put(currentEntry.getKey(), currentEntry.getValue()); } } return toReturn; } /** * Transforms (maps) each element within the source collection using the * provided transformer, returning the result in form of a Collection of * transformed instances. * * @param source The non-null source Collection holding T instances. * @param transformer The transformer used to produce each R element from a T element. * @param <T> The source collection type. * @param <R> The response (transformed) collection type. * @param <C> The Collection type. * @return A new Collection holding the transformed instances from the source. */ @SuppressWarnings("unchecked") public static <T, R, C extends Collection<R>> C transform(final Collection<T> source, final Transformer<T, R> transformer) { // Check sanity Validate.notNull(source, "source"); Validate.notNull(transformer, "transformer"); // Create a return instance Collection<R> toReturn = cloneEmptyFromType(source.getClass()); for (T current : source) { toReturn.add(transformer.transform(current)); } return (C) toReturn; } /** * Flattens (maps) each element within the source Map[K, V] using the * provided transformer, returning the result in form of a Collection of * transformed instances. * * @param source The non-null source Map holding entries of [K, V] objects. * @param transformer The transformer used to produce a T instance from each [K, V] * entry within source. * @param <K> The key type of the source map. * @param <V> The value type of the source map. * @param <T> The type of the returned Collection. * @return A List holding T instances. */ public static <K, V, T> List<T> flatten(final Map<K, V> source, final Transformer<Tuple<K, V>, T> transformer) { // Check sanity Validate.notNull(source, "source"); Validate.notNull(transformer, "transformer"); // Create a return instance List<T> toReturn = new ArrayList<>(); for (Map.Entry<K, V> currentEntry : source.entrySet()) { toReturn.add(transformer.transform(new Tuple<>(currentEntry.getKey(), currentEntry.getValue()))); } return toReturn; } /** * Transforms the sourceMap to a resulting Map using the provided transformer. * * @param source The non-null source Map holding entries of [K1, V1] objects. * @param transformer The transformer used to produce entries of [K2, V2] from each entry of * [K1, V1] within source. * @param <K1> The key type in the source Map. * @param <V1> The value type in the source Map. * @param <K2> The key type in the result Map. * @param <V2> The value type in the result Map. * @return A new Map holding the transformed Entries from the source Map. */ public static <K1, V1, K2, V2> Map<K2, V2> transform(final Map<K1, V1> source, final Transformer<Tuple<K1, V1>, Tuple<K2, V2>> transformer) { // Check sanity Validate.notNull(source, "source"); Validate.notNull(transformer, "transformer"); // Create a return instance Map<K2, V2> toReturn = new HashMap<>(); for (Map.Entry<K1, V1> current : source.entrySet()) { final Tuple<K1, V1> sourceTuple = new Tuple<>(current.getKey(), source.get(current.getKey())); final Tuple<K2, V2> resultTuple = transformer.transform(sourceTuple); if (resultTuple != null) { toReturn.put(resultTuple.getKey(), resultTuple.getValue()); } } return toReturn; } /** * Transforms (maps) each element within the source collection using the * provided transformer, returning the result in form of a Map of * the Tuple pair instances. <strong>Note!</strong> Only non-null Tuples * returned by the transformer are mapped into the returned Map. * * @param source The non-null source Collection holding T instances. * @param transformer The transformer used to produce each R element from a T element. * @param <T> The source collection type. * @param <K> The key type of the returned Map. * @param <V> The value type of the returned Map. * @return A Map containing the <strong>non-null</strong> Tuples generated by the Tuple transformer. */ public static <K, V, T> Map<K, V> map(final Collection<T> source, final Transformer<T, Tuple<K, V>> transformer) { // Check sanity Validate.notNull(source, "source"); Validate.notNull(transformer, "transformer"); // Create a return instance Map<K, V> toReturn = new HashMap<>(); for (T current : source) { final Tuple<K, V> keyValuePair = transformer.transform(current); if (keyValuePair != null) { toReturn.put(keyValuePair.getKey(), keyValuePair.getValue()); } } // All done. return toReturn; } /** * Extracts a typed old-style Enumeration from the provided Collection source. * * @param source The source Collection. * @param <T> The type of element enumerated by the returned Enumeration. * @return An Enumeration iterating over the provided source. */ public static <T> Enumeration<T> enumerate(final Collection<T> source) { final Iterator<T> it = source.iterator(); return new Enumeration<T>() { @Override public boolean hasMoreElements() { return it.hasNext(); } @Override public T nextElement() { return it.next(); } }; } // // Private helpers // @SuppressWarnings("unchecked") private static <T, C extends Collection<T>> C cloneEmptyFromType(final Class<C> type) { Validate.notNull(type, "type"); C toReturn = null; try { // Do we have a default Constructor in type? final Constructor<C> defaultConstructor = type.getConstructor(); if (defaultConstructor != null) { toReturn = defaultConstructor.newInstance(); } } catch (Exception e) { // Ignore this - fallback to default instances. } if (toReturn == null) { // Fallback. if (Set.class.isAssignableFrom(type)) { toReturn = (C) new TreeSet<C>(); } else if (List.class.isAssignableFrom(type)) { toReturn = (C) new ArrayList<T>(); } else { // this is a collection (potentially abstract) toReturn = (C) new ArrayList<T>(); } } // All done. return toReturn; } @SuppressWarnings("unchecked") private static <T, C extends Collection<T>> C cloneEmpty(final C source) { return (C) cloneEmptyFromType(source.getClass()); } }