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