001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.util;
018
019import java.lang.reflect.Array;
020import java.util.Arrays;
021import java.util.Collection;
022import java.util.Map;
023import java.util.Optional;
024import java.util.StringJoiner;
025
026import org.springframework.lang.Nullable;
027
028/**
029 * Miscellaneous object utility methods.
030 *
031 * <p>Mainly for internal use within the framework.
032 *
033 * <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
034 *
035 * @author Juergen Hoeller
036 * @author Keith Donald
037 * @author Rod Johnson
038 * @author Rob Harrop
039 * @author Chris Beams
040 * @author Sam Brannen
041 * @since 19.03.2004
042 * @see ClassUtils
043 * @see CollectionUtils
044 * @see StringUtils
045 */
046public abstract class ObjectUtils {
047
048        private static final int INITIAL_HASH = 7;
049        private static final int MULTIPLIER = 31;
050
051        private static final String EMPTY_STRING = "";
052        private static final String NULL_STRING = "null";
053        private static final String ARRAY_START = "{";
054        private static final String ARRAY_END = "}";
055        private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;
056        private static final String ARRAY_ELEMENT_SEPARATOR = ", ";
057        private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
058
059
060        /**
061         * Return whether the given throwable is a checked exception:
062         * that is, neither a RuntimeException nor an Error.
063         * @param ex the throwable to check
064         * @return whether the throwable is a checked exception
065         * @see java.lang.Exception
066         * @see java.lang.RuntimeException
067         * @see java.lang.Error
068         */
069        public static boolean isCheckedException(Throwable ex) {
070                return !(ex instanceof RuntimeException || ex instanceof Error);
071        }
072
073        /**
074         * Check whether the given exception is compatible with the specified
075         * exception types, as declared in a throws clause.
076         * @param ex the exception to check
077         * @param declaredExceptions the exception types declared in the throws clause
078         * @return whether the given exception is compatible
079         */
080        public static boolean isCompatibleWithThrowsClause(Throwable ex, @Nullable Class<?>... declaredExceptions) {
081                if (!isCheckedException(ex)) {
082                        return true;
083                }
084                if (declaredExceptions != null) {
085                        for (Class<?> declaredException : declaredExceptions) {
086                                if (declaredException.isInstance(ex)) {
087                                        return true;
088                                }
089                        }
090                }
091                return false;
092        }
093
094        /**
095         * Determine whether the given object is an array:
096         * either an Object array or a primitive array.
097         * @param obj the object to check
098         */
099        public static boolean isArray(@Nullable Object obj) {
100                return (obj != null && obj.getClass().isArray());
101        }
102
103        /**
104         * Determine whether the given array is empty:
105         * i.e. {@code null} or of zero length.
106         * @param array the array to check
107         * @see #isEmpty(Object)
108         */
109        public static boolean isEmpty(@Nullable Object[] array) {
110                return (array == null || array.length == 0);
111        }
112
113        /**
114         * Determine whether the given object is empty.
115         * <p>This method supports the following object types.
116         * <ul>
117         * <li>{@code Optional}: considered empty if {@link Optional#empty()}</li>
118         * <li>{@code Array}: considered empty if its length is zero</li>
119         * <li>{@link CharSequence}: considered empty if its length is zero</li>
120         * <li>{@link Collection}: delegates to {@link Collection#isEmpty()}</li>
121         * <li>{@link Map}: delegates to {@link Map#isEmpty()}</li>
122         * </ul>
123         * <p>If the given object is non-null and not one of the aforementioned
124         * supported types, this method returns {@code false}.
125         * @param obj the object to check
126         * @return {@code true} if the object is {@code null} or <em>empty</em>
127         * @since 4.2
128         * @see Optional#isPresent()
129         * @see ObjectUtils#isEmpty(Object[])
130         * @see StringUtils#hasLength(CharSequence)
131         * @see StringUtils#isEmpty(Object)
132         * @see CollectionUtils#isEmpty(java.util.Collection)
133         * @see CollectionUtils#isEmpty(java.util.Map)
134         */
135        public static boolean isEmpty(@Nullable Object obj) {
136                if (obj == null) {
137                        return true;
138                }
139
140                if (obj instanceof Optional) {
141                        return !((Optional<?>) obj).isPresent();
142                }
143                if (obj instanceof CharSequence) {
144                        return ((CharSequence) obj).length() == 0;
145                }
146                if (obj.getClass().isArray()) {
147                        return Array.getLength(obj) == 0;
148                }
149                if (obj instanceof Collection) {
150                        return ((Collection<?>) obj).isEmpty();
151                }
152                if (obj instanceof Map) {
153                        return ((Map<?, ?>) obj).isEmpty();
154                }
155
156                // else
157                return false;
158        }
159
160        /**
161         * Unwrap the given object which is potentially a {@link java.util.Optional}.
162         * @param obj the candidate object
163         * @return either the value held within the {@code Optional}, {@code null}
164         * if the {@code Optional} is empty, or simply the given object as-is
165         * @since 5.0
166         */
167        @Nullable
168        public static Object unwrapOptional(@Nullable Object obj) {
169                if (obj instanceof Optional) {
170                        Optional<?> optional = (Optional<?>) obj;
171                        if (!optional.isPresent()) {
172                                return null;
173                        }
174                        Object result = optional.get();
175                        Assert.isTrue(!(result instanceof Optional), "Multi-level Optional usage not supported");
176                        return result;
177                }
178                return obj;
179        }
180
181        /**
182         * Check whether the given array contains the given element.
183         * @param array the array to check (may be {@code null},
184         * in which case the return value will always be {@code false})
185         * @param element the element to check for
186         * @return whether the element has been found in the given array
187         */
188        public static boolean containsElement(@Nullable Object[] array, Object element) {
189                if (array == null) {
190                        return false;
191                }
192                for (Object arrayEle : array) {
193                        if (nullSafeEquals(arrayEle, element)) {
194                                return true;
195                        }
196                }
197                return false;
198        }
199
200        /**
201         * Check whether the given array of enum constants contains a constant with the given name,
202         * ignoring case when determining a match.
203         * @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()}
204         * @param constant the constant name to find (must not be null or empty string)
205         * @return whether the constant has been found in the given array
206         */
207        public static boolean containsConstant(Enum<?>[] enumValues, String constant) {
208                return containsConstant(enumValues, constant, false);
209        }
210
211        /**
212         * Check whether the given array of enum constants contains a constant with the given name.
213         * @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()}
214         * @param constant the constant name to find (must not be null or empty string)
215         * @param caseSensitive whether case is significant in determining a match
216         * @return whether the constant has been found in the given array
217         */
218        public static boolean containsConstant(Enum<?>[] enumValues, String constant, boolean caseSensitive) {
219                for (Enum<?> candidate : enumValues) {
220                        if (caseSensitive ? candidate.toString().equals(constant) :
221                                        candidate.toString().equalsIgnoreCase(constant)) {
222                                return true;
223                        }
224                }
225                return false;
226        }
227
228        /**
229         * Case insensitive alternative to {@link Enum#valueOf(Class, String)}.
230         * @param <E> the concrete Enum type
231         * @param enumValues the array of all Enum constants in question, usually per {@code Enum.values()}
232         * @param constant the constant to get the enum value of
233         * @throws IllegalArgumentException if the given constant is not found in the given array
234         * of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to avoid this exception.
235         */
236        public static <E extends Enum<?>> E caseInsensitiveValueOf(E[] enumValues, String constant) {
237                for (E candidate : enumValues) {
238                        if (candidate.toString().equalsIgnoreCase(constant)) {
239                                return candidate;
240                        }
241                }
242                throw new IllegalArgumentException("Constant [" + constant + "] does not exist in enum type " +
243                                enumValues.getClass().getComponentType().getName());
244        }
245
246        /**
247         * Append the given object to the given array, returning a new array
248         * consisting of the input array contents plus the given object.
249         * @param array the array to append to (can be {@code null})
250         * @param obj the object to append
251         * @return the new array (of the same component type; never {@code null})
252         */
253        public static <A, O extends A> A[] addObjectToArray(@Nullable A[] array, @Nullable O obj) {
254                Class<?> compType = Object.class;
255                if (array != null) {
256                        compType = array.getClass().getComponentType();
257                }
258                else if (obj != null) {
259                        compType = obj.getClass();
260                }
261                int newArrLength = (array != null ? array.length + 1 : 1);
262                @SuppressWarnings("unchecked")
263                A[] newArr = (A[]) Array.newInstance(compType, newArrLength);
264                if (array != null) {
265                        System.arraycopy(array, 0, newArr, 0, array.length);
266                }
267                newArr[newArr.length - 1] = obj;
268                return newArr;
269        }
270
271        /**
272         * Convert the given array (which may be a primitive array) to an
273         * object array (if necessary of primitive wrapper objects).
274         * <p>A {@code null} source value will be converted to an
275         * empty Object array.
276         * @param source the (potentially primitive) array
277         * @return the corresponding object array (never {@code null})
278         * @throws IllegalArgumentException if the parameter is not an array
279         */
280        public static Object[] toObjectArray(@Nullable Object source) {
281                if (source instanceof Object[]) {
282                        return (Object[]) source;
283                }
284                if (source == null) {
285                        return EMPTY_OBJECT_ARRAY;
286                }
287                if (!source.getClass().isArray()) {
288                        throw new IllegalArgumentException("Source is not an array: " + source);
289                }
290                int length = Array.getLength(source);
291                if (length == 0) {
292                        return EMPTY_OBJECT_ARRAY;
293                }
294                Class<?> wrapperType = Array.get(source, 0).getClass();
295                Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);
296                for (int i = 0; i < length; i++) {
297                        newArray[i] = Array.get(source, i);
298                }
299                return newArray;
300        }
301
302
303        //---------------------------------------------------------------------
304        // Convenience methods for content-based equality/hash-code handling
305        //---------------------------------------------------------------------
306
307        /**
308         * Determine if the given objects are equal, returning {@code true} if
309         * both are {@code null} or {@code false} if only one is {@code null}.
310         * <p>Compares arrays with {@code Arrays.equals}, performing an equality
311         * check based on the array elements rather than the array reference.
312         * @param o1 first Object to compare
313         * @param o2 second Object to compare
314         * @return whether the given objects are equal
315         * @see Object#equals(Object)
316         * @see java.util.Arrays#equals
317         */
318        public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
319                if (o1 == o2) {
320                        return true;
321                }
322                if (o1 == null || o2 == null) {
323                        return false;
324                }
325                if (o1.equals(o2)) {
326                        return true;
327                }
328                if (o1.getClass().isArray() && o2.getClass().isArray()) {
329                        return arrayEquals(o1, o2);
330                }
331                return false;
332        }
333
334        /**
335         * Compare the given arrays with {@code Arrays.equals}, performing an equality
336         * check based on the array elements rather than the array reference.
337         * @param o1 first array to compare
338         * @param o2 second array to compare
339         * @return whether the given objects are equal
340         * @see #nullSafeEquals(Object, Object)
341         * @see java.util.Arrays#equals
342         */
343        private static boolean arrayEquals(Object o1, Object o2) {
344                if (o1 instanceof Object[] && o2 instanceof Object[]) {
345                        return Arrays.equals((Object[]) o1, (Object[]) o2);
346                }
347                if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
348                        return Arrays.equals((boolean[]) o1, (boolean[]) o2);
349                }
350                if (o1 instanceof byte[] && o2 instanceof byte[]) {
351                        return Arrays.equals((byte[]) o1, (byte[]) o2);
352                }
353                if (o1 instanceof char[] && o2 instanceof char[]) {
354                        return Arrays.equals((char[]) o1, (char[]) o2);
355                }
356                if (o1 instanceof double[] && o2 instanceof double[]) {
357                        return Arrays.equals((double[]) o1, (double[]) o2);
358                }
359                if (o1 instanceof float[] && o2 instanceof float[]) {
360                        return Arrays.equals((float[]) o1, (float[]) o2);
361                }
362                if (o1 instanceof int[] && o2 instanceof int[]) {
363                        return Arrays.equals((int[]) o1, (int[]) o2);
364                }
365                if (o1 instanceof long[] && o2 instanceof long[]) {
366                        return Arrays.equals((long[]) o1, (long[]) o2);
367                }
368                if (o1 instanceof short[] && o2 instanceof short[]) {
369                        return Arrays.equals((short[]) o1, (short[]) o2);
370                }
371                return false;
372        }
373
374        /**
375         * Return as hash code for the given object; typically the value of
376         * {@code Object#hashCode()}}. If the object is an array,
377         * this method will delegate to any of the {@code nullSafeHashCode}
378         * methods for arrays in this class. If the object is {@code null},
379         * this method returns 0.
380         * @see Object#hashCode()
381         * @see #nullSafeHashCode(Object[])
382         * @see #nullSafeHashCode(boolean[])
383         * @see #nullSafeHashCode(byte[])
384         * @see #nullSafeHashCode(char[])
385         * @see #nullSafeHashCode(double[])
386         * @see #nullSafeHashCode(float[])
387         * @see #nullSafeHashCode(int[])
388         * @see #nullSafeHashCode(long[])
389         * @see #nullSafeHashCode(short[])
390         */
391        public static int nullSafeHashCode(@Nullable Object obj) {
392                if (obj == null) {
393                        return 0;
394                }
395                if (obj.getClass().isArray()) {
396                        if (obj instanceof Object[]) {
397                                return nullSafeHashCode((Object[]) obj);
398                        }
399                        if (obj instanceof boolean[]) {
400                                return nullSafeHashCode((boolean[]) obj);
401                        }
402                        if (obj instanceof byte[]) {
403                                return nullSafeHashCode((byte[]) obj);
404                        }
405                        if (obj instanceof char[]) {
406                                return nullSafeHashCode((char[]) obj);
407                        }
408                        if (obj instanceof double[]) {
409                                return nullSafeHashCode((double[]) obj);
410                        }
411                        if (obj instanceof float[]) {
412                                return nullSafeHashCode((float[]) obj);
413                        }
414                        if (obj instanceof int[]) {
415                                return nullSafeHashCode((int[]) obj);
416                        }
417                        if (obj instanceof long[]) {
418                                return nullSafeHashCode((long[]) obj);
419                        }
420                        if (obj instanceof short[]) {
421                                return nullSafeHashCode((short[]) obj);
422                        }
423                }
424                return obj.hashCode();
425        }
426
427        /**
428         * Return a hash code based on the contents of the specified array.
429         * If {@code array} is {@code null}, this method returns 0.
430         */
431        public static int nullSafeHashCode(@Nullable Object[] array) {
432                if (array == null) {
433                        return 0;
434                }
435                int hash = INITIAL_HASH;
436                for (Object element : array) {
437                        hash = MULTIPLIER * hash + nullSafeHashCode(element);
438                }
439                return hash;
440        }
441
442        /**
443         * Return a hash code based on the contents of the specified array.
444         * If {@code array} is {@code null}, this method returns 0.
445         */
446        public static int nullSafeHashCode(@Nullable boolean[] array) {
447                if (array == null) {
448                        return 0;
449                }
450                int hash = INITIAL_HASH;
451                for (boolean element : array) {
452                        hash = MULTIPLIER * hash + Boolean.hashCode(element);
453                }
454                return hash;
455        }
456
457        /**
458         * Return a hash code based on the contents of the specified array.
459         * If {@code array} is {@code null}, this method returns 0.
460         */
461        public static int nullSafeHashCode(@Nullable byte[] array) {
462                if (array == null) {
463                        return 0;
464                }
465                int hash = INITIAL_HASH;
466                for (byte element : array) {
467                        hash = MULTIPLIER * hash + element;
468                }
469                return hash;
470        }
471
472        /**
473         * Return a hash code based on the contents of the specified array.
474         * If {@code array} is {@code null}, this method returns 0.
475         */
476        public static int nullSafeHashCode(@Nullable char[] array) {
477                if (array == null) {
478                        return 0;
479                }
480                int hash = INITIAL_HASH;
481                for (char element : array) {
482                        hash = MULTIPLIER * hash + element;
483                }
484                return hash;
485        }
486
487        /**
488         * Return a hash code based on the contents of the specified array.
489         * If {@code array} is {@code null}, this method returns 0.
490         */
491        public static int nullSafeHashCode(@Nullable double[] array) {
492                if (array == null) {
493                        return 0;
494                }
495                int hash = INITIAL_HASH;
496                for (double element : array) {
497                        hash = MULTIPLIER * hash + Double.hashCode(element);
498                }
499                return hash;
500        }
501
502        /**
503         * Return a hash code based on the contents of the specified array.
504         * If {@code array} is {@code null}, this method returns 0.
505         */
506        public static int nullSafeHashCode(@Nullable float[] array) {
507                if (array == null) {
508                        return 0;
509                }
510                int hash = INITIAL_HASH;
511                for (float element : array) {
512                        hash = MULTIPLIER * hash + Float.hashCode(element);
513                }
514                return hash;
515        }
516
517        /**
518         * Return a hash code based on the contents of the specified array.
519         * If {@code array} is {@code null}, this method returns 0.
520         */
521        public static int nullSafeHashCode(@Nullable int[] array) {
522                if (array == null) {
523                        return 0;
524                }
525                int hash = INITIAL_HASH;
526                for (int element : array) {
527                        hash = MULTIPLIER * hash + element;
528                }
529                return hash;
530        }
531
532        /**
533         * Return a hash code based on the contents of the specified array.
534         * If {@code array} is {@code null}, this method returns 0.
535         */
536        public static int nullSafeHashCode(@Nullable long[] array) {
537                if (array == null) {
538                        return 0;
539                }
540                int hash = INITIAL_HASH;
541                for (long element : array) {
542                        hash = MULTIPLIER * hash + Long.hashCode(element);
543                }
544                return hash;
545        }
546
547        /**
548         * Return a hash code based on the contents of the specified array.
549         * If {@code array} is {@code null}, this method returns 0.
550         */
551        public static int nullSafeHashCode(@Nullable short[] array) {
552                if (array == null) {
553                        return 0;
554                }
555                int hash = INITIAL_HASH;
556                for (short element : array) {
557                        hash = MULTIPLIER * hash + element;
558                }
559                return hash;
560        }
561
562        /**
563         * Return the same value as {@link Boolean#hashCode(boolean)}}.
564         * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
565         */
566        @Deprecated
567        public static int hashCode(boolean bool) {
568                return Boolean.hashCode(bool);
569        }
570
571        /**
572         * Return the same value as {@link Double#hashCode(double)}}.
573         * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
574         */
575        @Deprecated
576        public static int hashCode(double dbl) {
577                return Double.hashCode(dbl);
578        }
579
580        /**
581         * Return the same value as {@link Float#hashCode(float)}}.
582         * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
583         */
584        @Deprecated
585        public static int hashCode(float flt) {
586                return Float.hashCode(flt);
587        }
588
589        /**
590         * Return the same value as {@link Long#hashCode(long)}}.
591         * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
592         */
593        @Deprecated
594        public static int hashCode(long lng) {
595                return Long.hashCode(lng);
596        }
597
598
599        //---------------------------------------------------------------------
600        // Convenience methods for toString output
601        //---------------------------------------------------------------------
602
603        /**
604         * Return a String representation of an object's overall identity.
605         * @param obj the object (may be {@code null})
606         * @return the object's identity as String representation,
607         * or an empty String if the object was {@code null}
608         */
609        public static String identityToString(@Nullable Object obj) {
610                if (obj == null) {
611                        return EMPTY_STRING;
612                }
613                return obj.getClass().getName() + "@" + getIdentityHexString(obj);
614        }
615
616        /**
617         * Return a hex String form of an object's identity hash code.
618         * @param obj the object
619         * @return the object's identity code in hex notation
620         */
621        public static String getIdentityHexString(Object obj) {
622                return Integer.toHexString(System.identityHashCode(obj));
623        }
624
625        /**
626         * Return a content-based String representation if {@code obj} is
627         * not {@code null}; otherwise returns an empty String.
628         * <p>Differs from {@link #nullSafeToString(Object)} in that it returns
629         * an empty String rather than "null" for a {@code null} value.
630         * @param obj the object to build a display String for
631         * @return a display String representation of {@code obj}
632         * @see #nullSafeToString(Object)
633         */
634        public static String getDisplayString(@Nullable Object obj) {
635                if (obj == null) {
636                        return EMPTY_STRING;
637                }
638                return nullSafeToString(obj);
639        }
640
641        /**
642         * Determine the class name for the given object.
643         * <p>Returns a {@code "null"} String if {@code obj} is {@code null}.
644         * @param obj the object to introspect (may be {@code null})
645         * @return the corresponding class name
646         */
647        public static String nullSafeClassName(@Nullable Object obj) {
648                return (obj != null ? obj.getClass().getName() : NULL_STRING);
649        }
650
651        /**
652         * Return a String representation of the specified Object.
653         * <p>Builds a String representation of the contents in case of an array.
654         * Returns a {@code "null"} String if {@code obj} is {@code null}.
655         * @param obj the object to build a String representation for
656         * @return a String representation of {@code obj}
657         */
658        public static String nullSafeToString(@Nullable Object obj) {
659                if (obj == null) {
660                        return NULL_STRING;
661                }
662                if (obj instanceof String) {
663                        return (String) obj;
664                }
665                if (obj instanceof Object[]) {
666                        return nullSafeToString((Object[]) obj);
667                }
668                if (obj instanceof boolean[]) {
669                        return nullSafeToString((boolean[]) obj);
670                }
671                if (obj instanceof byte[]) {
672                        return nullSafeToString((byte[]) obj);
673                }
674                if (obj instanceof char[]) {
675                        return nullSafeToString((char[]) obj);
676                }
677                if (obj instanceof double[]) {
678                        return nullSafeToString((double[]) obj);
679                }
680                if (obj instanceof float[]) {
681                        return nullSafeToString((float[]) obj);
682                }
683                if (obj instanceof int[]) {
684                        return nullSafeToString((int[]) obj);
685                }
686                if (obj instanceof long[]) {
687                        return nullSafeToString((long[]) obj);
688                }
689                if (obj instanceof short[]) {
690                        return nullSafeToString((short[]) obj);
691                }
692                String str = obj.toString();
693                return (str != null ? str : EMPTY_STRING);
694        }
695
696        /**
697         * Return a String representation of the contents of the specified array.
698         * <p>The String representation consists of a list of the array's elements,
699         * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
700         * by the characters {@code ", "} (a comma followed by a space).
701         * Returns a {@code "null"} String if {@code array} is {@code null}.
702         * @param array the array to build a String representation for
703         * @return a String representation of {@code array}
704         */
705        public static String nullSafeToString(@Nullable Object[] array) {
706                if (array == null) {
707                        return NULL_STRING;
708                }
709                int length = array.length;
710                if (length == 0) {
711                        return EMPTY_ARRAY;
712                }
713                StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
714                for (Object o : array) {
715                        stringJoiner.add(String.valueOf(o));
716                }
717                return stringJoiner.toString();
718        }
719
720        /**
721         * Return a String representation of the contents of the specified array.
722         * <p>The String representation consists of a list of the array's elements,
723         * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
724         * by the characters {@code ", "} (a comma followed by a space).
725         * Returns a {@code "null"} String if {@code array} is {@code null}.
726         * @param array the array to build a String representation for
727         * @return a String representation of {@code array}
728         */
729        public static String nullSafeToString(@Nullable boolean[] array) {
730                if (array == null) {
731                        return NULL_STRING;
732                }
733                int length = array.length;
734                if (length == 0) {
735                        return EMPTY_ARRAY;
736                }
737                StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
738                for (boolean b : array) {
739                        stringJoiner.add(String.valueOf(b));
740                }
741                return stringJoiner.toString();
742        }
743
744        /**
745         * Return a String representation of the contents of the specified array.
746         * <p>The String representation consists of a list of the array's elements,
747         * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
748         * by the characters {@code ", "} (a comma followed by a space).
749         * Returns a {@code "null"} String if {@code array} is {@code null}.
750         * @param array the array to build a String representation for
751         * @return a String representation of {@code array}
752         */
753        public static String nullSafeToString(@Nullable byte[] array) {
754                if (array == null) {
755                        return NULL_STRING;
756                }
757                int length = array.length;
758                if (length == 0) {
759                        return EMPTY_ARRAY;
760                }
761                StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
762                for (byte b : array) {
763                        stringJoiner.add(String.valueOf(b));
764                }
765                return stringJoiner.toString();
766        }
767
768        /**
769         * Return a String representation of the contents of the specified array.
770         * <p>The String representation consists of a list of the array's elements,
771         * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
772         * by the characters {@code ", "} (a comma followed by a space).
773         * Returns a {@code "null"} String if {@code array} is {@code null}.
774         * @param array the array to build a String representation for
775         * @return a String representation of {@code array}
776         */
777        public static String nullSafeToString(@Nullable char[] array) {
778                if (array == null) {
779                        return NULL_STRING;
780                }
781                int length = array.length;
782                if (length == 0) {
783                        return EMPTY_ARRAY;
784                }
785                StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
786                for (char c : array) {
787                        stringJoiner.add('\'' + String.valueOf(c) + '\'');
788                }
789                return stringJoiner.toString();
790        }
791
792        /**
793         * Return a String representation of the contents of the specified array.
794         * <p>The String representation consists of a list of the array's elements,
795         * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
796         * by the characters {@code ", "} (a comma followed by a space).
797         * Returns a {@code "null"} String if {@code array} is {@code null}.
798         * @param array the array to build a String representation for
799         * @return a String representation of {@code array}
800         */
801        public static String nullSafeToString(@Nullable double[] array) {
802                if (array == null) {
803                        return NULL_STRING;
804                }
805                int length = array.length;
806                if (length == 0) {
807                        return EMPTY_ARRAY;
808                }
809                StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
810                for (double d : array) {
811                        stringJoiner.add(String.valueOf(d));
812                }
813                return stringJoiner.toString();
814        }
815
816        /**
817         * Return a String representation of the contents of the specified array.
818         * <p>The String representation consists of a list of the array's elements,
819         * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
820         * by the characters {@code ", "} (a comma followed by a space).
821         * Returns a {@code "null"} String if {@code array} is {@code null}.
822         * @param array the array to build a String representation for
823         * @return a String representation of {@code array}
824         */
825        public static String nullSafeToString(@Nullable float[] array) {
826                if (array == null) {
827                        return NULL_STRING;
828                }
829                int length = array.length;
830                if (length == 0) {
831                        return EMPTY_ARRAY;
832                }
833                StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
834                for (float f : array) {
835                        stringJoiner.add(String.valueOf(f));
836                }
837                return stringJoiner.toString();
838        }
839
840        /**
841         * Return a String representation of the contents of the specified array.
842         * <p>The String representation consists of a list of the array's elements,
843         * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
844         * by the characters {@code ", "} (a comma followed by a space).
845         * Returns a {@code "null"} String if {@code array} is {@code null}.
846         * @param array the array to build a String representation for
847         * @return a String representation of {@code array}
848         */
849        public static String nullSafeToString(@Nullable int[] array) {
850                if (array == null) {
851                        return NULL_STRING;
852                }
853                int length = array.length;
854                if (length == 0) {
855                        return EMPTY_ARRAY;
856                }
857                StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
858                for (int i : array) {
859                        stringJoiner.add(String.valueOf(i));
860                }
861                return stringJoiner.toString();
862        }
863
864        /**
865         * Return a String representation of the contents of the specified array.
866         * <p>The String representation consists of a list of the array's elements,
867         * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
868         * by the characters {@code ", "} (a comma followed by a space).
869         * Returns a {@code "null"} String if {@code array} is {@code null}.
870         * @param array the array to build a String representation for
871         * @return a String representation of {@code array}
872         */
873        public static String nullSafeToString(@Nullable long[] array) {
874                if (array == null) {
875                        return NULL_STRING;
876                }
877                int length = array.length;
878                if (length == 0) {
879                        return EMPTY_ARRAY;
880                }
881                StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
882                for (long l : array) {
883                        stringJoiner.add(String.valueOf(l));
884                }
885                return stringJoiner.toString();
886        }
887
888        /**
889         * Return a String representation of the contents of the specified array.
890         * <p>The String representation consists of a list of the array's elements,
891         * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
892         * by the characters {@code ", "} (a comma followed by a space).
893         * Returns a {@code "null"} String if {@code array} is {@code null}.
894         * @param array the array to build a String representation for
895         * @return a String representation of {@code array}
896         */
897        public static String nullSafeToString(@Nullable short[] array) {
898                if (array == null) {
899                        return NULL_STRING;
900                }
901                int length = array.length;
902                if (length == 0) {
903                        return EMPTY_ARRAY;
904                }
905                StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
906                for (short s : array) {
907                        stringJoiner.add(String.valueOf(s));
908                }
909                return stringJoiner.toString();
910        }
911
912}