@@ -79,53 +79,119 @@ enum Nothing implements ApiTracker {}
7979 /// Discovers all classes in the local JSON API packages
8080 /// @return sorted set of classes from jdk.sandbox.java.util.json and jdk.sandbox.internal.util.json
8181 static Set <Class <?>> discoverLocalJsonClasses () {
82- LOGGER .fine ( "Discovering local JSON classes " );
82+ LOGGER .info ( "Starting class discovery for JSON API packages " );
8383 final var classes = new TreeSet <Class <?>>((a , b ) -> a .getName ().compareTo (b .getName ()));
8484
85- // Known public API classes
86- final var publicApiClasses = List .of (
87- "jdk.sandbox.java.util.json.Json" ,
88- "jdk.sandbox.java.util.json.JsonValue" ,
89- "jdk.sandbox.java.util.json.JsonObject" ,
90- "jdk.sandbox.java.util.json.JsonArray" ,
91- "jdk.sandbox.java.util.json.JsonString" ,
92- "jdk.sandbox.java.util.json.JsonNumber" ,
93- "jdk.sandbox.java.util.json.JsonBoolean" ,
94- "jdk.sandbox.java.util.json.JsonNull" ,
95- "jdk.sandbox.java.util.json.JsonParseException"
85+ // Packages to scan
86+ final var packages = List .of (
87+ "jdk.sandbox.java.util.json" ,
88+ "jdk.sandbox.internal.util.json"
9689 );
9790
98- // Known internal implementation classes
99- final var internalClasses = List .of (
100- "jdk.sandbox.internal.util.json.JsonParser" ,
101- "jdk.sandbox.internal.util.json.JsonObjectImpl" ,
102- "jdk.sandbox.internal.util.json.JsonArrayImpl" ,
103- "jdk.sandbox.internal.util.json.JsonStringImpl" ,
104- "jdk.sandbox.internal.util.json.JsonNumberImpl"
105- );
91+ final var classLoader = Thread .currentThread ().getContextClassLoader ();
92+
93+ for (final var packageName : packages ) {
94+ try {
95+ final var path = packageName .replace ('.' , '/' );
96+ final var resources = classLoader .getResources (path );
97+
98+ while (resources .hasMoreElements ()) {
99+ final var url = resources .nextElement ();
100+ LOGGER .fine (() -> "Scanning resource: " + url );
101+
102+ if ("file" .equals (url .getProtocol ())) {
103+ // Handle directory scanning
104+ scanDirectory (new java .io .File (url .toURI ()), packageName , classes );
105+ } else if ("jar" .equals (url .getProtocol ())) {
106+ // Handle JAR scanning
107+ scanJar (url , packageName , classes );
108+ }
109+ }
110+ } catch (Exception e ) {
111+ LOGGER .log (Level .WARNING , "Error scanning package: " + packageName , e );
112+ }
113+ }
106114
107- // Load all known classes
108- Stream .concat (publicApiClasses .stream (), internalClasses .stream ())
109- .forEach (className -> {
115+ LOGGER .info ("Discovered " + classes .size () + " classes in JSON API packages" );
116+ return Collections .unmodifiableSet (classes );
117+ }
118+
119+ /// Scans a directory for class files
120+ static void scanDirectory (java .io .File directory , String packageName , Set <Class <?>> classes ) {
121+ if (!directory .exists () || !directory .isDirectory ()) {
122+ return ;
123+ }
124+
125+ final var files = directory .listFiles ();
126+ if (files == null ) {
127+ return ;
128+ }
129+
130+ for (final var file : files ) {
131+ if (file .isDirectory ()) {
132+ scanDirectory (file , packageName + "." + file .getName (), classes );
133+ } else if (file .getName ().endsWith (".class" ) && !file .getName ().contains ("$" )) {
134+ final var className = packageName + '.' +
135+ file .getName ().substring (0 , file .getName ().length () - 6 );
110136 try {
111137 final var clazz = Class .forName (className );
112138 classes .add (clazz );
113- LOGGER .finer (() -> "Loaded class: " + className );
114- } catch (ClassNotFoundException e ) {
115- LOGGER .fine (() -> "Class not found (might not exist yet) : " + className );
139+ LOGGER .fine (() -> "Found class: " + className );
140+ } catch (ClassNotFoundException | NoClassDefFoundError e ) {
141+ LOGGER .fine (() -> "Could not load class : " + className );
116142 }
117- });
118-
119- LOGGER .fine (() -> "Discovered " + classes .size () + " classes" );
120- return Collections .unmodifiableSet (classes );
143+ }
144+ }
145+ }
146+
147+ /// Scans a JAR file for classes in the specified package
148+ static void scanJar (java .net .URL jarUrl , String packageName , Set <Class <?>> classes ) {
149+ try {
150+ final var jarPath = jarUrl .getPath ();
151+ final var exclamation = jarPath .indexOf ('!' );
152+ if (exclamation < 0 ) {
153+ return ;
154+ }
155+
156+ final var jarFilePath = jarPath .substring (5 , exclamation ); // Remove "file:"
157+ final var packagePath = packageName .replace ('.' , '/' );
158+
159+ try (final var jarFile = new java .util .jar .JarFile (jarFilePath )) {
160+ final var entries = jarFile .entries ();
161+
162+ while (entries .hasMoreElements ()) {
163+ final var entry = entries .nextElement ();
164+ final var entryName = entry .getName ();
165+
166+ if (entryName .startsWith (packagePath ) &&
167+ entryName .endsWith (".class" ) &&
168+ !entryName .contains ("$" )) {
169+
170+ final var className = entryName
171+ .substring (0 , entryName .length () - 6 )
172+ .replace ('/' , '.' );
173+
174+ try {
175+ final var clazz = Class .forName (className );
176+ classes .add (clazz );
177+ LOGGER .fine (() -> "Found class in JAR: " + className );
178+ } catch (ClassNotFoundException | NoClassDefFoundError e ) {
179+ LOGGER .fine (() -> "Could not load class from JAR: " + className );
180+ }
181+ }
182+ }
183+ }
184+ } catch (Exception e ) {
185+ LOGGER .log (Level .WARNING , "Error scanning JAR: " + jarUrl , e );
186+ }
121187 }
122188
123189 /// Fetches upstream source files from GitHub for the given local classes
124190 /// @param localClasses set of local classes to fetch upstream sources for
125191 /// @return map of className to source code (or error message if fetch failed)
126192 static Map <String , String > fetchUpstreamSources (Set <Class <?>> localClasses ) {
127193 Objects .requireNonNull (localClasses , "localClasses must not be null" );
128- LOGGER .fine (() -> "Fetching upstream sources for " + localClasses .size () + " classes" );
194+ LOGGER .info ( "Fetching upstream sources for " + localClasses .size () + " classes" );
129195
130196 final var results = new LinkedHashMap <String , String >();
131197 final var httpClient = HttpClient .newBuilder ()
@@ -197,7 +263,7 @@ static String mapToUpstreamPath(String className) {
197263 /// @return JSON representation of the class's public API
198264 static JsonObject extractLocalApi (Class <?> clazz ) {
199265 Objects .requireNonNull (clazz , "clazz must not be null" );
200- LOGGER .fine (() -> "Extracting local API for: " + clazz .getName ());
266+ LOGGER .info ( "Extracting local API for: " + clazz .getName ());
201267
202268 final var apiMap = new LinkedHashMap <String , JsonValue >();
203269
@@ -350,7 +416,7 @@ static JsonObject extractUpstreamApi(String sourceCode, String className) {
350416 return JsonObject .of (errorMap );
351417 }
352418
353- LOGGER .fine (() -> "Extracting upstream API for: " + className );
419+ LOGGER .info ( "Extracting upstream API for: " + className );
354420
355421 final var compiler = ToolProvider .getSystemJavaCompiler ();
356422 if (compiler == null ) {
@@ -927,7 +993,7 @@ static JsonObject runFullComparison() {
927993
928994 // Discover local classes
929995 final var localClasses = discoverLocalJsonClasses ();
930- LOGGER .info (() -> "Found " + localClasses .size () + " local classes" );
996+ LOGGER .info ("Found " + localClasses .size () + " local classes" );
931997
932998 // Fetch upstream sources
933999 final var upstreamSources = fetchUpstreamSources (localClasses );
@@ -969,7 +1035,7 @@ static JsonObject runFullComparison() {
9691035 final var duration = Duration .between (startTime , Instant .now ());
9701036 reportMap .put ("durationMs" , JsonNumber .of (duration .toMillis ()));
9711037
972- LOGGER .info (() -> "Comparison completed in " + duration .toMillis () + "ms" );
1038+ LOGGER .info ("Comparison completed in " + duration .toMillis () + "ms" );
9731039
9741040 return JsonObject .of (reportMap );
9751041 }
0 commit comments