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 package com.android.internal.telephony.util;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 
21 import java.lang.reflect.Array;
22 import java.util.Collection;
23 import java.util.Map;
24 import java.util.Objects;
25 
26 /** Utility methods for array operations. */
27 public final class ArrayUtils {
ArrayUtils()28     private ArrayUtils() { /* cannot be instantiated */ }
29 
30     /**
31      * Adds value to given array if not already present, providing set-like behavior.
32      *
33      * @param kind    The class of the array elements.
34      * @param array   The array to append to.
35      * @param element The array element to append.
36      * @return The array containing the appended element.
37      */
38     @SuppressWarnings("unchecked")
39     @NonNull
appendElement(Class<T> kind, @Nullable T[] array, T element)40     public static <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
41         return appendElement(kind, array, element, false);
42     }
43 
44     /**
45      * Adds value to given array.
46      *
47      * @param kind            The class of the array elements.
48      * @param array           The array to append to.
49      * @param element         The array element to append.
50      * @param allowDuplicates Whether to allow duplicated elements in array.
51      * @return The array containing the appended element.
52      */
53     @SuppressWarnings("unchecked")
54     @NonNull
appendElement(Class<T> kind, @Nullable T[] array, T element, boolean allowDuplicates)55     public static <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element,
56             boolean allowDuplicates) {
57         final T[] result;
58         final int end;
59         if (array != null) {
60             if (!allowDuplicates && contains(array, element)) return array;
61             end = array.length;
62             result = (T[]) Array.newInstance(kind, end + 1);
63             System.arraycopy(array, 0, result, 0, end);
64         } else {
65             end = 0;
66             result = (T[]) Array.newInstance(kind, 1);
67         }
68         result[end] = element;
69         return result;
70     }
71 
72     /**
73      * Combine multiple arrays into a single array.
74      *
75      * @param kind   The class of the array elements
76      * @param arrays The arrays to combine
77      * @param <T>    The class of the array elements (inferred from kind).
78      * @return A single array containing all the elements of the parameter arrays.
79      */
80     @SuppressWarnings("unchecked")
81     @NonNull
concatElements(Class<T> kind, @Nullable T[]... arrays)82     public static <T> T[] concatElements(Class<T> kind, @Nullable T[]... arrays) {
83         if (arrays == null || arrays.length == 0) {
84             return createEmptyArray(kind);
85         }
86 
87         int totalLength = 0;
88         for (T[] item : arrays) {
89             if (item == null) {
90                 continue;
91             }
92 
93             totalLength += item.length;
94         }
95 
96         // Optimization for entirely empty arrays.
97         if (totalLength == 0) {
98             return createEmptyArray(kind);
99         }
100 
101         final T[] all = (T[]) Array.newInstance(kind, totalLength);
102         int pos = 0;
103         for (T[] item : arrays) {
104             if (item == null || item.length == 0) {
105                 continue;
106             }
107             System.arraycopy(item, 0, all, pos, item.length);
108             pos += item.length;
109         }
110         return all;
111     }
112 
createEmptyArray(Class<T> kind)113     private static @NonNull <T> T[] createEmptyArray(Class<T> kind) {
114         if (kind == String.class) {
115             return (T[]) EmptyArray.STRING;
116         } else if (kind == Object.class) {
117             return (T[]) EmptyArray.OBJECT;
118         }
119 
120         return (T[]) Array.newInstance(kind, 0);
121     }
122 
123     private static final class EmptyArray {
EmptyArray()124         private EmptyArray() {}
125 
126         public static final Object[] OBJECT = new Object[0];
127         public static final String[] STRING = new String[0];
128     }
129 
130     /**
131      * Checks if {@code value} is in {@code array}.
132      */
contains(@ullable char[] array, char value)133     public static boolean contains(@Nullable char[] array, char value) {
134         if (array == null) return false;
135         for (char element : array) {
136             if (element == value) {
137                 return true;
138             }
139         }
140         return false;
141     }
142 
143     /**
144      * Checks if {@code value} is in {@code array}.
145      */
contains(@ullable Collection<T> cur, T val)146     public static <T> boolean contains(@Nullable Collection<T> cur, T val) {
147         return (cur != null) ? cur.contains(val) : false;
148     }
149 
150     /**
151      * Checks if {@code value} is in {@code array}.
152      */
contains(@ullable int[] array, int value)153     public static boolean contains(@Nullable int[] array, int value) {
154         if (array == null) return false;
155         for (int element : array) {
156             if (element == value) {
157                 return true;
158             }
159         }
160         return false;
161     }
162 
163     /**
164      * Checks if {@code value} is in {@code array}.
165      */
contains(@ullable long[] array, long value)166     public static boolean contains(@Nullable long[] array, long value) {
167         if (array == null) return false;
168         for (long element : array) {
169             if (element == value) {
170                 return true;
171             }
172         }
173         return false;
174     }
175 
176     /**
177      * Checks if {@code value} is in {@code array}.
178      */
contains(@ullable T[] array, T value)179     public static <T> boolean contains(@Nullable T[] array, T value) {
180         return indexOf(array, value) != -1;
181     }
182 
183     /**
184      * Return first index of {@code value} in {@code array}, or {@code -1} if
185      * not found.
186      */
indexOf(@ullable T[] array, T value)187     public static <T> int indexOf(@Nullable T[] array, T value) {
188         if (array == null) return -1;
189         for (int i = 0; i < array.length; i++) {
190             if (Objects.equals(array[i], value)) return i;
191         }
192         return -1;
193     }
194 
195     /**
196      * Checks if given array is null or has zero elements.
197      */
isEmpty(@ullable Collection<?> array)198     public static boolean isEmpty(@Nullable Collection<?> array) {
199         return array == null || array.isEmpty();
200     }
201 
202     /**
203      * Checks if given map is null or has zero elements.
204      */
isEmpty(@ullable Map<?, ?> map)205     public static boolean isEmpty(@Nullable Map<?, ?> map) {
206         return map == null || map.isEmpty();
207     }
208 
209     /**
210      * Checks if given array is null or has zero elements.
211      */
isEmpty(@ullable T[] array)212     public static <T> boolean isEmpty(@Nullable T[] array) {
213         return array == null || array.length == 0;
214     }
215 
216     /**
217      * Checks if given array is null or has zero elements.
218      */
isEmpty(@ullable int[] array)219     public static boolean isEmpty(@Nullable int[] array) {
220         return array == null || array.length == 0;
221     }
222 
223     /**
224      * Checks if given array is null or has zero elements.
225      */
isEmpty(@ullable long[] array)226     public static boolean isEmpty(@Nullable long[] array) {
227         return array == null || array.length == 0;
228     }
229 
230     /**
231      * Checks if given array is null or has zero elements.
232      */
isEmpty(@ullable byte[] array)233     public static boolean isEmpty(@Nullable byte[] array) {
234         return array == null || array.length == 0;
235     }
236 
237     /**
238      * Checks if given array is null or has zero elements.
239      */
isEmpty(@ullable boolean[] array)240     public static boolean isEmpty(@Nullable boolean[] array) {
241         return array == null || array.length == 0;
242     }
243 }
244