1/*
2 * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.util;
24
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.Collection;
28import java.util.Collections;
29import java.util.Iterator;
30import java.util.List;
31import java.util.Objects;
32import java.util.function.Function;
33import java.util.function.IntFunction;
34import java.util.function.Predicate;
35import java.util.function.Supplier;
36
37/**
38 * This class contains utility methods for commonly used functional patterns for collections.
39 */
40public class CollectionsUtil {
41
42    /**
43     * Concatenates two iterables into a single iterable. The iterator exposed by the returned
44     * iterable does not support {@link Iterator#remove()} even if the input iterables do.
45     *
46     * @throws NullPointerException if {@code a} or {@code b} is {@code null}
47     */
48    public static <T> Iterable<T> concat(Iterable<T> a, Iterable<T> b) {
49        List<Iterable<T>> l = Arrays.asList(a, b);
50        return concat(l);
51    }
52
53    /**
54     * Concatenates multiple iterables into a single iterable. The iterator exposed by the returned
55     * iterable does not support {@link Iterator#remove()} even if the input iterables do.
56     *
57     * @throws NullPointerException if {@code iterables} or any of its elements are {@code null}
58     */
59    public static <T> Iterable<T> concat(List<Iterable<T>> iterables) {
60        for (Iterable<T> iterable : iterables) {
61            Objects.requireNonNull(iterable);
62        }
63        return new Iterable<T>() {
64            @Override
65            public Iterator<T> iterator() {
66                if (iterables.size() == 0) {
67                    return Collections.emptyIterator();
68                }
69                return new Iterator<T>() {
70                    Iterator<Iterable<T>> cursor = iterables.iterator();
71                    Iterator<T> currentIterator = cursor.next().iterator();
72
73                    private void advance() {
74                        while (!currentIterator.hasNext() && cursor.hasNext()) {
75                            currentIterator = cursor.next().iterator();
76                        }
77                    }
78
79                    @Override
80                    public boolean hasNext() {
81                        advance();
82                        return currentIterator.hasNext();
83                    }
84
85                    @Override
86                    public T next() {
87                        advance();
88                        return currentIterator.next();
89                    }
90                };
91            }
92
93        };
94    }
95
96    public static <T> boolean allMatch(T[] inputs, Predicate<T> predicate) {
97        return allMatch(Arrays.asList(inputs), predicate);
98    }
99
100    public static <T> boolean allMatch(Iterable<T> inputs, Predicate<T> predicate) {
101        for (T t : inputs) {
102            if (!predicate.test(t)) {
103                return false;
104            }
105        }
106        return true;
107    }
108
109    public static <T> boolean anyMatch(T[] inputs, Predicate<T> predicate) {
110        return anyMatch(Arrays.asList(inputs), predicate);
111    }
112
113    public static <T> boolean anyMatch(Iterable<T> inputs, Predicate<T> predicate) {
114        for (T t : inputs) {
115            if (predicate.test(t)) {
116                return true;
117            }
118        }
119        return false;
120    }
121
122    public static <T> List<T> filterToList(List<T> inputs, Predicate<? super T> predicate) {
123        return filterToList(inputs, predicate, ArrayList::new);
124    }
125
126    public static <T> List<T> filterToList(List<T> inputs, Predicate<? super T> predicate, Supplier<List<T>> listGenerator) {
127        List<T> resultList = listGenerator.get();
128        for (T t : inputs) {
129            if (predicate.test(t)) {
130                resultList.add(t);
131            }
132        }
133        return resultList;
134    }
135
136    /**
137     * Filters the inputs, maps them given the mapping function and adds them in the array provided
138     * by the generator.
139     */
140    public static <T, R> R[] filterAndMapToArray(T[] inputs, Predicate<? super T> predicate, Function<? super T, ? extends R> mapper, IntFunction<R[]> arrayGenerator) {
141        List<R> resultList = new ArrayList<>();
142        for (T t : inputs) {
143            if (predicate.test(t)) {
144                resultList.add(mapper.apply(t));
145            }
146        }
147        return resultList.toArray(arrayGenerator.apply(resultList.size()));
148    }
149
150    /**
151     * Maps the inputs given the mapping function and adds them in the array provided by the
152     * generator.
153     */
154    public static <T, R> R[] mapToArray(T[] inputs, Function<? super T, ? extends R> mapper, IntFunction<R[]> arrayGenerator) {
155        return mapToArray(Arrays.asList(inputs), mapper, arrayGenerator);
156    }
157
158    public static <T, R> R[] mapToArray(Collection<T> inputs, Function<? super T, ? extends R> mapper, IntFunction<R[]> arrayGenerator) {
159        R[] result = arrayGenerator.apply(inputs.size());
160        int idx = 0;
161        for (T t : inputs) {
162            result[idx++] = mapper.apply(t);
163        }
164        return result;
165    }
166
167    public static <T, R> String mapAndJoin(T[] inputs, Function<? super T, ? extends R> mapper, String delimiter) {
168        return mapAndJoin(Arrays.asList(inputs), mapper, delimiter, "", "");
169    }
170
171    public static <T, R> String mapAndJoin(T[] inputs, Function<? super T, ? extends R> mapper, String delimiter, String prefix) {
172        return mapAndJoin(Arrays.asList(inputs), mapper, delimiter, prefix, "");
173    }
174
175    public static <T, R> String mapAndJoin(T[] inputs, Function<? super T, ? extends R> mapper, String delimiter, String prefix, String suffix) {
176        return mapAndJoin(Arrays.asList(inputs), mapper, delimiter, prefix, suffix);
177    }
178
179    public static <T, R> String mapAndJoin(Iterable<T> inputs, Function<? super T, ? extends R> mapper, String delimiter) {
180        return mapAndJoin(inputs, mapper, delimiter, "", "");
181    }
182
183    public static <T, R> String mapAndJoin(Iterable<T> inputs, Function<? super T, ? extends R> mapper, String delimiter, String prefix) {
184        return mapAndJoin(inputs, mapper, delimiter, prefix, "");
185    }
186
187    public static <T, R> String mapAndJoin(Iterable<T> inputs, Function<? super T, ? extends R> mapper, String delimiter, String prefix, String suffix) {
188        StringBuilder strb = new StringBuilder();
189        String sep = "";
190        for (T t : inputs) {
191            strb.append(sep).append(prefix).append(mapper.apply(t)).append(suffix);
192            sep = delimiter;
193        }
194        return strb.toString();
195    }
196}
197