Make ArraySorter null-safe

Make ArrayUtils.removeAll() null-safe
This commit is contained in:
Gary Gregory 2024-02-23 20:27:30 -05:00
parent 7bb195000a
commit aeca68f2a5
5 changed files with 137 additions and 36 deletions

View File

@ -109,6 +109,8 @@ The <action> type attribute can be add,update,fix,remove.
<action type="fix" dev="ggregory" due-to="Gary Gregory">Deprecate ThreadUtils 0-argument constructor.</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory">Deprecate TypeUtils 0-argument constructor.</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory">Make ArrayFill null-safe.</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory">Make ArraySorter null-safe.</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory">Make ArrayUtils.removeAll() null-safe.</action>
<!-- UPDATE -->
<action type="update" dev="sebb" due-to="Dependabot">Bump commons-parent from 64 to 66.</action>
<action type="update" dev="ggregory" due-to="Dependabot">Bump org.codehaus.mojo:exec-maven-plugin from 3.1.1 to 3.2.0 #1175.</action>

View File

@ -35,7 +35,9 @@ public class ArraySorter {
* @see Arrays#sort(byte[])
*/
public static byte[] sort(final byte[] array) {
Arrays.sort(array);
if (array != null) {
Arrays.sort(array);
}
return array;
}
@ -47,7 +49,9 @@ public class ArraySorter {
* @see Arrays#sort(char[])
*/
public static char[] sort(final char[] array) {
Arrays.sort(array);
if (array != null) {
Arrays.sort(array);
}
return array;
}
@ -59,7 +63,9 @@ public class ArraySorter {
* @see Arrays#sort(double[])
*/
public static double[] sort(final double[] array) {
Arrays.sort(array);
if (array != null) {
Arrays.sort(array);
}
return array;
}
@ -71,7 +77,9 @@ public class ArraySorter {
* @see Arrays#sort(float[])
*/
public static float[] sort(final float[] array) {
Arrays.sort(array);
if (array != null) {
Arrays.sort(array);
}
return array;
}
@ -83,7 +91,9 @@ public class ArraySorter {
* @see Arrays#sort(int[])
*/
public static int[] sort(final int[] array) {
Arrays.sort(array);
if (array != null) {
Arrays.sort(array);
}
return array;
}
@ -95,7 +105,9 @@ public class ArraySorter {
* @see Arrays#sort(long[])
*/
public static long[] sort(final long[] array) {
Arrays.sort(array);
if (array != null) {
Arrays.sort(array);
}
return array;
}
@ -107,7 +119,9 @@ public class ArraySorter {
* @see Arrays#sort(short[])
*/
public static short[] sort(final short[] array) {
Arrays.sort(array);
if (array != null) {
Arrays.sort(array);
}
return array;
}
@ -120,7 +134,9 @@ public class ArraySorter {
* @see Arrays#sort(Object[])
*/
public static <T> T[] sort(final T[] array) {
Arrays.sort(array);
if (array != null) {
Arrays.sort(array);
}
return array;
}
@ -135,7 +151,9 @@ public class ArraySorter {
* @see Arrays#sort(Object[])
*/
public static <T> T[] sort(final T[] array, final Comparator<? super T> comparator) {
Arrays.sort(array, comparator);
if (array != null) {
Arrays.sort(array, comparator);
}
return array;
}

View File

@ -5296,6 +5296,9 @@ public class ArrayUtils {
*/
// package protected for access by unit tests
static Object removeAll(final Object array, final int... indices) {
if (array == null) {
return null;
}
final int length = getLength(array);
int diff = 0; // number of distinct indexes, i.e. number of entries that will be removed
final int[] clonedIndices = ArraySorter.sort(clone(indices));
@ -5319,7 +5322,7 @@ public class ArrayUtils {
// create result array
final Object result = Array.newInstance(array.getClass().getComponentType(), length - diff);
if (diff < length) {
if (diff < length && clonedIndices != null) {
int end = length; // index just after last copy
int dest = length - diff; // number of entries so far not copied
for (int i = clonedIndices.length - 1; i >= 0; i--) {

View File

@ -18,11 +18,15 @@
package org.apache.commons.lang3;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
/**
* Tests {@link ArraySorter}.
*/
public class ArraySorterTest extends AbstractLangTest {
@Test
@ -31,6 +35,7 @@ public class ArraySorterTest extends AbstractLangTest {
final byte[] array2 = array1.clone();
Arrays.sort(array1);
assertArrayEquals(array1, ArraySorter.sort(array2));
assertNull(ArraySorter.sort((byte[]) null));
}
@Test
@ -39,6 +44,7 @@ public class ArraySorterTest extends AbstractLangTest {
final char[] array2 = array1.clone();
Arrays.sort(array1);
assertArrayEquals(array1, ArraySorter.sort(array2));
assertNull(ArraySorter.sort((char[]) null));
}
@Test
@ -47,6 +53,7 @@ public class ArraySorterTest extends AbstractLangTest {
final String[] array2 = array1.clone();
Arrays.sort(array1);
assertArrayEquals(array1, ArraySorter.sort(array2, String::compareTo));
assertNull(ArraySorter.sort((String[]) null));
}
@Test
@ -55,6 +62,7 @@ public class ArraySorterTest extends AbstractLangTest {
final double[] array2 = array1.clone();
Arrays.sort(array1);
assertArrayEquals(array1, ArraySorter.sort(array2));
assertNull(ArraySorter.sort((double[]) null));
}
@Test
@ -63,6 +71,7 @@ public class ArraySorterTest extends AbstractLangTest {
final float[] array2 = array1.clone();
Arrays.sort(array1);
assertArrayEquals(array1, ArraySorter.sort(array2));
assertNull(ArraySorter.sort((float[]) null));
}
@Test
@ -71,6 +80,7 @@ public class ArraySorterTest extends AbstractLangTest {
final int[] array2 = array1.clone();
Arrays.sort(array1);
assertArrayEquals(array1, ArraySorter.sort(array2));
assertNull(ArraySorter.sort((int[]) null));
}
@Test
@ -79,6 +89,7 @@ public class ArraySorterTest extends AbstractLangTest {
final long[] array2 = array1.clone();
Arrays.sort(array1);
assertArrayEquals(array1, ArraySorter.sort(array2));
assertNull(ArraySorter.sort((long[]) null));
}
@Test
@ -87,6 +98,7 @@ public class ArraySorterTest extends AbstractLangTest {
final String[] array2 = array1.clone();
Arrays.sort(array1);
assertArrayEquals(array1, ArraySorter.sort(array2));
assertNull(ArraySorter.sort((String[]) null));
}
@Test
@ -95,6 +107,7 @@ public class ArraySorterTest extends AbstractLangTest {
final short[] array2 = array1.clone();
Arrays.sort(array1);
assertArrayEquals(array1, ArraySorter.sort(array2));
assertNull(ArraySorter.sort((short[]) null));
}
}

View File

@ -26,10 +26,12 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
/**
* Tests ArrayUtils remove and removeElement methods.
* Tests {@link ArrayUtils} remove and removeElement methods.
*/
public class ArrayUtilsRemoveMultipleTest extends AbstractLangTest {
private static final int[] NULL_INDICES = null;
@Test
public void testRemoveAllBooleanArray() {
boolean[] array;
@ -550,47 +552,110 @@ public class ArrayUtilsRemoveMultipleTest extends AbstractLangTest {
@Test
public void testRemoveAllNullBooleanArray() {
assertThrows(IndexOutOfBoundsException.class, () -> ArrayUtils.removeAll((boolean[]) null, 0));
assertNull(ArrayUtils.removeAll((boolean[]) null, 0));
assertNull(ArrayUtils.removeAll((boolean[]) null, NULL_INDICES));
final boolean[] array0 = {};
assertArrayEquals(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
assertNotSame(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
final boolean[] array1 = new boolean[1];
assertArrayEquals(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
assertNotSame(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
}
@Test
public void testRemoveAllNullByteArray() {
assertThrows(IndexOutOfBoundsException.class, () -> ArrayUtils.removeAll((byte[]) null, 0));
assertNull(ArrayUtils.removeAll((byte[]) null, 0));
assertNull(ArrayUtils.removeAll((byte[]) null, NULL_INDICES));
final byte[] array0 = {};
assertArrayEquals(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
assertNotSame(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
final byte[] array1 = new byte[1];
assertArrayEquals(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
assertNotSame(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
}
@Test
public void testRemoveAllNullCharArray() {
assertThrows(IndexOutOfBoundsException.class, () -> ArrayUtils.removeAll((char[]) null, 0));
assertNull(ArrayUtils.removeAll((char[]) null, 0));
assertNull(ArrayUtils.removeAll((char[]) null, NULL_INDICES));
final char[] array0 = {};
assertArrayEquals(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
assertNotSame(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
final char[] array1 = new char[1];
assertArrayEquals(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
assertNotSame(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
}
@Test
public void testRemoveAllNullDoubleArray() {
assertThrows(IndexOutOfBoundsException.class, () -> ArrayUtils.removeAll((double[]) null, 0));
assertNull(ArrayUtils.removeAll((double[]) null, 0));
assertNull(ArrayUtils.removeAll((double[]) null, NULL_INDICES));
final double[] array0 = {};
assertArrayEquals(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
assertNotSame(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
final double[] array1 = new double[1];
assertArrayEquals(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
assertNotSame(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
}
@Test
public void testRemoveAllNullFloatArray() {
assertThrows(IndexOutOfBoundsException.class, () -> ArrayUtils.removeAll((float[]) null, 0));
assertNull(ArrayUtils.removeAll((float[]) null, 0));
assertNull(ArrayUtils.removeAll((float[]) null, NULL_INDICES));
final float[] array0 = {};
assertArrayEquals(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
assertNotSame(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
final float[] array1 = new float[1];
assertArrayEquals(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
assertNotSame(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
}
@Test
public void testRemoveAllNullIntArray() {
assertThrows(IndexOutOfBoundsException.class, () -> ArrayUtils.removeAll((int[]) null, 0));
assertNull(ArrayUtils.removeAll((int[]) null, 0));
assertNull(ArrayUtils.removeAll((int[]) null, NULL_INDICES));
final int[] array0 = {};
assertArrayEquals(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
assertNotSame(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
final int[] array1 = new int[1];
assertArrayEquals(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
assertNotSame(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
}
@Test
public void testRemoveAllNullLongArray() {
assertThrows(IndexOutOfBoundsException.class, () -> ArrayUtils.removeAll((long[]) null, 0));
assertNull(ArrayUtils.removeAll((long[]) null, 0));
assertNull(ArrayUtils.removeAll((long[]) null, NULL_INDICES));
final long[] array0 = {};
assertArrayEquals(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
assertNotSame(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
final long[] array1 = new long[1];
assertArrayEquals(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
assertNotSame(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
}
@Test
public void testRemoveAllNullObjectArray() {
assertThrows(IndexOutOfBoundsException.class, () -> ArrayUtils.remove((Object[]) null, 0));
assertNull(ArrayUtils.removeAll((Object[]) null, 0));
assertNull(ArrayUtils.removeAll((Object[]) null, NULL_INDICES));
final Object[] array0 = {};
assertArrayEquals(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
assertNotSame(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
final Object[] array1 = new Object[1];
assertArrayEquals(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
assertNotSame(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
}
@Test
public void testRemoveAllNullShortArray() {
assertThrows(IndexOutOfBoundsException.class, () -> ArrayUtils.removeAll((short[]) null, 0));
assertNull(ArrayUtils.removeAll((short[]) null, 0));
assertNull(ArrayUtils.removeAll((short[]) null, NULL_INDICES));
final short[] array0 = {};
assertArrayEquals(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
assertNotSame(array0, ArrayUtils.removeAll(array0, NULL_INDICES));
final short[] array1 = new short[1];
assertArrayEquals(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
assertNotSame(array1, ArrayUtils.removeAll(array1, NULL_INDICES));
}
@Test
@ -953,34 +1018,34 @@ public class ArrayUtilsRemoveMultipleTest extends AbstractLangTest {
assertArrayEquals(new double[]{2, 1}, array);
assertEquals(Double.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements((double[]) null, (double) 1, (double) 2);
array = ArrayUtils.removeElements((double[]) null, 1, 2);
assertNull(array);
array = ArrayUtils.removeElements(ArrayUtils.EMPTY_DOUBLE_ARRAY, (double) 1, (double) 2);
array = ArrayUtils.removeElements(ArrayUtils.EMPTY_DOUBLE_ARRAY, 1, 2);
assertArrayEquals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array);
assertEquals(Double.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new double[] { 1 }, (double) 1, (double) 2);
array = ArrayUtils.removeElements(new double[] { 1 }, 1, 2);
assertArrayEquals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array);
assertEquals(Double.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new double[] { 1, 2 }, (double) 1, (double) 2);
array = ArrayUtils.removeElements(new double[] { 1, 2 }, 1, 2);
assertArrayEquals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array);
assertEquals(Double.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new double[] { 1, 2 }, (double) 1, (double) 1);
array = ArrayUtils.removeElements(new double[] { 1, 2 }, 1, 1);
assertArrayEquals(new double[]{2}, array);
assertEquals(Double.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new double[] { 1, 2, 1 }, (double) 1, (double) 2);
array = ArrayUtils.removeElements(new double[] { 1, 2, 1 }, 1, 2);
assertArrayEquals(new double[]{1}, array);
assertEquals(Double.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new double[] { 1, 2, 1 }, (double) 1, (double) 1);
array = ArrayUtils.removeElements(new double[] { 1, 2, 1 }, 1, 1);
assertArrayEquals(new double[]{2}, array);
assertEquals(Double.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new double[] { 1, 2, 1 }, (double) 1, (double) 1, (double) 1, (double) 1);
array = ArrayUtils.removeElements(new double[] { 1, 2, 1 }, 1, 1, 1, 1);
assertArrayEquals(new double[]{2}, array);
assertEquals(Double.TYPE, array.getClass().getComponentType());
}
@ -1009,34 +1074,34 @@ public class ArrayUtilsRemoveMultipleTest extends AbstractLangTest {
assertArrayEquals(new float[]{2, 1}, array);
assertEquals(Float.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements((float[]) null, (float) 1, (float) 1);
array = ArrayUtils.removeElements((float[]) null, 1, 1);
assertNull(array);
array = ArrayUtils.removeElements(ArrayUtils.EMPTY_FLOAT_ARRAY, (float) 1, (float) 1);
array = ArrayUtils.removeElements(ArrayUtils.EMPTY_FLOAT_ARRAY, 1, 1);
assertArrayEquals(ArrayUtils.EMPTY_FLOAT_ARRAY, array);
assertEquals(Float.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new float[] { 1 }, (float) 1, (float) 1);
array = ArrayUtils.removeElements(new float[] { 1 }, 1, 1);
assertArrayEquals(ArrayUtils.EMPTY_FLOAT_ARRAY, array);
assertEquals(Float.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new float[] { 1, 2 }, (float) 1, (float) 2);
array = ArrayUtils.removeElements(new float[] { 1, 2 }, 1, 2);
assertArrayEquals(ArrayUtils.EMPTY_FLOAT_ARRAY, array);
assertEquals(Float.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new float[] { 1, 2 }, (float) 1, (float) 1);
array = ArrayUtils.removeElements(new float[] { 1, 2 }, 1, 1);
assertArrayEquals(new float[]{2}, array);
assertEquals(Float.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new float[] { 1, 2, 1 }, (float) 1, (float) 1);
array = ArrayUtils.removeElements(new float[] { 1, 2, 1 }, 1, 1);
assertArrayEquals(new float[]{2}, array);
assertEquals(Float.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new float[] { 1, 2, 1 }, (float) 1, (float) 2);
array = ArrayUtils.removeElements(new float[] { 1, 2, 1 }, 1, 2);
assertArrayEquals(new float[]{1}, array);
assertEquals(Float.TYPE, array.getClass().getComponentType());
array = ArrayUtils.removeElements(new float[] { 1, 2, 1 }, (float) 1, (float) 1, (float) 1, (float) 1);
array = ArrayUtils.removeElements(new float[] { 1, 2, 1 }, 1, 1, 1, 1);
assertArrayEquals(new float[]{2}, array);
assertEquals(Float.TYPE, array.getClass().getComponentType());
}