View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.common;
20  
21  import java.util.Collection;
22  import java.util.Enumeration;
23  import java.util.Map;
24  import java.util.Spliterator;
25  import java.util.Spliterators;
26  import java.util.concurrent.ConcurrentHashMap;
27  import java.util.function.Function;
28  import java.util.function.Predicate;
29  import java.util.stream.Stream;
30  import java.util.stream.StreamSupport;
31  
32  import com.google.common.collect.Iterators;
33  
34  /**
35   * Helper methods to handle common Stream use cases.
36   *
37   * @author Thomas Scheffler (yagee)
38   */
39  public class MCRStreamUtils {
40  
41      /**
42       * Short circuit for calling <code>flatten(node, subNodeSupplier, subNodeSupplier, t -&gt; true)</code>
43       * @param node node that holds kind-of subtree.
44       * @param subNodeSupplier a function that delivers subtree items of next level
45       * @param streamProvider a function that makes a Stream of a Collection&lt;T&gt;, usually <code>Collection::stream</code> or <code>Collection::parallelStream</code>
46       * @see #flatten(Object, Function, Function, Predicate)
47       * @since 2016.04
48       */
49      public static <T> Stream<T> flatten(T node, Function<T, Collection<T>> subNodeSupplier,
50          Function<Collection<T>, Stream<T>> streamProvider) {
51          return Stream.concat(Stream.of(node), subNodeSupplier.andThen(streamProvider).apply(node).flatMap(
52              subNode -> flatten(subNode, subNodeSupplier, streamProvider)));
53      }
54  
55      /**
56       * Example:
57       * <pre>
58       *   MCRCategory foo = MCRCategoryDAOFactory.getInstance().getCategory(MCRCategoryID.rootID("foo"), -1);
59       *   Stream&lt;MCRCategory&gt; parentCategories = flatten(foo, MCRCategory::getChildren, true, MCRCategory::hasChildren);
60       * </pre>
61       * @param node first node the stream is made of
62       * @param subNodeSupplier a function that delivers subtree items of next level
63       * @param streamProvider a function that makes a Stream of a Collection&lt;T&gt;, usually <code>Collection::stream</code> or <code>Collection::parallelStream</code>
64       * @param filter a predicate that filters the element of the next level
65       * @since 2016.04
66       */
67      public static <T> Stream<T> flatten(T node, Function<T, Collection<T>> subNodeSupplier,
68          Function<Collection<T>, Stream<T>> streamProvider, Predicate<T> filter) {
69          return Stream.concat(Stream.of(node),
70              subNodeSupplier.andThen(streamProvider).apply(node).filter(filter).flatMap(
71                  subNode -> flatten(subNode, subNodeSupplier, streamProvider, filter)));
72      }
73  
74      /**
75       * Transforms an Enumeration in a Stream.
76       * @param e the enumeration to transform
77       * @return a sequential, ordered Stream of unknown size
78       */
79      public static <T> Stream<T> asStream(Enumeration<T> e) {
80          return StreamSupport.stream(
81              Spliterators.spliteratorUnknownSize(Iterators.forEnumeration(e), Spliterator.ORDERED), false);
82      }
83  
84      /**
85       * Concats any number of Streams not just 2 as in {@link Stream#concat(Stream, Stream)}.
86       * @since 2016.04
87       */
88      @SafeVarargs
89      public static <T> Stream<T> concat(Stream<T>... streams) {
90          return Stream.of(streams).reduce(Stream::concat).orElse(Stream.empty());
91      }
92  
93      /**
94       * Stream distinct by filter function.
95       * <p>
96       * <code>
97       * persons.stream().filter(MCRStreamUtils.distinctByKey(p -&gt; p.getName());
98       * </code>
99       * </p>
100      * It should be noted that for ordered parallel stream this solution does not guarantee
101      * which object will be extracted (unlike normal distinct()).
102      * 
103      * @see <a href="https://stackoverflow.com/questions/23699371/java-8-distinct-by-property">stackoverflow</a>
104      * @param keyExtractor a compare function
105      * @return a predicate
106      */
107     public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
108         Map<Object, Boolean> seen = new ConcurrentHashMap<>();
109         return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
110     }
111 
112     /**
113      * Negates a predicate.
114      *
115      * @see <a href="https://stackoverflow.com/questions/28235764/how-can-i-negate-a-lambda-predicate">stackoverflow</a>
116      * @param predicate the predicate to negate
117      * @return the negated predicate
118      */
119     public static <T> Predicate<T> not(Predicate<T> predicate) {
120         return predicate.negate();
121     }
122 
123 }