44
55import java .io .File ;
66import java .io .IOException ;
7- import java .lang .reflect .Method ;
8- import java .net .JarURLConnection ;
97import java .net .URL ;
10- import java .net .URLConnection ;
118import java .net .URLDecoder ;
129import java .util .Collection ;
13- import java .util .Enumeration ;
10+ import java .util .Iterator ;
1411import java .util .List ;
12+ import java .util .Objects ;
13+ import java .util .Spliterator ;
14+ import java .util .Spliterators ;
1515import java .util .concurrent .ConcurrentLinkedDeque ;
16- import java .util .jar . JarEntry ;
17- import java .util .jar . JarFile ;
18- import java .util .regex . Pattern ;
16+ import java .util .function . Function ;
17+ import java .util .stream . Stream ;
18+ import java .util .stream . StreamSupport ;
1919import org .testng .collections .Lists ;
20+ import org .testng .internal .protocols .Input ;
21+ import org .testng .internal .protocols .Processor ;
22+ import org .testng .internal .protocols .UnhandledIOException ;
2023
2124/**
2225 * Utility class that finds all the classes in a given package.
2629 * @author <a href="mailto:[email protected] ">Cedric Beust</a> 2730 */
2831public class PackageUtils {
29- private static final String PACKAGE_UTILS = PackageUtils .class .getSimpleName ();
3032 private static String [] testClassPaths ;
3133
3234 /** The additional class loaders to find classes in. */
@@ -45,101 +47,36 @@ private PackageUtils() {
4547 */
4648 public static String [] findClassesInPackage (
4749 String packageName , List <String > included , List <String > excluded ) throws IOException {
48- String packageOnly = packageName ;
49- boolean recursive = false ;
50- if (packageName .endsWith (".*" )) {
51- packageOnly = packageName .substring (0 , packageName .lastIndexOf (".*" ));
52- recursive = true ;
50+ String packageNameWithoutWildCards = packageName ;
51+ boolean recursive = packageName .endsWith (".*" );
52+ if (recursive ) {
53+ packageNameWithoutWildCards = packageName .substring (0 , packageName .lastIndexOf (".*" ));
5354 }
5455
55- List <String > vResult = Lists .newArrayList ();
56- String packageDirName = packageOnly .replace ('.' , '/' ) + (packageOnly .length () > 0 ? "/" : "" );
56+ String packageDirName =
57+ packageNameWithoutWildCards .replace ('.' , '/' )
58+ + (packageNameWithoutWildCards .length () > 0 ? "/" : "" );
59+
60+ Input input =
61+ Input .Builder .newBuilder ()
62+ .forPackageWithoutWildCards (packageNameWithoutWildCards )
63+ .withRecursive (recursive )
64+ .include (included )
65+ .exclude (excluded )
66+ .withPackageName (packageName )
67+ .forPackageDirectory (packageDirName )
68+ .build ();
5769
58- List <URL > dirs = Lists .newArrayList ();
5970 // go through additional class loaders
6071 List <ClassLoader > allClassLoaders =
6172 ClassHelper .appendContextualClassLoaders (Lists .newArrayList (classLoaders ));
6273
63- for (ClassLoader classLoader : allClassLoaders ) {
64- if (null == classLoader ) {
65- continue ;
66- }
67- Enumeration <URL > dirEnumeration = classLoader .getResources (packageDirName );
68- while (dirEnumeration .hasMoreElements ()) {
69- URL dir = dirEnumeration .nextElement ();
70- dirs .add (dir );
71- }
72- }
73-
74- for (URL url : dirs ) {
75- String protocol = url .getProtocol ();
76- if (!matchTestClasspath (url , packageDirName , recursive )) {
77- continue ;
78- }
79-
80- if ("file" .equals (protocol )) {
81- findClassesInDirPackage (
82- packageOnly ,
83- included ,
84- excluded ,
85- URLDecoder .decode (url .getFile (), UTF_8 ),
86- recursive ,
87- vResult );
88- } else if ("jar" .equals (protocol )) {
89- JarFile jar = ((JarURLConnection ) url .openConnection ()).getJarFile ();
90- Enumeration <JarEntry > entries = jar .entries ();
91- while (entries .hasMoreElements ()) {
92- JarEntry entry = entries .nextElement ();
93- String name = entry .getName ();
94- if (name .startsWith ("module-info" ) || name .startsWith ("META-INF" )) {
95- continue ;
96- }
97- if (name .charAt (0 ) == '/' ) {
98- name = name .substring (1 );
99- }
100- if (name .startsWith (packageDirName )) {
101- int idx = name .lastIndexOf ('/' );
102- if (idx != -1 ) {
103- packageName = name .substring (0 , idx ).replace ('/' , '.' );
104- }
105-
106- if (recursive || packageName .equals (packageOnly )) {
107- // it's not inside a deeper dir
108- Utils .log (PACKAGE_UTILS , 4 , "Package name is " + packageName );
109- if (name .endsWith (".class" ) && !entry .isDirectory ()) {
110- String className = name .substring (packageName .length () + 1 , name .length () - 6 );
111- Utils .log (
112- PACKAGE_UTILS ,
113- 4 ,
114- "Found class " + className + ", seeing it if it's included or excluded" );
115- includeOrExcludeClass (packageName , className , included , excluded , vResult );
116- }
117- }
118- }
119- }
120- } else if ("bundleresource" .equals (protocol )) {
121- try {
122- Class <?>[] params = {};
123- // BundleURLConnection
124- URLConnection connection = url .openConnection ();
125- Method thisMethod =
126- url .openConnection ().getClass ().getDeclaredMethod ("getFileURL" , params );
127- Object [] paramsObj = {};
128- URL fileUrl = (URL ) thisMethod .invoke (connection , paramsObj );
129- findClassesInDirPackage (
130- packageOnly ,
131- included ,
132- excluded ,
133- URLDecoder .decode (fileUrl .getFile (), UTF_8 ),
134- recursive ,
135- vResult );
136- } catch (Exception ex ) {
137- // ignore - probably not an Eclipse OSGi bundle
138- }
139- }
140- }
141-
142- return vResult .toArray (new String [0 ]);
74+ return allClassLoaders .stream ()
75+ .filter (Objects ::nonNull )
76+ .flatMap (asURLs (packageDirName ))
77+ .filter (url -> matchTestClasspath (url , packageDirName , recursive ))
78+ .flatMap (url -> Processor .newInstance (url .getProtocol ()).process (input , url ).stream ())
79+ .toArray (String []::new );
14380 }
14481
14582 private static String [] getTestClasspath () {
@@ -174,14 +111,25 @@ private static String[] getTestClasspath() {
174111 return testClassPaths ;
175112 }
176113
114+ private static Function <ClassLoader , Stream <URL >> asURLs (String packageDir ) {
115+ return cl -> {
116+ try {
117+ Iterator <URL > iterator = cl .getResources (packageDir ).asIterator ();
118+ return StreamSupport .stream (
119+ Spliterators .spliteratorUnknownSize (iterator , Spliterator .ORDERED ), false );
120+ } catch (IOException e ) {
121+ throw new UnhandledIOException (e );
122+ }
123+ };
124+ }
125+
177126 private static boolean matchTestClasspath (URL url , String lastFragment , boolean recursive ) {
178127 String [] classpathFragments = getTestClasspath ();
179128 if (null == classpathFragments ) {
180129 return true ;
181130 }
182131
183- String fileName = "" ;
184- fileName = URLDecoder .decode (url .getFile (), UTF_8 );
132+ String fileName = URLDecoder .decode (url .getFile (), UTF_8 );
185133
186134 for (String classpathFrag : classpathFragments ) {
187135 String path = classpathFrag + lastFragment ;
@@ -195,101 +143,6 @@ private static boolean matchTestClasspath(URL url, String lastFragment, boolean
195143 return true ;
196144 }
197145 }
198-
199- return false ;
200- }
201-
202- private static void findClassesInDirPackage (
203- String packageName ,
204- List <String > included ,
205- List <String > excluded ,
206- String packagePath ,
207- final boolean recursive ,
208- List <String > classes ) {
209- File dir = new File (packagePath );
210-
211- if (!dir .exists () || !dir .isDirectory ()) {
212- return ;
213- }
214-
215- File [] dirfiles =
216- dir .listFiles (
217- file ->
218- (recursive && file .isDirectory ())
219- || (file .getName ().endsWith (".class" ))
220- || (file .getName ().endsWith (".groovy" )));
221-
222- Utils .log (PACKAGE_UTILS , 4 , "Looking for test classes in the directory: " + dir );
223- if (dirfiles == null ) {
224- return ;
225- }
226- for (File file : dirfiles ) {
227- if (file .isDirectory ()) {
228- findClassesInDirPackage (
229- makeFullClassName (packageName , file .getName ()),
230- included ,
231- excluded ,
232- file .getAbsolutePath (),
233- recursive ,
234- classes );
235- } else {
236- String className = file .getName ().substring (0 , file .getName ().lastIndexOf ('.' ));
237- Utils .log (
238- PACKAGE_UTILS ,
239- 4 ,
240- "Found class " + className + ", seeing it if it's included or excluded" );
241- includeOrExcludeClass (packageName , className , included , excluded , classes );
242- }
243- }
244- }
245-
246- private static String makeFullClassName (String pkg , String cls ) {
247- return pkg .length () > 0 ? pkg + "." + cls : cls ;
248- }
249-
250- private static void includeOrExcludeClass (
251- String packageName ,
252- String className ,
253- List <String > included ,
254- List <String > excluded ,
255- List <String > classes ) {
256- if (isIncluded (packageName , included , excluded )) {
257- Utils .log (PACKAGE_UTILS , 4 , "... Including class " + className );
258- classes .add (makeFullClassName (packageName , className ));
259- } else {
260- Utils .log (PACKAGE_UTILS , 4 , "... Excluding class " + className );
261- }
262- }
263-
264- /** @return true if name should be included. */
265- private static boolean isIncluded (String name , List <String > included , List <String > excluded ) {
266- boolean result ;
267-
268- //
269- // If no includes nor excludes were specified, return true.
270- //
271- if (included .isEmpty () && excluded .isEmpty ()) {
272- result = true ;
273- } else {
274- boolean isIncluded = PackageUtils .find (name , included );
275- boolean isExcluded = PackageUtils .find (name , excluded );
276- if (isIncluded && !isExcluded ) {
277- result = true ;
278- } else if (isExcluded ) {
279- result = false ;
280- } else {
281- result = included .isEmpty ();
282- }
283- }
284- return result ;
285- }
286-
287- private static boolean find (String name , List <String > list ) {
288- for (String regexpStr : list ) {
289- if (Pattern .matches (regexpStr , name )) {
290- return true ;
291- }
292- }
293146 return false ;
294147 }
295148}
0 commit comments