2020import java .nio .charset .StandardCharsets ;
2121import java .nio .file .Files ;
2222import java .nio .file .Path ;
23+ import java .nio .file .Paths ;
24+ import java .util .Arrays ;
2325import java .util .Collections ;
2426import java .util .Comparator ;
2527import java .util .Map ;
2628import java .util .Set ;
2729import java .util .TreeMap ;
2830import java .util .TreeSet ;
31+ import java .util .function .Predicate ;
32+ import java .util .stream .Collectors ;
2933import java .util .stream .Stream ;
3034
3135import static org .objectweb .asm .Opcodes .ACC_DEPRECATED ;
@@ -49,15 +53,24 @@ public class JdkApiExtractor {
4953 new AccessibleMethod ("close" , "()V" , true , false , true )
5054 );
5155
56+ private static String DEPRECATIONS_ONLY = "--deprecations-only" ;
57+ private static String INCLUDE_INCUBATOR = "--include-incubator" ;
58+
59+ private static Set <String > OPTIONAL_ARGS = Set .of (DEPRECATIONS_ONLY , INCLUDE_INCUBATOR );
60+
5261 public static void main (String [] args ) throws IOException {
5362 validateArgs (args );
54- boolean deprecationsOnly = args . length == 2 && args [ 1 ]. equals ( "--deprecations-only" );
63+ boolean deprecationsOnly = optionalArgs ( args ). anyMatch ( DEPRECATIONS_ONLY :: equals );
5564
5665 Map <String , Set <AccessibleMethod >> accessibleImplementationsByClass = new TreeMap <>();
5766 Map <String , Set <AccessibleMethod >> accessibleForOverridesByClass = new TreeMap <>();
5867 Map <String , Set <AccessibleMethod >> deprecationsByClass = new TreeMap <>();
5968
60- Utils .walkJdkModules ((moduleName , moduleClasses , moduleExports ) -> {
69+ Predicate <String > modulePredicate = Utils .DEFAULT_MODULE_PREDICATE .or (
70+ m -> optionalArgs (args ).anyMatch (INCLUDE_INCUBATOR ::equals ) && m .contains (".incubator." )
71+ );
72+
73+ Utils .walkJdkModules (modulePredicate , (moduleName , moduleClasses , moduleExports ) -> {
6174 var visitor = new AccessibleClassVisitor (
6275 moduleExports ,
6376 accessibleImplementationsByClass ,
@@ -87,16 +100,37 @@ private static String internalClassName(Path clazz, String moduleName) {
87100 return relativePath .substring (0 , relativePath .length () - ".class" .length ());
88101 }
89102
103+ private static Stream <String > optionalArgs (String [] args ) {
104+ return Arrays .stream (args ).skip (1 );
105+ }
106+
90107 @ SuppressForbidden (reason = "cli tool printing to standard err/out" )
91108 private static void validateArgs (String [] args ) {
92- boolean valid = args .length == 1 || (args .length == 2 && "--deprecations-only" .equals (args [1 ]));
93-
109+ boolean valid = args .length > 0 && optionalArgs (args ).allMatch (OPTIONAL_ARGS ::contains );
110+ if (valid && isWritableOutputPath (args [0 ]) == false ) {
111+ valid = false ;
112+ System .err .println ("invalid output path: " + args [0 ]);
113+ }
94114 if (valid == false ) {
95- System .err .println ("usage: <output file path> [--deprecations-only]" );
115+ String optionalArgs = OPTIONAL_ARGS .stream ().collect (Collectors .joining ("] [" , " [" , "]" ));
116+ System .err .println ("usage: <output file path>" + optionalArgs );
96117 System .exit (1 );
97118 }
98119 }
99120
121+ private static boolean isWritableOutputPath (String pathStr ) {
122+ try {
123+ Path path = Paths .get (pathStr );
124+ if (Files .exists (path ) && Files .isRegularFile (path )) {
125+ return Files .isWritable (path );
126+ }
127+ Path parent = path .toAbsolutePath ().getParent ();
128+ return parent != null && Files .isDirectory (parent ) && Files .isWritable (parent );
129+ } catch (Exception e ) {
130+ return false ;
131+ }
132+ }
133+
100134 @ SuppressForbidden (reason = "cli tool printing to standard err/out" )
101135 private static void writeFile (Path path , Map <String , Set <AccessibleMethod >> methods ) throws IOException {
102136 System .out .println ("Writing result for " + Runtime .version () + " to " + path .toAbsolutePath ());
0 commit comments