1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.net.module.util;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.util.ArrayMap;
22 import android.util.Pair;
23 import android.util.SparseArray;
24 
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.List;
28 import java.util.Objects;
29 import java.util.function.Function;
30 import java.util.function.Predicate;
31 
32 /**
33  * Utilities for {@link Collection} and arrays.
34  * @hide
35  */
36 public final class CollectionUtils {
CollectionUtils()37     private CollectionUtils() {}
38 
39     /**
40      * @return True if the array is null or 0-length.
41      */
isEmpty(@ullable T[] array)42     public static <T> boolean isEmpty(@Nullable T[] array) {
43         return array == null || array.length == 0;
44     }
45 
46     /**
47      * @return True if the collection is null or 0-length.
48      */
isEmpty(@ullable Collection<T> collection)49     public static <T> boolean isEmpty(@Nullable Collection<T> collection) {
50         return collection == null || collection.isEmpty();
51     }
52 
53     /**
54      * Returns an int array from the given Integer list.
55      */
56     @NonNull
toIntArray(@onNull Collection<Integer> list)57     public static int[] toIntArray(@NonNull Collection<Integer> list) {
58         int[] array = new int[list.size()];
59         int i = 0;
60         for (Integer item : list) {
61             array[i] = item;
62             i++;
63         }
64         return array;
65     }
66 
67     /**
68      * Returns a long array from the given long list.
69      */
70     @NonNull
toLongArray(@onNull Collection<Long> list)71     public static long[] toLongArray(@NonNull Collection<Long> list) {
72         long[] array = new long[list.size()];
73         int i = 0;
74         for (Long item : list) {
75             array[i] = item;
76             i++;
77         }
78         return array;
79     }
80 
81     /**
82      * @return True if all elements satisfy the predicate, false otherwise.
83      *   Note that means this always returns true for empty collections.
84      */
all(@onNull Collection<T> elem, @NonNull Predicate<T> predicate)85     public static <T> boolean all(@NonNull Collection<T> elem, @NonNull Predicate<T> predicate) {
86         for (final T e : elem) {
87             if (!predicate.test(e)) return false;
88         }
89         return true;
90 
91     }
92 
93     /**
94      * @return True if any element satisfies the predicate, false otherwise.
95      *   Note that means this always returns false for empty collections.
96      */
any(@onNull Collection<T> elem, @NonNull Predicate<T> predicate)97     public static <T> boolean any(@NonNull Collection<T> elem, @NonNull Predicate<T> predicate) {
98         return indexOf(elem, predicate) >= 0;
99     }
100 
101     /**
102      * @return The index of the first element that matches the predicate, or -1 if none.
103      */
indexOf(@onNull final Collection<T> elem, @NonNull final Predicate<? super T> predicate)104     public static <T> int indexOf(@NonNull final Collection<T> elem,
105             @NonNull final Predicate<? super T> predicate) {
106         int idx = 0;
107         for (final T e : elem) {
108             if (predicate.test(e)) return idx;
109             idx++;
110         }
111         return -1;
112     }
113 
114     /**
115      * @return True if there exists at least one element in the sparse array for which
116      * condition {@code predicate}
117      */
any(@onNull SparseArray<T> array, @NonNull Predicate<T> predicate)118     public static <T> boolean any(@NonNull SparseArray<T> array, @NonNull Predicate<T> predicate) {
119         for (int i = 0; i < array.size(); ++i) {
120             if (predicate.test(array.valueAt(i))) {
121                 return true;
122             }
123         }
124         return false;
125     }
126 
127     /**
128      * @return true if the array contains the specified value.
129      */
contains(@ullable short[] array, short value)130     public static boolean contains(@Nullable short[] array, short value) {
131         if (array == null) return false;
132         for (int element : array) {
133             if (element == value) {
134                 return true;
135             }
136         }
137         return false;
138     }
139 
140     /**
141      * @return true if the array contains the specified value.
142      */
contains(@ullable int[] array, int value)143     public static boolean contains(@Nullable int[] array, int value) {
144         if (array == null) return false;
145         for (int element : array) {
146             if (element == value) {
147                 return true;
148             }
149         }
150         return false;
151     }
152 
153     /**
154      * @return true if the array contains the specified value.
155      */
contains(@ullable T[] array, @Nullable T value)156     public static <T> boolean contains(@Nullable T[] array, @Nullable T value) {
157         return indexOf(array, value) != -1;
158     }
159 
160     /**
161      * Return first index of value in given array, or -1 if not found.
162      */
indexOf(@ullable T[] array, @Nullable T value)163     public static <T> int indexOf(@Nullable T[] array, @Nullable T value) {
164         if (array == null) return -1;
165         for (int i = 0; i < array.length; i++) {
166             if (Objects.equals(array[i], value)) return i;
167         }
168         return -1;
169     }
170 
171     /**
172      * Returns the index of the needle array in the haystack array, or -1 if it can't be found.
173      * This is a byte array equivalent of Collections.indexOfSubList().
174      */
indexOfSubArray(@onNull byte[] haystack, @NonNull byte[] needle)175     public static int indexOfSubArray(@NonNull byte[] haystack, @NonNull byte[] needle) {
176         for (int i = 0; i < haystack.length - needle.length + 1; i++) {
177             boolean found = true;
178             for (int j = 0; j < needle.length; j++) {
179                 if (haystack[i + j] != needle[j]) {
180                     found = false;
181                     break;
182                 }
183             }
184             if (found) {
185                 return i;
186             }
187         }
188         return -1;
189     }
190 
191     /**
192      * Returns a new collection of elements that match the passed predicate.
193      * @param source the elements to filter.
194      * @param test the predicate to test for.
195      * @return a new collection containing only the source elements that satisfy the predicate.
196      */
filter(@onNull final Collection<T> source, @NonNull final Predicate<T> test)197     @NonNull public static <T> ArrayList<T> filter(@NonNull final Collection<T> source,
198             @NonNull final Predicate<T> test) {
199         final ArrayList<T> matches = new ArrayList<>();
200         for (final T e : source) {
201             if (test.test(e)) {
202                 matches.add(e);
203             }
204         }
205         return matches;
206     }
207 
208     /**
209      * Return sum of the given long array.
210      */
total(@ullable long[] array)211     public static long total(@Nullable long[] array) {
212         long total = 0;
213         if (array != null) {
214             for (long value : array) {
215                 total += value;
216             }
217         }
218         return total;
219     }
220 
221     /**
222      * Returns true if the first collection contains any of the elements of the second.
223      * @param haystack where to search
224      * @param needles what to search for
225      * @param <T> type of elements
226      * @return true if |haystack| contains any of the |needles|, false otherwise
227      */
containsAny(@onNull final Collection<T> haystack, @NonNull final Collection<? extends T> needles)228     public static <T> boolean containsAny(@NonNull final Collection<T> haystack,
229             @NonNull final Collection<? extends T> needles) {
230         for (T needle : needles) {
231             if (haystack.contains(needle)) return true;
232         }
233         return false;
234     }
235 
236     /**
237      * Returns true if the first collection contains all of the elements of the second.
238      * @param haystack where to search
239      * @param needles what to search for
240      * @param <T> type of elements
241      * @return true if |haystack| contains all of the |needles|, false otherwise
242      */
containsAll(@onNull final Collection<T> haystack, @NonNull final Collection<? extends T> needles)243     public static <T> boolean containsAll(@NonNull final Collection<T> haystack,
244             @NonNull final Collection<? extends T> needles) {
245         return haystack.containsAll(needles);
246     }
247 
248     /**
249      * Returns the first item of a collection that matches the predicate.
250      * @param haystack The collection to search.
251      * @param condition The predicate to match.
252      * @param <T> The type of element in the collection.
253      * @return The first element matching the predicate, or null if none.
254      */
255     @Nullable
findFirst(@onNull final Collection<T> haystack, @NonNull final Predicate<? super T> condition)256     public static <T> T findFirst(@NonNull final Collection<T> haystack,
257             @NonNull final Predicate<? super T> condition) {
258         for (T needle : haystack) {
259             if (condition.test(needle)) return needle;
260         }
261         return null;
262     }
263 
264     /**
265      * Returns the last item of a List that matches the predicate.
266      * @param haystack The List to search.
267      * @param condition The predicate to match.
268      * @param <T> The type of element in the list.
269      * @return The last element matching the predicate, or null if none.
270      */
271     // There is no way to reverse iterate a Collection in Java (e.g. the collection may
272     // be a single-linked list), so implementing this on Collection is necessarily very
273     // wasteful (store and reverse a copy, test all elements, or recurse to the end of the
274     // list to test on the up path and possibly blow the call stack)
275     @Nullable
findLast(@onNull final List<T> haystack, @NonNull final Predicate<? super T> condition)276     public static <T> T findLast(@NonNull final List<T> haystack,
277             @NonNull final Predicate<? super T> condition) {
278         for (int i = haystack.size() - 1; i >= 0; --i) {
279             final T needle = haystack.get(i);
280             if (condition.test(needle)) return needle;
281         }
282         return null;
283     }
284 
285     /**
286      * Returns whether a collection contains an element matching a condition
287      * @param haystack The collection to search.
288      * @param condition The predicate to match.
289      * @param <T> The type of element in the collection.
290      * @return Whether the collection contains any element matching the condition.
291      */
contains(@onNull final Collection<T> haystack, @NonNull final Predicate<? super T> condition)292     public static <T> boolean contains(@NonNull final Collection<T> haystack,
293             @NonNull final Predicate<? super T> condition) {
294         return -1 != indexOf(haystack, condition);
295     }
296 
297     /**
298      * Standard map function, but returns a new modifiable ArrayList
299      *
300      * This returns a new list that contains, for each element of the source collection, its
301      * image through the passed transform.
302      * Elements in the source can be null if the transform accepts null inputs.
303      * Elements in the output can be null if the transform ever returns null.
304      * This function never returns null. If the source collection is empty, it returns the
305      * empty list.
306      * Contract : this method calls the transform function exactly once for each element in the
307      * list, in iteration order.
308      *
309      * @param source the source collection
310      * @param transform the function to transform the elements
311      * @param <T> type of source elements
312      * @param <R> type of destination elements
313      * @return an unmodifiable list of transformed elements
314      */
315     @NonNull
map(@onNull final Collection<T> source, @NonNull final Function<? super T, ? extends R> transform)316     public static <T, R> ArrayList<R> map(@NonNull final Collection<T> source,
317             @NonNull final Function<? super T, ? extends R> transform) {
318         final ArrayList<R> dest = new ArrayList<>(source.size());
319         for (final T e : source) {
320             dest.add(transform.apply(e));
321         }
322         return dest;
323     }
324 
325     /**
326      * Standard zip function, but returns a new modifiable ArrayList
327      *
328      * This returns a list of pairs containing, at each position, a pair of the element from the
329      * first list at that index and the element from the second list at that index.
330      * Both lists must be the same size. They may contain null.
331      *
332      * The easiest way to visualize what's happening is to think of two lists being laid out next
333      * to each other and stitched together with a zipper.
334      *
335      * Contract : this method will read each element of each list exactly once, in some unspecified
336      * order. If it throws, it will not read any element.
337      *
338      * @param first the first list of elements
339      * @param second the second list of elements
340      * @param <T> the type of first elements
341      * @param <R> the type of second elements
342      * @return the zipped list
343      */
344     @NonNull
zip(@onNull final List<T> first, @NonNull final List<R> second)345     public static <T, R> ArrayList<Pair<T, R>> zip(@NonNull final List<T> first,
346             @NonNull final List<R> second) {
347         final int size = first.size();
348         if (size != second.size()) {
349             throw new IllegalArgumentException("zip : collections must be the same size");
350         }
351         final ArrayList<Pair<T, R>> dest = new ArrayList<>(size);
352         for (int i = 0; i < size; ++i) {
353             dest.add(new Pair<>(first.get(i), second.get(i)));
354         }
355         return dest;
356     }
357 
358     /**
359      * Returns a new ArrayMap that associates each key with the value at the same index.
360      *
361      * Both lists must be the same size.
362      * Both keys and values may contain null.
363      * Keys may not contain the same value twice.
364      *
365      * Contract : this method will read each element of each list exactly once, but does not
366      * specify the order, except if it throws in which case the number of reads is undefined.
367      *
368      * @param keys The list of keys
369      * @param values The list of values
370      * @param <T> The type of keys
371      * @param <R> The type of values
372      * @return The associated map
373      */
374     @NonNull
assoc( @onNull final List<T> keys, @NonNull final List<R> values)375     public static <T, R> ArrayMap<T, R> assoc(
376             @NonNull final List<T> keys, @NonNull final List<R> values) {
377         final int size = keys.size();
378         if (size != values.size()) {
379             throw new IllegalArgumentException("assoc : collections must be the same size");
380         }
381         final ArrayMap<T, R> dest = new ArrayMap<>(size);
382         for (int i = 0; i < size; ++i) {
383             final T key = keys.get(i);
384             if (dest.containsKey(key)) {
385                 throw new IllegalArgumentException(
386                         "assoc : keys may not contain the same value twice");
387             }
388             dest.put(key, values.get(i));
389         }
390         return dest;
391     }
392 
393     /**
394      * Returns an index of the given SparseArray that contains the given value, or -1
395      * number if no keys map to the given value.
396      *
397      * <p>Note this is a linear search, and if multiple keys can map to the same value
398      * then the smallest index is returned.
399      *
400      * <p>This function compares values with {@code equals} while the
401      * {@link SparseArray#indexOfValue} compares values using {@code ==}.
402      */
getIndexForValue(SparseArray<T> sparseArray, T value)403     public static <T> int getIndexForValue(SparseArray<T> sparseArray, T value) {
404         for(int i = 0, nsize = sparseArray.size(); i < nsize; i++) {
405             T valueAt = sparseArray.valueAt(i);
406             if (valueAt == null) {
407                 if (value == null) {
408                     return i;
409                 };
410             } else if (valueAt.equals(value)) {
411                 return i;
412             }
413         }
414         return -1;
415     }
416 }
417