Skip to content

Commit f32d798

Browse files
Removed support for spliterator.
- Narrowed reflective stream conversion to Iterator providing method with name 'iterator' ```markdown --- I hereby agree to the terms of the JUnit Contributor License Agreement. ```
1 parent c8a5ba7 commit f32d798

4 files changed

Lines changed: 53 additions & 269 deletions

File tree

‎junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java‎

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
import java.util.stream.Stream;
3737

3838
import org.apiguardian.api.API;
39+
import org.junit.platform.commons.JUnitException;
3940
import org.junit.platform.commons.PreconditionViolationException;
41+
import org.junit.platform.commons.function.Try;
4042

4143
/**
4244
* Collection of utilities for working with {@link Collection Collections}.
@@ -140,12 +142,12 @@ public static boolean isConvertibleToStream(Class<?> type) {
140142
|| LongStream.class.isAssignableFrom(type)//
141143
|| Iterable.class.isAssignableFrom(type)//
142144
|| Iterator.class.isAssignableFrom(type)//
143-
|| Spliterator.class.isAssignableFrom(type)//
144145
|| Object[].class.isAssignableFrom(type)//
145146
|| (type.isArray() && type.getComponentType().isPrimitive())//
146147
|| Arrays.stream(type.getMethods())//
148+
.filter(m -> m.getName().equals("iterator"))//
147149
.map(Method::getReturnType)//
148-
.anyMatch(returnType -> returnType == Iterator.class || returnType == Spliterator.class));
150+
.anyMatch(returnType -> returnType == Iterator.class));
149151
}
150152

151153
/**
@@ -159,10 +161,9 @@ public static boolean isConvertibleToStream(Class<?> type) {
159161
* <li>{@link Collection}</li>
160162
* <li>{@link Iterable}</li>
161163
* <li>{@link Iterator}</li>
162-
* <li>{@link Spliterator}</li>
163164
* <li>{@link Object} array</li>
164165
* <li>primitive array</li>
165-
* <li>An object that contains an iterator or spliterator returning method</li>
166+
* <li>An object that contains a method with name `iterator` returning an Iterator object</li>
166167
* </ul>
167168
*
168169
* @param object the object to convert into a stream; never {@code null}
@@ -194,9 +195,6 @@ public static Stream<?> toStream(Object object) {
194195
if (object instanceof Iterator) {
195196
return stream(spliteratorUnknownSize((Iterator<?>) object, ORDERED), false);
196197
}
197-
if (object instanceof Spliterator) {
198-
return stream((Spliterator<?>) object, false);
199-
}
200198
if (object instanceof Object[]) {
201199
return Arrays.stream((Object[]) object);
202200
}
@@ -212,7 +210,31 @@ public static Stream<?> toStream(Object object) {
212210
if (object.getClass().isArray() && object.getClass().getComponentType().isPrimitive()) {
213211
return IntStream.range(0, Array.getLength(object)).mapToObj(i -> Array.get(object, i));
214212
}
215-
return StreamUtils.tryConvertToStreamByReflection(object);
213+
return tryConvertToStreamByReflection(object);
214+
}
215+
216+
private static Stream<?> tryConvertToStreamByReflection(Object object) {
217+
Preconditions.notNull(object, "Object must not be null");
218+
try {
219+
String name = "iterator";
220+
Method method = object.getClass().getMethod(name);
221+
if (method.getReturnType() == Iterator.class) {
222+
return stream(() -> tryIteratorToSpliterator(object, method), ORDERED, false);
223+
}
224+
else {
225+
throw new PreconditionViolationException(
226+
"Method with name 'iterator' does not return " + Iterator.class.getName());
227+
}
228+
}
229+
catch (NoSuchMethodException | IllegalStateException e) {
230+
throw new PreconditionViolationException(//
231+
"Cannot convert instance of " + object.getClass().getName() + " into a Stream: " + object, e);
232+
}
233+
}
234+
235+
private static Spliterator<?> tryIteratorToSpliterator(Object object, Method method) {
236+
return Try.call(() -> spliteratorUnknownSize((Iterator<?>) method.invoke(object), ORDERED))//
237+
.getOrThrow(e -> new JUnitException("Cannot invoke method " + method.getName() + " onto " + object, e));//
216238
}
217239

218240
/**

‎junit-platform-commons/src/main/java/org/junit/platform/commons/util/StreamUtils.java‎

Lines changed: 0 additions & 109 deletions
This file was deleted.

‎platform-tests/src/test/java/org/junit/platform/commons/util/CollectionUtilsTests.java‎

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,7 @@ void toUnmodifiableListThrowsOnMutation() {
9999
Collection.class, //
100100
Iterable.class, //
101101
Iterator.class, //
102-
Spliterator.class, //
103-
MySpliteratorProvider.class, //
104-
MyIteratorProvider.class, //
102+
IteratorProvider.class, //
105103
Object[].class, //
106104
String[].class, //
107105
int[].class, //
@@ -123,12 +121,11 @@ static Stream<Object> objectsConvertibleToStreams() {
123121
Stream.of("cat", "dog"), //
124122
DoubleStream.of(42.3), //
125123
IntStream.of(99), //
126-
LongStream.of(100000000), //
124+
LongStream.of(100_000_000), //
127125
Set.of(1, 2, 3), //
128126
Arguments.of((Object) new Object[] { 9, 8, 7 }), //
129127
new int[] { 5, 10, 15 }, //
130-
MySpliteratorProvider.of(new String[] { "mouse", "bear" }), //
131-
MyIteratorProvider.of(new Integer[] { 1, 2, 3, 4, 5 })//
128+
IteratorProvider.of(new Integer[] { 1, 2, 3, 4, 5 })//
132129
);
133130
}
134131

@@ -139,6 +136,8 @@ static Stream<Object> objectsConvertibleToStreams() {
139136
Object.class, //
140137
Integer.class, //
141138
String.class, //
139+
IteratorProviderNotUsable.class, //
140+
Spliterator.class, //
142141
int.class, //
143142
boolean.class //
144143
})
@@ -251,34 +250,22 @@ void toStreamWithIterator() {
251250
assertThat(result).containsExactly("foo", "bar");
252251
}
253252

254-
@Test
255-
@SuppressWarnings("unchecked")
256-
void toStreamWithSpliterator() {
257-
final var input = List.of("foo", "bar").spliterator();
258-
259-
final var result = (Stream<String>) CollectionUtils.toStream(input);
260-
261-
assertThat(result).containsExactly("foo", "bar");
262-
}
263-
264253
@Test
265254
@SuppressWarnings("unchecked")
266255
void toStreamWithIteratorProvider() {
267-
final var input = MyIteratorProvider.of(new String[] { "foo", "bar" });
256+
final var input = IteratorProvider.of(new String[] { "foo", "bar" });
268257

269258
final var result = (Stream<String>) CollectionUtils.toStream(input);
270259

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

274263
@Test
275-
@SuppressWarnings("unchecked")
276-
void toStreamWithSpliteratorProvider() {
277-
final var input = MySpliteratorProvider.of(new String[] { "foo", "bar" });
278-
279-
var result = (Stream<String>) CollectionUtils.toStream(input);
264+
void throwWhenIteratorNamedMethodDoesNotReturnAnIterator() {
265+
var o = IteratorProviderNotUsable.of(new String[] { "Test" });
266+
var e = assertThrows(PreconditionViolationException.class, () -> CollectionUtils.toStream(o));
280267

281-
assertThat(result).containsExactly("foo", "bar");
268+
assertEquals("Method with name 'iterator' does not return java.util.Iterator", e.getMessage());
282269
}
283270

284271
@Test
@@ -345,24 +332,29 @@ public Object convert(Object source, ParameterContext context) throws ArgumentCo
345332
}
346333
}
347334

335+
/**
336+
* An interface that has a method with name 'iterator', returning a java.util/Iterator as a return type
337+
*/
348338
@FunctionalInterface
349-
private interface MySpliteratorProvider<T> {
339+
private interface IteratorProvider<T> {
350340

351341
@SuppressWarnings("unused")
352-
Spliterator<T> thisReturnsASpliterator();
342+
Iterator<T> iterator();
353343

354-
static <T> MySpliteratorProvider<T> of(T[] elements) {
355-
return () -> Arrays.spliterator(elements);
344+
static <T> IteratorProvider<T> of(T[] elements) {
345+
return () -> Spliterators.iterator(Arrays.spliterator(elements));
356346
}
357347
}
358348

349+
/**
350+
* An interface that has a method with name 'iterator', but does not return java.util/Iterator as a return type
351+
*/
359352
@FunctionalInterface
360-
private interface MyIteratorProvider<T> {
361-
353+
private interface IteratorProviderNotUsable {
362354
@SuppressWarnings("unused")
363-
Iterator<T> thisReturnsAnIterator();
355+
Object iterator();
364356

365-
static <T> MyIteratorProvider<T> of(T[] elements) {
357+
static <T> IteratorProviderNotUsable of(T[] elements) {
366358
return () -> Spliterators.iterator(Arrays.spliterator(elements));
367359
}
368360
}

0 commit comments

Comments
 (0)