1 /* 2 * Copyright (C) 2021 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.car.internal.util; 18 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 20 import static com.android.car.internal.common.CommonConstants.EMPTY_INT_ARRAY; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.util.ArraySet; 25 26 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 27 28 import dalvik.system.VMRuntime; 29 30 import java.io.File; 31 import java.lang.reflect.Array; 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.Collection; 35 import java.util.Collections; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Objects; 39 import java.util.Set; 40 import java.util.function.IntFunction; 41 42 // Copy of frameworks/base/core/java/com/android/internal/util/ArrayUtils.java 43 /** 44 * ArrayUtils contains some methods that you can call to find out 45 * the most efficient increments by which to grow arrays. 46 */ 47 public final class ArrayUtils { 48 private static final int CACHE_SIZE = 73; 49 private static Object[] sCache = new Object[CACHE_SIZE]; 50 51 public static final File[] EMPTY_FILE = new File[0]; 52 ArrayUtils()53 private ArrayUtils() { /* cannot be instantiated */ } 54 55 /** See {@link VMRuntime#newUnpaddedArray} for details. */ newUnpaddedLongArray(int minLen)56 public static long[] newUnpaddedLongArray(int minLen) { 57 return (long[]) VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen); 58 } 59 60 /** 61 * Checks if the beginnings of two byte arrays are equal. 62 * 63 * @param array1 the first byte array 64 * @param array2 the second byte array 65 * @param length the number of bytes to check 66 * @return true if they're equal, false otherwise 67 */ equals(byte[] array1, byte[] array2, int length)68 public static boolean equals(byte[] array1, byte[] array2, int length) { 69 if (length < 0) { 70 throw new IllegalArgumentException(); 71 } 72 73 if (array1 == array2) { 74 return true; 75 } 76 if (array1 == null || array2 == null || array1.length < length || array2.length < length) { 77 return false; 78 } 79 for (int i = 0; i < length; i++) { 80 if (array1[i] != array2[i]) { 81 return false; 82 } 83 } 84 return true; 85 } 86 87 /** 88 * Returns an empty array of the specified type. The intent is that 89 * it will return the same empty array every time to avoid reallocation, 90 * although this is not guaranteed. 91 */ 92 @SuppressWarnings("unchecked") emptyArray(Class<T> kind)93 public static <T> T[] emptyArray(Class<T> kind) { 94 if (kind == Object.class) { 95 return (T[]) new Object[0]; 96 } 97 98 int bucket = (kind.hashCode() & 0x7FFFFFFF) % CACHE_SIZE; 99 Object cache = sCache[bucket]; 100 101 if (cache == null || cache.getClass().getComponentType() != kind) { 102 cache = Array.newInstance(kind, 0); 103 sCache[bucket] = cache; 104 105 // Slog.e("cache", "new empty " + kind.getName() + " at " + bucket); 106 } 107 108 return (T[]) cache; 109 } 110 111 /** 112 * Returns the same array or an empty one if it's null. 113 */ 114 @NonNull emptyIfNull(@ullable T[] items, Class<T> kind)115 public static <T> T[] emptyIfNull(@Nullable T[] items, Class<T> kind) { 116 return items != null ? items : emptyArray(kind); 117 } 118 119 /** 120 * Checks if given array is null or has zero elements. 121 */ isEmpty(@ullable Collection<?> array)122 public static boolean isEmpty(@Nullable Collection<?> array) { 123 return array == null || array.isEmpty(); 124 } 125 126 /** 127 * Checks if given map is null or has zero elements. 128 */ isEmpty(@ullable Map<?, ?> map)129 public static boolean isEmpty(@Nullable Map<?, ?> map) { 130 return map == null || map.isEmpty(); 131 } 132 133 /** 134 * Checks if given array is null or has zero elements. 135 */ isEmpty(@ullable T[] array)136 public static <T> boolean isEmpty(@Nullable T[] array) { 137 return array == null || array.length == 0; 138 } 139 140 /** 141 * Checks if given array is null or has zero elements. 142 */ isEmpty(@ullable int[] array)143 public static boolean isEmpty(@Nullable int[] array) { 144 return array == null || array.length == 0; 145 } 146 147 /** 148 * Checks if given array is null or has zero elements. 149 */ isEmpty(@ullable long[] array)150 public static boolean isEmpty(@Nullable long[] array) { 151 return array == null || array.length == 0; 152 } 153 154 /** 155 * Checks if given array is null or has zero elements. 156 */ isEmpty(@ullable byte[] array)157 public static boolean isEmpty(@Nullable byte[] array) { 158 return array == null || array.length == 0; 159 } 160 161 /** 162 * Checks if given array is null or has zero elements. 163 */ isEmpty(@ullable boolean[] array)164 public static boolean isEmpty(@Nullable boolean[] array) { 165 return array == null || array.length == 0; 166 } 167 168 /** 169 * Length of the given array or 0 if it's null. 170 */ size(@ullable Object[] array)171 public static int size(@Nullable Object[] array) { 172 return array == null ? 0 : array.length; 173 } 174 175 /** 176 * Length of the given collection or 0 if it's null. 177 */ size(@ullable Collection<?> collection)178 public static int size(@Nullable Collection<?> collection) { 179 return collection == null ? 0 : collection.size(); 180 } 181 182 /** 183 * Length of the given map or 0 if it's null. 184 */ size(@ullable Map<?, ?> map)185 public static int size(@Nullable Map<?, ?> map) { 186 return map == null ? 0 : map.size(); 187 } 188 189 /** 190 * Checks that value is present as at least one of the elements of the array. 191 * 192 * @param array the array to check in 193 * @param value the value to check for 194 * @return true if the value is present in the array 195 */ contains(@ullable T[] array, T value)196 public static <T> boolean contains(@Nullable T[] array, T value) { 197 return indexOf(array, value) != -1; 198 } 199 200 /** 201 * Return first index of {@code value} in {@code array}, or {@code -1} if 202 * not found. 203 */ indexOf(@ullable T[] array, T value)204 public static <T> int indexOf(@Nullable T[] array, T value) { 205 if (array == null) return -1; 206 for (int i = 0; i < array.length; i++) { 207 if (Objects.equals(array[i], value)) return i; 208 } 209 return -1; 210 } 211 212 /** 213 * Test if all {@code check} items are contained in {@code array}. 214 */ containsAll(@ullable T[] array, T[] check)215 public static <T> boolean containsAll(@Nullable T[] array, T[] check) { 216 if (check == null) return true; 217 for (T checkItem : check) { 218 if (!contains(array, checkItem)) { 219 return false; 220 } 221 } 222 return true; 223 } 224 225 /** 226 * Test if any {@code check} items are contained in {@code array}. 227 */ containsAny(@ullable T[] array, T[] check)228 public static <T> boolean containsAny(@Nullable T[] array, T[] check) { 229 if (check == null) return false; 230 for (T checkItem : check) { 231 if (contains(array, checkItem)) { 232 return true; 233 } 234 } 235 return false; 236 } 237 238 /** Checks if value is contained in the array */ contains(@ullable int[] array, int value)239 public static boolean contains(@Nullable int[] array, int value) { 240 if (array == null) return false; 241 for (int element : array) { 242 if (element == value) { 243 return true; 244 } 245 } 246 return false; 247 } 248 249 /** Checks if value is contained in the array */ contains(@ullable long[] array, long value)250 public static boolean contains(@Nullable long[] array, long value) { 251 if (array == null) return false; 252 for (long element : array) { 253 if (element == value) { 254 return true; 255 } 256 } 257 return false; 258 } 259 260 /** Checks if value is contained in the array */ contains(@ullable char[] array, char value)261 public static boolean contains(@Nullable char[] array, char value) { 262 if (array == null) return false; 263 for (char element : array) { 264 if (element == value) { 265 return true; 266 } 267 } 268 return false; 269 } 270 271 /** 272 * Test if all {@code check} items are contained in {@code array}. 273 */ containsAll(@ullable char[] array, char[] check)274 public static <T> boolean containsAll(@Nullable char[] array, char[] check) { 275 if (check == null) return true; 276 for (char checkItem : check) { 277 if (!contains(array, checkItem)) { 278 return false; 279 } 280 } 281 return true; 282 } 283 284 /** Returns sum */ total(@ullable long[] array)285 public static long total(@Nullable long[] array) { 286 long total = 0; 287 if (array != null) { 288 for (long value : array) { 289 total += value; 290 } 291 } 292 return total; 293 } 294 295 /** 296 * @deprecated use {@code IntArray} instead 297 */ 298 @Deprecated convertToIntArray(List<Integer> list)299 public static int[] convertToIntArray(List<Integer> list) { 300 int[] array = new int[list.size()]; 301 for (int i = 0; i < list.size(); i++) { 302 array[i] = list.get(i); 303 } 304 return array; 305 } 306 307 /** Cnnverts array */ 308 @Nullable convertToLongArray(@ullable int[] intArray)309 public static long[] convertToLongArray(@Nullable int[] intArray) { 310 if (intArray == null) return null; 311 long[] array = new long[intArray.length]; 312 for (int i = 0; i < intArray.length; i++) { 313 array[i] = (long) intArray[i]; 314 } 315 return array; 316 } 317 318 /** 319 * Combine multiple arrays into a single array. 320 * 321 * @param kind The class of the array elements 322 * @param arrays The arrays to combine 323 * @param <T> The class of the array elements (inferred from kind). 324 * @return A single array containing all the elements of the parameter arrays. 325 */ 326 @SuppressWarnings("unchecked") 327 @NonNull concatElements(Class<T> kind, @Nullable T[]... arrays)328 public static <T> T[] concatElements(Class<T> kind, @Nullable T[]... arrays) { 329 if (arrays == null || arrays.length == 0) { 330 return createEmptyArray(kind); 331 } 332 333 int totalLength = 0; 334 for (T[] item : arrays) { 335 if (item == null) { 336 continue; 337 } 338 339 totalLength += item.length; 340 } 341 342 // Optimization for entirely empty arrays. 343 if (totalLength == 0) { 344 return createEmptyArray(kind); 345 } 346 347 final T[] all = (T[]) Array.newInstance(kind, totalLength); 348 int pos = 0; 349 for (T[] item : arrays) { 350 if (item == null || item.length == 0) { 351 continue; 352 } 353 System.arraycopy(item, 0, all, pos, item.length); 354 pos += item.length; 355 } 356 return all; 357 } 358 359 /** Creates an empty array */ 360 @NonNull createEmptyArray(Class<T> kind)361 private static <T> T[] createEmptyArray(Class<T> kind) { 362 if (kind == String.class) { 363 return (T[]) new String[0]; 364 } else if (kind == Object.class) { 365 return (T[]) new Object[0]; 366 } 367 368 return (T[]) Array.newInstance(kind, 0); 369 } 370 371 372 /** 373 * Adds value to given array if not already present, providing set-like 374 * behavior. 375 */ 376 @NonNull 377 @SuppressWarnings("unchecked") appendElement(Class<T> kind, @Nullable T[] array, T element)378 public static <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) { 379 return appendElement(kind, array, element, false); 380 } 381 382 /** 383 * Adds value to given array. 384 */ 385 @SuppressWarnings("unchecked") 386 @NonNull appendElement(Class<T> kind, @Nullable T[] array, T element, boolean allowDuplicates)387 public static <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element, 388 boolean allowDuplicates) { 389 final T[] result; 390 final int end; 391 if (array != null) { 392 if (!allowDuplicates && contains(array, element)) return array; 393 end = array.length; 394 result = (T[]) Array.newInstance(kind, end + 1); 395 System.arraycopy(array, 0, result, 0, end); 396 } else { 397 end = 0; 398 result = (T[]) Array.newInstance(kind, 1); 399 } 400 result[end] = element; 401 return result; 402 } 403 404 /** 405 * Removes value from given array if present, providing set-like behavior. 406 */ 407 @SuppressWarnings("unchecked") 408 @Nullable removeElement(Class<T> kind, @Nullable T[] array, T element)409 public static <T> T[] removeElement(Class<T> kind, @Nullable T[] array, T element) { 410 if (array != null) { 411 if (!contains(array, element)) return array; 412 final int length = array.length; 413 for (int i = 0; i < length; i++) { 414 if (Objects.equals(array[i], element)) { 415 if (length == 1) { 416 return null; 417 } 418 T[] result = (T[]) Array.newInstance(kind, length - 1); 419 System.arraycopy(array, 0, result, 0, i); 420 System.arraycopy(array, i + 1, result, i, length - i - 1); 421 return result; 422 } 423 } 424 } 425 return array; 426 } 427 428 /** 429 * Adds value to given array. 430 */ 431 @NonNull appendInt(@ullable int[] cur, int val, boolean allowDuplicates)432 public static int[] appendInt(@Nullable int[] cur, int val, 433 boolean allowDuplicates) { 434 if (cur == null) { 435 return new int[]{val}; 436 } 437 final int n = cur.length; 438 if (!allowDuplicates) { 439 for (int i = 0; i < n; i++) { 440 if (cur[i] == val) { 441 return cur; 442 } 443 } 444 } 445 int[] ret = new int[n + 1]; 446 System.arraycopy(cur, 0, ret, 0, n); 447 ret[n] = val; 448 return ret; 449 } 450 451 /** 452 * Adds value to given array if not already present, providing set-like 453 * behavior. 454 */ 455 @NonNull appendInt(@ullable int[] cur, int val)456 public static int[] appendInt(@Nullable int[] cur, int val) { 457 return appendInt(cur, val, false); 458 } 459 460 /** 461 * Removes value from given array if present, providing set-like behavior. 462 */ 463 @Nullable removeInt(@ullable int[] cur, int val)464 public static int[] removeInt(@Nullable int[] cur, int val) { 465 if (cur == null) { 466 return null; 467 } 468 final int n = cur.length; 469 for (int i = 0; i < n; i++) { 470 if (cur[i] == val) { 471 int[] ret = new int[n - 1]; 472 if (i > 0) { 473 System.arraycopy(cur, 0, ret, 0, i); 474 } 475 if (i < (n - 1)) { 476 System.arraycopy(cur, i + 1, ret, i, n - i - 1); 477 } 478 return ret; 479 } 480 } 481 return cur; 482 } 483 484 /** 485 * Removes value from given array if present, providing set-like behavior. 486 */ 487 @Nullable removeString(@ullable String[] cur, String val)488 public static String[] removeString(@Nullable String[] cur, String val) { 489 if (cur == null) { 490 return null; 491 } 492 final int n = cur.length; 493 for (int i = 0; i < n; i++) { 494 if (Objects.equals(cur[i], val)) { 495 String[] ret = new String[n - 1]; 496 if (i > 0) { 497 System.arraycopy(cur, 0, ret, 0, i); 498 } 499 if (i < (n - 1)) { 500 System.arraycopy(cur, i + 1, ret, i, n - i - 1); 501 } 502 return ret; 503 } 504 } 505 return cur; 506 } 507 508 /** 509 * Adds value to given array if not already present, providing set-like 510 * behavior. 511 */ 512 @NonNull appendLong(@ullable long[] cur, long val, boolean allowDuplicates)513 public static long[] appendLong(@Nullable long[] cur, long val, 514 boolean allowDuplicates) { 515 if (cur == null) { 516 return new long[]{val}; 517 } 518 final int n = cur.length; 519 if (!allowDuplicates) { 520 for (int i = 0; i < n; i++) { 521 if (cur[i] == val) { 522 return cur; 523 } 524 } 525 } 526 long[] ret = new long[n + 1]; 527 System.arraycopy(cur, 0, ret, 0, n); 528 ret[n] = val; 529 return ret; 530 } 531 532 /** 533 * Adds value to given array if not already present, providing set-like 534 * behavior. 535 */ 536 @NonNull appendLong(@ullable long[] cur, long val)537 public static long[] appendLong(@Nullable long[] cur, long val) { 538 return appendLong(cur, val, false); 539 } 540 541 /** 542 * Removes value from given array if present, providing set-like behavior. 543 */ 544 @Nullable removeLong(@ullable long[] cur, long val)545 public static long[] removeLong(@Nullable long[] cur, long val) { 546 if (cur == null) { 547 return null; 548 } 549 final int n = cur.length; 550 for (int i = 0; i < n; i++) { 551 if (cur[i] == val) { 552 long[] ret = new long[n - 1]; 553 if (i > 0) { 554 System.arraycopy(cur, 0, ret, 0, i); 555 } 556 if (i < (n - 1)) { 557 System.arraycopy(cur, i + 1, ret, i, n - i - 1); 558 } 559 return ret; 560 } 561 } 562 return cur; 563 } 564 565 /** Clones if non null */ 566 @Nullable cloneOrNull(@ullable long[] array)567 public static long[] cloneOrNull(@Nullable long[] array) { 568 return (array != null) ? array.clone() : null; 569 } 570 571 /** 572 * Clones an array or returns null if the array is null. 573 */ 574 @Nullable cloneOrNull(@ullable T[] array)575 public static <T> T[] cloneOrNull(@Nullable T[] array) { 576 return (array != null) ? array.clone() : null; 577 } 578 579 /** Clones if non null */ 580 @Nullable cloneOrNull(@ullable ArraySet<T> array)581 public static <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) { 582 return (array != null) ? new ArraySet<T>(array) : null; 583 } 584 585 /** Add to array */ 586 @NonNull add(@ullable ArraySet<T> arraySet, T val)587 public static <T> ArraySet<T> add(@Nullable ArraySet<T> arraySet, T val) { 588 ArraySet<T> cur = arraySet; 589 if (cur == null) { 590 cur = new ArraySet<>(); 591 } 592 cur.add(val); 593 return cur; 594 } 595 596 /** 597 * Similar to {@link Set#addAll(Collection)}}, but with support for set values of {@code null}. 598 */ 599 @NonNull addAll(@ullable ArraySet<T> arraySet, @Nullable Collection<T> val)600 public static <T> ArraySet<T> addAll(@Nullable ArraySet<T> arraySet, 601 @Nullable Collection<T> val) { 602 ArraySet<T> cur = arraySet; 603 if (cur == null) { 604 cur = new ArraySet<>(); 605 } 606 if (val != null) { 607 cur.addAll(val); 608 } 609 return cur; 610 } 611 612 /** TODO: add javadoc */ 613 @Nullable remove(@ullable ArraySet<T> cur, T val)614 public static <T> ArraySet<T> remove(@Nullable ArraySet<T> cur, T val) { 615 if (cur == null) { 616 return null; 617 } 618 cur.remove(val); 619 if (cur.isEmpty()) { 620 return null; 621 } else { 622 return cur; 623 } 624 } 625 626 /** TODO: add javadoc */ 627 @NonNull add(@ullable ArrayList<T> arraySet, T val)628 public static <T> ArrayList<T> add(@Nullable ArrayList<T> arraySet, T val) { 629 ArrayList<T> cur = arraySet; 630 if (cur == null) { 631 cur = new ArrayList<>(); 632 } 633 cur.add(val); 634 return cur; 635 } 636 637 /** TODO: add javadoc */ 638 @NonNull add(@ullable ArrayList<T> arraySet, int index, T val)639 public static <T> ArrayList<T> add(@Nullable ArrayList<T> arraySet, int index, T val) { 640 ArrayList<T> cur = arraySet; 641 if (cur == null) { 642 cur = new ArrayList<>(); 643 } 644 cur.add(index, val); 645 return cur; 646 } 647 648 /** TODO: add javadoc */ 649 @Nullable remove(@ullable ArrayList<T> cur, T val)650 public static <T> ArrayList<T> remove(@Nullable ArrayList<T> cur, T val) { 651 if (cur == null) { 652 return null; 653 } 654 cur.remove(val); 655 if (cur.isEmpty()) { 656 return null; 657 } else { 658 return cur; 659 } 660 } 661 662 /** TODO: add javadoc */ contains(@ullable Collection<T> cur, T val)663 public static <T> boolean contains(@Nullable Collection<T> cur, T val) { 664 return (cur != null) ? cur.contains(val) : false; 665 } 666 667 /** TODO: add javadoc */ 668 @Nullable trimToSize(@ullable T[] array, int size)669 public static <T> T[] trimToSize(@Nullable T[] array, int size) { 670 if (array == null || size == 0) { 671 return null; 672 } else if (array.length == size) { 673 return array; 674 } else { 675 return Arrays.copyOf(array, size); 676 } 677 } 678 679 /** 680 * Returns true if the two ArrayLists are equal with respect to the objects they contain. 681 * The objects must be in the same order and be reference equal (== not .equals()). 682 */ referenceEquals(ArrayList<T> a, ArrayList<T> b)683 public static <T> boolean referenceEquals(ArrayList<T> a, ArrayList<T> b) { 684 if (a == b) { 685 return true; 686 } 687 688 final int sizeA = a.size(); 689 final int sizeB = b.size(); 690 if (a == null || b == null || sizeA != sizeB) { 691 return false; 692 } 693 694 boolean diff = false; 695 for (int i = 0; i < sizeA && !diff; i++) { 696 diff |= a.get(i) != b.get(i); 697 } 698 return !diff; 699 } 700 701 /** 702 * Removes elements that match the predicate in an efficient way that alters the order of 703 * elements in the collection. This should only be used if order is not important. 704 * 705 * @param collection The ArrayList from which to remove elements. 706 * @param predicate The predicate that each element is tested against. 707 * @return the number of elements removed. 708 */ unstableRemoveIf(@ullable ArrayList<T> collection, @NonNull java.util.function.Predicate<T> predicate)709 public static <T> int unstableRemoveIf(@Nullable ArrayList<T> collection, 710 @NonNull java.util.function.Predicate<T> predicate) { 711 if (collection == null) { 712 return 0; 713 } 714 715 final int size = collection.size(); 716 int leftIdx = 0; 717 int rightIdx = size - 1; 718 while (leftIdx <= rightIdx) { 719 // Find the next element to remove moving left to right. 720 while (leftIdx < size && !predicate.test(collection.get(leftIdx))) { 721 leftIdx++; 722 } 723 724 // Find the next element to keep moving right to left. 725 while (rightIdx > leftIdx && predicate.test(collection.get(rightIdx))) { 726 rightIdx--; 727 } 728 729 if (leftIdx >= rightIdx) { 730 // Done. 731 break; 732 } 733 734 Collections.swap(collection, leftIdx, rightIdx); 735 leftIdx++; 736 rightIdx--; 737 } 738 739 // leftIdx is now at the end. 740 for (int i = size - 1; i >= leftIdx; i--) { 741 collection.remove(i); 742 } 743 return size - leftIdx; 744 } 745 746 /** TODO: add javadoc */ 747 @NonNull defeatNullable(@ullable int[] val)748 public static int[] defeatNullable(@Nullable int[] val) { 749 return (val != null) ? val : EMPTY_INT_ARRAY; 750 } 751 752 /** TODO: add javadoc */ 753 @NonNull defeatNullable(@ullable String[] val)754 public static String[] defeatNullable(@Nullable String[] val) { 755 return (val != null) ? val : new String[0]; 756 } 757 758 /** TODO: add javadoc */ 759 @NonNull defeatNullable(@ullable File[] val)760 public static File[] defeatNullable(@Nullable File[] val) { 761 return (val != null) ? val : EMPTY_FILE; 762 } 763 764 /** 765 * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds. 766 * 767 * @param len length of the array. Must be non-negative 768 * @param index the index to check 769 * @throws ArrayIndexOutOfBoundsException if the {@code index} is out of bounds of the array 770 */ checkBounds(int len, int index)771 public static void checkBounds(int len, int index) { 772 if (index < 0 || len <= index) { 773 throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index); 774 } 775 } 776 777 /** 778 * Throws {@link ArrayIndexOutOfBoundsException} if the range is out of bounds. 779 * 780 * @param len length of the array. Must be non-negative 781 * @param offset start index of the range. Must be non-negative 782 * @param count length of the range. Must be non-negative 783 * @throws ArrayIndexOutOfBoundsException if the range from {@code offset} with length 784 * {@code count} is out of bounds of the array 785 */ throwsIfOutOfBounds(int len, int offset, int count)786 public static void throwsIfOutOfBounds(int len, int offset, int count) { 787 if (len < 0) { 788 throw new ArrayIndexOutOfBoundsException("Negative length: " + len); 789 } 790 791 if ((offset | count) < 0 || offset > len - count) { 792 throw new ArrayIndexOutOfBoundsException( 793 "length=" + len + "; regionStart=" + offset + "; regionLength=" + count); 794 } 795 } 796 797 /** 798 * Returns an array with values from {@code val} minus {@code null} values 799 * 800 * @param arrayConstructor typically {@code T[]::new} e.g. {@code String[]::new} 801 */ filterNotNull(T[] val, IntFunction<T[]> arrayConstructor)802 public static <T> T[] filterNotNull(T[] val, IntFunction<T[]> arrayConstructor) { 803 int nullCount = 0; 804 int size = size(val); 805 for (int i = 0; i < size; i++) { 806 if (val[i] == null) { 807 nullCount++; 808 } 809 } 810 if (nullCount == 0) { 811 return val; 812 } 813 T[] result = arrayConstructor.apply(size - nullCount); 814 int outIdx = 0; 815 for (int i = 0; i < size; i++) { 816 if (val[i] != null) { 817 result[outIdx++] = val[i]; 818 } 819 } 820 return result; 821 } 822 823 /** 824 * Returns an array containing elements from the given one that match the given predicate. 825 * The returned array may, in some cases, be the reference to the input array. 826 */ 827 @Nullable filter(@ullable T[] items, @NonNull IntFunction<T[]> arrayConstructor, @NonNull java.util.function.Predicate<T> predicate)828 public static <T> T[] filter(@Nullable T[] items, 829 @NonNull IntFunction<T[]> arrayConstructor, 830 @NonNull java.util.function.Predicate<T> predicate) { 831 if (isEmpty(items)) { 832 return items; 833 } 834 835 int matchesCount = 0; 836 int size = size(items); 837 final boolean[] tests = new boolean[size]; 838 for (int i = 0; i < size; i++) { 839 tests[i] = predicate.test(items[i]); 840 if (tests[i]) { 841 matchesCount++; 842 } 843 } 844 if (matchesCount == items.length) { 845 return items; 846 } 847 T[] result = arrayConstructor.apply(matchesCount); 848 if (matchesCount == 0) { 849 return result; 850 } 851 int outIdx = 0; 852 for (int i = 0; i < size; i++) { 853 if (tests[i]) { 854 result[outIdx++] = items[i]; 855 } 856 } 857 return result; 858 } 859 860 /** TODO: add javadoc */ startsWith(byte[] cur, byte[] val)861 public static boolean startsWith(byte[] cur, byte[] val) { 862 if (cur == null || val == null) return false; 863 if (cur.length < val.length) return false; 864 for (int i = 0; i < val.length; i++) { 865 if (cur[i] != val[i]) return false; 866 } 867 return true; 868 } 869 870 /** 871 * Returns the first element from the array for which 872 * condition {@code predicate} is true, or null if there is no such element 873 */ 874 @Nullable find(@ullable T[] items, @NonNull java.util.function.Predicate<T> predicate)875 public static <T> T find(@Nullable T[] items, 876 @NonNull java.util.function.Predicate<T> predicate) { 877 if (isEmpty(items)) return null; 878 for (final T item : items) { 879 if (predicate.test(item)) return item; 880 } 881 return null; 882 } 883 884 /** TODO: add javadoc */ 885 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) deepToString(Object value)886 public static String deepToString(Object value) { 887 if (value != null && value.getClass().isArray()) { 888 if (value.getClass() == boolean[].class) { 889 return Arrays.toString((boolean[]) value); 890 } else if (value.getClass() == byte[].class) { 891 return Arrays.toString((byte[]) value); 892 } else if (value.getClass() == char[].class) { 893 return Arrays.toString((char[]) value); 894 } else if (value.getClass() == double[].class) { 895 return Arrays.toString((double[]) value); 896 } else if (value.getClass() == float[].class) { 897 return Arrays.toString((float[]) value); 898 } else if (value.getClass() == int[].class) { 899 return Arrays.toString((int[]) value); 900 } else if (value.getClass() == long[].class) { 901 return Arrays.toString((long[]) value); 902 } else if (value.getClass() == short[].class) { 903 return Arrays.toString((short[]) value); 904 } else { 905 return Arrays.deepToString((Object[]) value); 906 } 907 } else { 908 return String.valueOf(value); 909 } 910 } 911 912 /** TODO: add javadoc */ firstOrNull(T[] items)913 public static @Nullable <T> T firstOrNull(T[] items) { 914 return items.length > 0 ? items[0] : null; 915 } 916 } 917