Skip to content

Commit 16de26f

Browse files
authored
Add Stream counterparts to ReflectionSupport utility methods returning List (#3084)
Resolves #2779
1 parent b3827e2 commit 16de26f

File tree

5 files changed

+244
-15
lines changed

5 files changed

+244
-15
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-M1.adoc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ repository on GitHub.
2626

2727
==== New Features and Improvements
2828

29-
* ❓
30-
29+
* All utility methods from `ReflectionSupport` now have counterparts returning `Stream` instead of `List`
3130

3231
[[release-notes-5.10.0-M1-junit-jupiter]]
3332
=== JUnit Jupiter

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ExtensionUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
import static org.junit.platform.commons.util.AnnotationUtils.findRepeatableAnnotations;
1616
import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated;
1717
import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.TOP_DOWN;
18-
import static org.junit.platform.commons.util.ReflectionUtils.findFields;
1918
import static org.junit.platform.commons.util.ReflectionUtils.getDeclaredConstructor;
19+
import static org.junit.platform.commons.util.ReflectionUtils.streamFields;
2020
import static org.junit.platform.commons.util.ReflectionUtils.tryToReadFieldValue;
2121

2222
import java.lang.reflect.AnnotatedElement;
@@ -93,7 +93,7 @@ static void registerExtensionsFromFields(ExtensionRegistrar registrar, Class<?>
9393

9494
Predicate<Field> predicate = (instance == null ? ReflectionUtils::isStatic : ReflectionUtils::isNotStatic);
9595

96-
findFields(clazz, predicate, TOP_DOWN).stream()//
96+
streamFields(clazz, predicate, TOP_DOWN)//
9797
.sorted(orderComparator)//
9898
.forEach(field -> {
9999
List<Class<? extends Extension>> extensionTypes = streamExtensionTypes(field).collect(toList());

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassSelectorResolver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import static java.util.function.Predicate.isEqual;
1414
import static java.util.stream.Collectors.toCollection;
1515
import static org.junit.jupiter.engine.discovery.predicates.IsTestClassWithTests.isTestOrTestFactoryOrTestTemplateMethod;
16-
import static org.junit.platform.commons.support.ReflectionSupport.findNestedClasses;
16+
import static org.junit.platform.commons.support.ReflectionSupport.streamNestedClasses;
1717
import static org.junit.platform.commons.util.FunctionUtils.where;
1818
import static org.junit.platform.commons.util.ReflectionUtils.findMethods;
1919
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;
@@ -135,7 +135,7 @@ private Resolution toResolution(Optional<? extends ClassBasedTestDescriptor> tes
135135
return Resolution.match(Match.exact(it, () -> {
136136
Stream<DiscoverySelector> methods = findMethods(testClass, isTestOrTestFactoryOrTestTemplateMethod).stream()
137137
.map(method -> selectMethod(testClasses, method));
138-
Stream<NestedClassSelector> nestedClasses = findNestedClasses(testClass, isNestedTestClass).stream()
138+
Stream<NestedClassSelector> nestedClasses = streamNestedClasses(testClass, isNestedTestClass)
139139
.map(nestedClass -> DiscoverySelectors.selectNestedClass(testClasses, nestedClass));
140140
return Stream.concat(methods, nestedClasses).collect(toCollection((Supplier<Set<DiscoverySelector>>) LinkedHashSet::new));
141141
}));

junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.List;
2020
import java.util.Optional;
2121
import java.util.function.Predicate;
22+
import java.util.stream.Stream;
2223

2324
import org.apiguardian.api.API;
2425
import org.junit.platform.commons.JUnitException;
@@ -114,6 +115,31 @@ public static List<Class<?>> findAllClassesInClasspathRoot(URI root, Predicate<C
114115
return ReflectionUtils.findAllClassesInClasspathRoot(root, classFilter, classNameFilter);
115116
}
116117

118+
/**
119+
* Find all {@linkplain Class classes} in the supplied classpath {@code root}
120+
* that match the specified {@code classFilter} and {@code classNameFilter}
121+
* predicates.
122+
*
123+
* <p>The classpath scanning algorithm searches recursively in subpackages
124+
* beginning with the root of the classpath.
125+
*
126+
* @param root the URI for the classpath root in which to scan; never
127+
* {@code null}
128+
* @param classFilter the class type filter; never {@code null}
129+
* @param classNameFilter the class name filter; never {@code null}
130+
* @return a stream of all such classes found; never {@code null}
131+
* but potentially empty
132+
* @since 1.10
133+
* @see #findAllClassesInPackage(String, Predicate, Predicate)
134+
* @see #findAllClassesInModule(String, Predicate, Predicate)
135+
*/
136+
@API(status = MAINTAINED, since = "1.10")
137+
public static Stream<Class<?>> streamAllClassesInClasspathRoot(URI root, Predicate<Class<?>> classFilter,
138+
Predicate<String> classNameFilter) {
139+
140+
return ReflectionUtils.streamAllClassesInClasspathRoot(root, classFilter, classNameFilter);
141+
}
142+
117143
/**
118144
* Find all {@linkplain Class classes} in the supplied {@code basePackageName}
119145
* that match the specified {@code classFilter} and {@code classNameFilter}
@@ -138,6 +164,32 @@ public static List<Class<?>> findAllClassesInPackage(String basePackageName, Pre
138164
return ReflectionUtils.findAllClassesInPackage(basePackageName, classFilter, classNameFilter);
139165
}
140166

167+
/**
168+
* Find all {@linkplain Class classes} in the supplied {@code basePackageName}
169+
* that match the specified {@code classFilter} and {@code classNameFilter}
170+
* predicates.
171+
*
172+
* <p>The classpath scanning algorithm searches recursively in subpackages
173+
* beginning within the supplied base package.
174+
*
175+
* @param basePackageName the name of the base package in which to start
176+
* scanning; must not be {@code null} and must be valid in terms of Java
177+
* syntax
178+
* @param classFilter the class type filter; never {@code null}
179+
* @param classNameFilter the class name filter; never {@code null}
180+
* @return a stream of all such classes found; never {@code null}
181+
* but potentially empty
182+
* @since 1.10
183+
* @see #findAllClassesInClasspathRoot(URI, Predicate, Predicate)
184+
* @see #findAllClassesInModule(String, Predicate, Predicate)
185+
*/
186+
@API(status = MAINTAINED, since = "1.10")
187+
public static Stream<Class<?>> streamAllClassesInPackage(String basePackageName, Predicate<Class<?>> classFilter,
188+
Predicate<String> classNameFilter) {
189+
190+
return ReflectionUtils.streamAllClassesInPackage(basePackageName, classFilter, classNameFilter);
191+
}
192+
141193
/**
142194
* Find all {@linkplain Class classes} in the supplied {@code moduleName}
143195
* that match the specified {@code classFilter} and {@code classNameFilter}
@@ -162,6 +214,31 @@ public static List<Class<?>> findAllClassesInModule(String moduleName, Predicate
162214
return ReflectionUtils.findAllClassesInModule(moduleName, classFilter, classNameFilter);
163215
}
164216

217+
/**
218+
* Find all {@linkplain Class classes} in the supplied {@code moduleName}
219+
* that match the specified {@code classFilter} and {@code classNameFilter}
220+
* predicates.
221+
*
222+
* <p>The module-path scanning algorithm searches recursively in all
223+
* packages contained in the module.
224+
*
225+
* @param moduleName the name of the module to scan; never {@code null} or
226+
* <em>empty</em>
227+
* @param classFilter the class type filter; never {@code null}
228+
* @param classNameFilter the class name filter; never {@code null}
229+
* @return a stream of all such classes found; never {@code null}
230+
* but potentially empty
231+
* @since 1.10
232+
* @see #findAllClassesInClasspathRoot(URI, Predicate, Predicate)
233+
* @see #findAllClassesInPackage(String, Predicate, Predicate)
234+
*/
235+
@API(status = MAINTAINED, since = "1.10")
236+
public static Stream<Class<?>> streamAllClassesInModule(String moduleName, Predicate<Class<?>> classFilter,
237+
Predicate<String> classNameFilter) {
238+
239+
return ReflectionUtils.streamAllClassesInModule(moduleName, classFilter, classNameFilter);
240+
}
241+
165242
/**
166243
* Create a new instance of the specified {@link Class} by invoking
167244
* the constructor whose argument list matches the types of the supplied
@@ -225,6 +302,31 @@ public static List<Field> findFields(Class<?> clazz, Predicate<Field> predicate,
225302
ReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));
226303
}
227304

305+
/**
306+
* Find all {@linkplain Field fields} of the supplied class or interface
307+
* that match the specified {@code predicate}.
308+
*
309+
* <p>Fields declared in the same class or interface will be ordered using
310+
* an algorithm that is deterministic but intentionally nonobvious.
311+
*
312+
* <p>The results will not contain fields that are <em>hidden</em> or
313+
* {@linkplain Field#isSynthetic() synthetic}.
314+
*
315+
* @param clazz the class or interface in which to find the fields; never {@code null}
316+
* @param predicate the field filter; never {@code null}
317+
* @param traversalMode the hierarchy traversal mode; never {@code null}
318+
* @return a stream of all such fields found; never {@code null}
319+
* but potentially empty
320+
* @since 1.10
321+
*/
322+
@API(status = MAINTAINED, since = "1.10")
323+
public static Stream<Field> streamFields(Class<?> clazz, Predicate<Field> predicate,
324+
HierarchyTraversalMode traversalMode) {
325+
Preconditions.notNull(traversalMode, "HierarchyTraversalMode must not be null");
326+
return ReflectionUtils.streamFields(clazz, predicate,
327+
ReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));
328+
}
329+
228330
/**
229331
* Try to read the value of a potentially inaccessible field.
230332
*
@@ -307,6 +409,34 @@ public static List<Method> findMethods(Class<?> clazz, Predicate<Method> predica
307409
ReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));
308410
}
309411

412+
/**
413+
* Find all distinct {@linkplain Method methods} of the supplied class or
414+
* interface that match the specified {@code predicate}.
415+
*
416+
* <p>The results will not contain instance methods that are <em>overridden</em>
417+
* or {@code static} methods that are <em>hidden</em>.
418+
*
419+
* <p>If you're are looking for methods annotated with a certain annotation
420+
* type, consider using
421+
* {@link AnnotationSupport#findAnnotatedMethods(Class, Class, HierarchyTraversalMode)}.
422+
*
423+
* @param clazz the class or interface in which to find the methods; never {@code null}
424+
* @param predicate the method filter; never {@code null}
425+
* @param traversalMode the hierarchy traversal mode; never {@code null}
426+
* @return a stream of all such methods found; never {@code null}
427+
* @since 1.10
428+
* but potentially empty
429+
*/
430+
@API(status = MAINTAINED, since = "1.10")
431+
public static Stream<Method> streamMethods(Class<?> clazz, Predicate<Method> predicate,
432+
HierarchyTraversalMode traversalMode) {
433+
434+
Preconditions.notNull(traversalMode, "HierarchyTraversalMode must not be null");
435+
436+
return ReflectionUtils.streamMethods(clazz, predicate,
437+
ReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));
438+
}
439+
310440
/**
311441
* Find all nested classes within the supplied class, or inherited by the
312442
* supplied class, that conform to the supplied predicate.
@@ -333,4 +463,32 @@ public static List<Class<?>> findNestedClasses(Class<?> clazz, Predicate<Class<?
333463
return ReflectionUtils.findNestedClasses(clazz, predicate);
334464
}
335465

466+
/**
467+
* Find all nested classes within the supplied class, or inherited by the
468+
* supplied class, that conform to the supplied predicate.
469+
*
470+
* <p>This method does <strong>not</strong> search for nested classes
471+
* recursively.
472+
*
473+
* <p>As of JUnit Platform 1.6, this method detects cycles in <em>inner</em>
474+
* class hierarchies &mdash; from the supplied class up to the outermost
475+
* enclosing class &mdash; and throws a {@link JUnitException} if such a cycle
476+
* is detected. Cycles within inner class hierarchies <em>below</em> the
477+
* supplied class are not detected by this method.
478+
*
479+
* @param clazz the class to be searched; never {@code null}
480+
* @param predicate the predicate against which the list of nested classes is
481+
* checked; never {@code null}
482+
* @return a stream of all such classes found; never {@code null}
483+
* but potentially empty
484+
* @throws JUnitException if a cycle is detected within an inner class hierarchy
485+
* @since 1.10
486+
*/
487+
@API(status = MAINTAINED, since = "1.10")
488+
public static Stream<Class<?>> streamNestedClasses(Class<?> clazz, Predicate<Class<?>> predicate)
489+
throws JUnitException {
490+
491+
return ReflectionUtils.streamNestedClasses(clazz, predicate);
492+
}
493+
336494
}

0 commit comments

Comments
 (0)