Skip to content

Commit

Permalink
Removed support for spliterator.
Browse files Browse the repository at this point in the history
- Narrowed reflective stream conversion to Iterator providing method with name 'iterator'

```markdown
---
I hereby agree to the terms of the JUnit Contributor License Agreement.
```
  • Loading branch information
Hans Zuidervaart committed Jul 7, 2023
1 parent c8a5ba7 commit f32d798
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 269 deletions.
Expand Up @@ -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}.
Expand Down Expand Up @@ -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));
}

/**
Expand All @@ -159,10 +161,9 @@ public static boolean isConvertibleToStream(Class<?> type) {
* <li>{@link Collection}</li>
* <li>{@link Iterable}</li>
* <li>{@link Iterator}</li>
* <li>{@link Spliterator}</li>
* <li>{@link Object} array</li>
* <li>primitive array</li>
* <li>An object that contains an iterator or spliterator returning method</li>
* <li>An object that contains a method with name `iterator` returning an Iterator object</li>
* </ul>
*
* @param object the object to convert into a stream; never {@code null}
Expand Down Expand Up @@ -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);
}
Expand All @@ -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));//
}

/**
Expand Down

This file was deleted.

Expand Up @@ -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, //
Expand All @@ -123,12 +121,11 @@ static Stream<Object> 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 })//
);
}

Expand All @@ -139,6 +136,8 @@ static Stream<Object> objectsConvertibleToStreams() {
Object.class, //
Integer.class, //
String.class, //
IteratorProviderNotUsable.class, //
Spliterator.class, //
int.class, //
boolean.class //
})
Expand Down Expand Up @@ -251,34 +250,22 @@ void toStreamWithIterator() {
assertThat(result).containsExactly("foo", "bar");
}

@Test
@SuppressWarnings("unchecked")
void toStreamWithSpliterator() {
final var input = List.of("foo", "bar").spliterator();

final var result = (Stream<String>) 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<String>) CollectionUtils.toStream(input);

assertThat(result).containsExactly("foo", "bar");
}

@Test
@SuppressWarnings("unchecked")
void toStreamWithSpliteratorProvider() {
final var input = MySpliteratorProvider.of(new String[] { "foo", "bar" });

var result = (Stream<String>) 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
Expand Down Expand Up @@ -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<T> {
private interface IteratorProvider<T> {

@SuppressWarnings("unused")
Spliterator<T> thisReturnsASpliterator();
Iterator<T> iterator();

static <T> MySpliteratorProvider<T> of(T[] elements) {
return () -> Arrays.spliterator(elements);
static <T> IteratorProvider<T> 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<T> {

private interface IteratorProviderNotUsable {
@SuppressWarnings("unused")
Iterator<T> thisReturnsAnIterator();
Object iterator();

static <T> MyIteratorProvider<T> of(T[] elements) {
static <T> IteratorProviderNotUsable of(T[] elements) {
return () -> Spliterators.iterator(Arrays.spliterator(elements));
}
}
Expand Down

0 comments on commit f32d798

Please sign in to comment.