diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java index c4b790e2f20b..d563c7f934e2 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java @@ -36,7 +36,9 @@ import java.util.stream.Stream; import org.apiguardian.api.API; +import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.function.Try; /** * Collection of utilities for working with {@link Collection Collections}. @@ -140,12 +142,12 @@ public static boolean isConvertibleToStream(Class type) { || LongStream.class.isAssignableFrom(type)// || Iterable.class.isAssignableFrom(type)// || Iterator.class.isAssignableFrom(type)// - || Spliterator.class.isAssignableFrom(type)// || Object[].class.isAssignableFrom(type)// || (type.isArray() && type.getComponentType().isPrimitive())// || Arrays.stream(type.getMethods())// + .filter(m -> m.getName().equals("iterator"))// .map(Method::getReturnType)// - .anyMatch(returnType -> returnType == Iterator.class || returnType == Spliterator.class)); + .anyMatch(returnType -> returnType == Iterator.class)); } /** @@ -159,10 +161,9 @@ public static boolean isConvertibleToStream(Class type) { *
  • {@link Collection}
  • *
  • {@link Iterable}
  • *
  • {@link Iterator}
  • - *
  • {@link Spliterator}
  • *
  • {@link Object} array
  • *
  • primitive array
  • - *
  • An object that contains an iterator or spliterator returning method
  • + *
  • An object that contains a method with name `iterator` returning an Iterator object
  • * * * @param object the object to convert into a stream; never {@code null} @@ -194,9 +195,6 @@ public static Stream toStream(Object object) { if (object instanceof Iterator) { return stream(spliteratorUnknownSize((Iterator) object, ORDERED), false); } - if (object instanceof Spliterator) { - return stream((Spliterator) object, false); - } if (object instanceof Object[]) { return Arrays.stream((Object[]) object); } @@ -212,7 +210,31 @@ public static Stream toStream(Object object) { if (object.getClass().isArray() && object.getClass().getComponentType().isPrimitive()) { return IntStream.range(0, Array.getLength(object)).mapToObj(i -> Array.get(object, i)); } - return StreamUtils.tryConvertToStreamByReflection(object); + return tryConvertToStreamByReflection(object); + } + + private static Stream tryConvertToStreamByReflection(Object object) { + Preconditions.notNull(object, "Object must not be null"); + try { + String name = "iterator"; + Method method = object.getClass().getMethod(name); + if (method.getReturnType() == Iterator.class) { + return stream(() -> tryIteratorToSpliterator(object, method), ORDERED, false); + } + else { + throw new PreconditionViolationException( + "Method with name 'iterator' does not return " + Iterator.class.getName()); + } + } + catch (NoSuchMethodException | IllegalStateException e) { + throw new PreconditionViolationException(// + "Cannot convert instance of " + object.getClass().getName() + " into a Stream: " + object, e); + } + } + + private static Spliterator tryIteratorToSpliterator(Object object, Method method) { + return Try.call(() -> spliteratorUnknownSize((Iterator) method.invoke(object), ORDERED))// + .getOrThrow(e -> new JUnitException("Cannot invoke method " + method.getName() + " onto " + object, e));// } /** diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/StreamUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/StreamUtils.java deleted file mode 100644 index 1c629d251f71..000000000000 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/StreamUtils.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.platform.commons.util; - -import static java.util.Spliterator.ORDERED; -import static java.util.Spliterators.spliteratorUnknownSize; -import static java.util.stream.StreamSupport.stream; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Iterator; -import java.util.Optional; -import java.util.Spliterator; -import java.util.stream.Stream; - -import org.junit.platform.commons.JUnitException; -import org.junit.platform.commons.PreconditionViolationException; -import org.junit.platform.commons.function.Try; - -/** - * Collection of utilities for working with {@link Stream Streams}. - * - * @since 5.10 - */ -final class StreamUtils { - - private StreamUtils() { - } - - static Stream tryConvertToStreamByReflection(Object object) { - Preconditions.notNull(object, "Object must not be null"); - Class theClass = object.getClass(); - try { - String name = "iterator"; - Method method = theClass.getMethod(name); - if (method.getReturnType() == Iterator.class) { - return stream(() -> tryIteratorToSpliterator(object, method), ORDERED, false); - } - else { - throw new IllegalStateException( - "Method with name 'iterator' does not return " + Iterator.class.getName()); - } - } - catch (NoSuchMethodException | IllegalStateException e) { - return tryConvertToStreamBySpliterator(object, e); - } - } - - private static Stream tryConvertToStreamBySpliterator(Object object, Exception e) { - try { - String name = "spliterator"; - Method method = object.getClass().getMethod(name); - if (method.getReturnType() == Spliterator.class) { - return stream(() -> tryInvokeSpliterator(object, method), ORDERED, false); - } - else { - throw new IllegalStateException( - "Method with name '" + name + "' does not return " + Spliterator.class.getName()); - } - } - catch (NoSuchMethodException | IllegalStateException ex) { - ex.addSuppressed(e); - return tryConvertByIteratorSpliteratorReturnType(object, ex); - } - } - - private static Stream tryConvertByIteratorSpliteratorReturnType(Object object, Exception ex) { - return streamFromSpliteratorSupplier(object)// - .orElseGet(() -> streamFromIteratorSupplier(object)// - .orElseThrow(() -> // - new PreconditionViolationException(// - "Cannot convert instance of " + object.getClass().getName() + " into a Stream: " + object, - ex))); - } - - private static Optional> streamFromSpliteratorSupplier(Object object) { - return Arrays.stream(object.getClass().getMethods())// - .filter(m -> m.getReturnType() == Spliterator.class)// - .findFirst()// - .map(m -> stream(() -> tryInvokeSpliterator(object, m), ORDERED, false));// - } - - private static Optional> streamFromIteratorSupplier(Object object) { - return Arrays.stream(object.getClass().getMethods())// - .filter(m -> m.getReturnType() == Iterator.class)// - .findFirst()// - .map(m -> stream(() -> tryIteratorToSpliterator(object, m), ORDERED, false));// - } - - private static Spliterator tryInvokeSpliterator(Object object, Method method) { - return Try.call(() -> (Spliterator) method.invoke(object))// - .getOrThrow(e -> new JUnitException("Cannot invoke method " + method.getName() + " onto " + object, e));// - } - - private static Spliterator tryIteratorToSpliterator(Object object, Method method) { - return Try.call(() -> method.invoke(object))// - .andThen(m -> Try.call(() -> spliteratorUnknownSize((Iterator) m, ORDERED)))// - .getOrThrow(e -> new JUnitException("Cannot invoke method " + method.getName() + " onto " + object, e));// - } - -} diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/CollectionUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/CollectionUtilsTests.java index 642159695910..d4a5e41efb5e 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/CollectionUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/CollectionUtilsTests.java @@ -99,9 +99,7 @@ void toUnmodifiableListThrowsOnMutation() { Collection.class, // Iterable.class, // Iterator.class, // - Spliterator.class, // - MySpliteratorProvider.class, // - MyIteratorProvider.class, // + IteratorProvider.class, // Object[].class, // String[].class, // int[].class, // @@ -123,12 +121,11 @@ static Stream objectsConvertibleToStreams() { Stream.of("cat", "dog"), // DoubleStream.of(42.3), // IntStream.of(99), // - LongStream.of(100000000), // + LongStream.of(100_000_000), // Set.of(1, 2, 3), // Arguments.of((Object) new Object[] { 9, 8, 7 }), // new int[] { 5, 10, 15 }, // - MySpliteratorProvider.of(new String[] { "mouse", "bear" }), // - MyIteratorProvider.of(new Integer[] { 1, 2, 3, 4, 5 })// + IteratorProvider.of(new Integer[] { 1, 2, 3, 4, 5 })// ); } @@ -139,6 +136,8 @@ static Stream objectsConvertibleToStreams() { Object.class, // Integer.class, // String.class, // + IteratorProviderNotUsable.class, // + Spliterator.class, // int.class, // boolean.class // }) @@ -251,20 +250,10 @@ void toStreamWithIterator() { assertThat(result).containsExactly("foo", "bar"); } - @Test - @SuppressWarnings("unchecked") - void toStreamWithSpliterator() { - final var input = List.of("foo", "bar").spliterator(); - - final var result = (Stream) CollectionUtils.toStream(input); - - assertThat(result).containsExactly("foo", "bar"); - } - @Test @SuppressWarnings("unchecked") void toStreamWithIteratorProvider() { - final var input = MyIteratorProvider.of(new String[] { "foo", "bar" }); + final var input = IteratorProvider.of(new String[] { "foo", "bar" }); final var result = (Stream) CollectionUtils.toStream(input); @@ -272,13 +261,11 @@ void toStreamWithIteratorProvider() { } @Test - @SuppressWarnings("unchecked") - void toStreamWithSpliteratorProvider() { - final var input = MySpliteratorProvider.of(new String[] { "foo", "bar" }); - - var result = (Stream) CollectionUtils.toStream(input); + void throwWhenIteratorNamedMethodDoesNotReturnAnIterator() { + var o = IteratorProviderNotUsable.of(new String[] { "Test" }); + var e = assertThrows(PreconditionViolationException.class, () -> CollectionUtils.toStream(o)); - assertThat(result).containsExactly("foo", "bar"); + assertEquals("Method with name 'iterator' does not return java.util.Iterator", e.getMessage()); } @Test @@ -345,24 +332,29 @@ public Object convert(Object source, ParameterContext context) throws ArgumentCo } } + /** + * An interface that has a method with name 'iterator', returning a java.util/Iterator as a return type + */ @FunctionalInterface - private interface MySpliteratorProvider { + private interface IteratorProvider { @SuppressWarnings("unused") - Spliterator thisReturnsASpliterator(); + Iterator iterator(); - static MySpliteratorProvider of(T[] elements) { - return () -> Arrays.spliterator(elements); + static IteratorProvider of(T[] elements) { + return () -> Spliterators.iterator(Arrays.spliterator(elements)); } } + /** + * An interface that has a method with name 'iterator', but does not return java.util/Iterator as a return type + */ @FunctionalInterface - private interface MyIteratorProvider { - + private interface IteratorProviderNotUsable { @SuppressWarnings("unused") - Iterator thisReturnsAnIterator(); + Object iterator(); - static MyIteratorProvider of(T[] elements) { + static IteratorProviderNotUsable of(T[] elements) { return () -> Spliterators.iterator(Arrays.spliterator(elements)); } } diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/StreamUtilsTest.java b/platform-tests/src/test/java/org/junit/platform/commons/util/StreamUtilsTest.java deleted file mode 100644 index 6fda1a6e28d9..000000000000 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/StreamUtilsTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2015-2023 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.platform.commons.util; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.Iterator; -import java.util.List; -import java.util.Spliterator; -import java.util.stream.Stream; - -import org.junit.jupiter.api.Test; -import org.junit.platform.commons.PreconditionViolationException; - -class StreamUtilsTest { - - @Test - void throwWhenIteratorNamedMethodDoesNotReturnAnIterator() { - var o = IteratorProviderNotUsable.of("Test"); - var e = assertThrows(PreconditionViolationException.class, () -> StreamUtils.tryConvertToStreamByReflection(o)); - - e.printStackTrace(); - assertEquals(1, e.getCause().getSuppressed().length); - } - - @SuppressWarnings("unchecked") - @Test - void usesSpliteratorToConvertToStream() { - var object = SpliteratorProvider.of(List.of("this", "is", "a", "test")); - - var result = (Stream) StreamUtils.tryConvertToStreamByReflection(object); - - assertThat(result).containsExactly("this", "is", "a", "test"); - } - - @SuppressWarnings("unchecked") - @Test - void usesIteratorExposingMethodToConvertToStream() { - var object = IteratorExposing.of(List.of("this", "is", "a", "test")); - - var result = (Stream) StreamUtils.tryConvertToStreamByReflection(object); - - assertThat(result).containsExactly("this", "is", "a", "test"); - } - - /** - * An interface that has an iterator method but does not return java.util/Iterator as a return type - */ - @FunctionalInterface - private interface IteratorProviderNotUsable { - @SuppressWarnings("unused") - Object iterator(); - - static IteratorProviderNotUsable of(Object o) { - return () -> o; - } - } - - /** - * An object that exposes: - *
      - *
    1. a method with name 'iterator' that does not return an iterator
    2. - *
    3. a method with name 'spliterator' that returns a java.util.Spliterator
    4. - *
    - * @param The type of the spliterator - */ - @FunctionalInterface - private interface SpliteratorProvider { - - @SuppressWarnings("unused") - default Object iterator() { - return null; - } - - @SuppressWarnings("unused") - Spliterator spliterator(); - - static SpliteratorProvider of(Iterable iterable) { - return iterable::spliterator; - } - - } - - /** - * An object that exposes: - *
      - *
    1. a method with name 'iterator' that does not return an iterator
    2. - *
    3. a method with name 'spliterator' that does not return a java.util.Spliterator
    4. - *
    5. a method with other name than 'iterator' returning an iterator
    6. - *
    - */ - private interface IteratorExposing { - - @SuppressWarnings("unused") - default Object iterator() { - return null; - } - - @SuppressWarnings("unused") - default Object spliterator() { - return null; - } - - @SuppressWarnings("unused") - Iterator returnsAnIterator(); - - static IteratorExposing of(Iterable iterable) { - return iterable::iterator; - } - } -}