@@ -170,89 +170,113 @@ private java.util.Optional<String> extractClassNameFromJar(JarFile jarFile) {
170170 }
171171
172172 /**
173- * look through the jar for the module name with each step commented inline;
174- * the algorithm used here is based on the described in {@link ModuleFinder#of}
173+ * Look through the jar for the module name using a succession of techniques corresponding
174+ * to how the JDK itself determines module names,
175+ * as documented in {@link java.lang.module.ModuleFinder#of}.
175176 */
176177 private String extractModuleNameFromJar (File file , JarFile jarFile ) throws IOException {
177178 String moduleName = null ;
178179
179- // if the jar is multi-release, there will be a set versions
180- // under the path META-INF/versions/<version number>;
181- // each version will have its own module-info.class if this is a modular jar;
182- // look for the module name in the module-info from the latest version
183- // fewer than or equal to the current JVM version
184180 if (jarFile .isMultiRelease ()) {
185- List <Integer > versions = jarFile .stream ()
186- .filter (je -> je .getName ().startsWith (META_INF_VERSIONS_PREFIX ) && je .getName ().endsWith ("/module-info.class" ))
187- .map (
188- je -> Integer .parseInt (
189- je .getName ().substring (META_INF_VERSIONS_PREFIX .length (), je .getName ().length () - META_INF_VERSIONS_PREFIX .length ())
190- )
191- )
192- .toList ();
193- versions = new ArrayList <>(versions );
194- versions .sort (Integer ::compareTo );
195- versions = versions .reversed ();
196- int major = Runtime .version ().feature ();
197- StringBuilder path = new StringBuilder ("META-INF/versions/" );
198- for (int version : versions ) {
199- if (version <= major ) {
200- path .append (version );
201- break ;
202- }
203- }
204- if (path .length () > META_INF_VERSIONS_PREFIX .length ()) {
205- path .append ("/module-info.class" );
206- JarEntry moduleEntry = jarFile .getJarEntry (path .toString ());
207- if (moduleEntry != null ) {
208- try (InputStream inputStream = jarFile .getInputStream (moduleEntry )) {
209- moduleName = extractModuleNameFromModuleInfo (inputStream );
210- }
211- }
181+ StringBuilder dir = versionDirectoryIfExists (jarFile );
182+ if (dir != null ) {
183+ dir .append ("/module-info.class" );
184+ moduleName = getModuleNameFromModuleInfoFile (dir .toString (), jarFile );
212185 }
213186 }
214187
215- // if the jar is *not* multi-release then first look in
216- // module-info.class from the top-level if it exists
217188 if (moduleName == null ) {
218- JarEntry moduleEntry = jarFile .getJarEntry ("module-info.class" );
219- if (moduleEntry != null ) {
220- try (InputStream inputStream = jarFile .getInputStream (moduleEntry )) {
221- moduleName = extractModuleNameFromModuleInfo (inputStream );
222- }
223- }
189+ moduleName = getModuleNameFromModuleInfoFile ("module-info.class" , jarFile );
224190 }
225191
226- // if the jar does *not* contain module-info.class
227- // check the manifest file for the module name
228192 if (moduleName == null ) {
229- JarEntry manifestEntry = jarFile .getJarEntry ("META-INF/MANIFEST.MF" );
230- if (manifestEntry != null ) {
231- try (InputStream inputStream = jarFile .getInputStream (manifestEntry )) {
232- Manifest manifest = new Manifest (inputStream );
233- String amn = manifest .getMainAttributes ().getValue ("Automatic-Module-Name" );
234- if (amn != null ) {
235- moduleName = amn ;
236- }
237- }
238- }
193+ moduleName = getAutomaticModuleNameFromManifest (jarFile );
239194 }
240195
241- // if the jar does not have module-info.class and no module name in the manifest
242- // default to the jar name without .jar and no versioning
243196 if (moduleName == null ) {
244- String jn = file .getName ().substring (0 , file .getName ().length () - JAR_DESCRIPTOR_SUFFIX .length ());
245- Matcher matcher = Pattern .compile ("-(\\ d+(\\ .|$))" ).matcher (jn );
246- if (matcher .find ()) {
247- jn = jn .substring (0 , matcher .start ());
248- }
249- jn = jn .replaceAll ("[^A-Za-z0-9]" , "." );
250- moduleName = jn ;
197+ moduleName = deriveModuleNameFromJarFileName (file );
251198 }
252199
253200 return moduleName ;
254201 }
255202
203+ /**
204+ * if the jar is multi-release, there will be a set versions
205+ * under the path META-INF/versions/<version number>;
206+ * each version will have its own module-info.class if this is a modular jar;
207+ * look for the module name in the module-info from the latest version
208+ * fewer than or equal to the current JVM version
209+ *
210+ * @return a {@link StringBuilder} with the {@code META-INF/versions/<version number>} if it exists; otherwise null
211+ */
212+ private static StringBuilder versionDirectoryIfExists (JarFile jarFile ) {
213+ List <Integer > versions = jarFile .stream ()
214+ .filter (je -> je .getName ().startsWith (META_INF_VERSIONS_PREFIX ) && je .getName ().endsWith ("/module-info.class" ))
215+ .map (
216+ je -> Integer .parseInt (
217+ je .getName ().substring (META_INF_VERSIONS_PREFIX .length (), je .getName ().length () - META_INF_VERSIONS_PREFIX .length ())
218+ )
219+ )
220+ .toList ();
221+ versions = new ArrayList <>(versions );
222+ versions .sort (Integer ::compareTo );
223+ versions = versions .reversed ();
224+ int major = Runtime .version ().feature ();
225+ StringBuilder path = new StringBuilder (META_INF_VERSIONS_PREFIX );
226+ for (int version : versions ) {
227+ if (version <= major ) {
228+ return path .append (version );
229+ }
230+ }
231+ return null ;
232+ }
233+
234+ /**
235+ * Looks into the specified {@code module-info.class} file, if it exists, and extracts the declared name of the module.
236+ * @return the module name, or null if there is no such {@code module-info.class} file.
237+ */
238+ private String getModuleNameFromModuleInfoFile (String moduleInfoFileName , JarFile jarFile ) throws IOException {
239+ JarEntry moduleEntry = jarFile .getJarEntry (moduleInfoFileName );
240+ if (moduleEntry != null ) {
241+ try (InputStream inputStream = jarFile .getInputStream (moduleEntry )) {
242+ return extractModuleNameFromModuleInfo (inputStream );
243+ }
244+ }
245+ return null ;
246+ }
247+
248+ /**
249+ * Looks into the {@code MANIFEST.MF} file and returns the {@code Automatic-Module-Name} value if there is one.
250+ * @return the module name, or null if the manifest is nonexistent or has no {@code Automatic-Module-Name} value
251+ */
252+ private static String getAutomaticModuleNameFromManifest (JarFile jarFile ) throws IOException {
253+ JarEntry manifestEntry = jarFile .getJarEntry ("META-INF/MANIFEST.MF" );
254+ if (manifestEntry != null ) {
255+ try (InputStream inputStream = jarFile .getInputStream (manifestEntry )) {
256+ Manifest manifest = new Manifest (inputStream );
257+ String amn = manifest .getMainAttributes ().getValue ("Automatic-Module-Name" );
258+ if (amn != null ) {
259+ return amn ;
260+ }
261+ }
262+ }
263+ return null ;
264+ }
265+
266+ /**
267+ * Compose a module name from the given {@code jarFile} name,
268+ * as documented in {@link java.lang.module.ModuleFinder#of}.
269+ */
270+ private static @ NotNull String deriveModuleNameFromJarFileName (File jarFile ) {
271+ String jn = jarFile .getName ().substring (0 , jarFile .getName ().length () - JAR_DESCRIPTOR_SUFFIX .length ());
272+ Matcher matcher = Pattern .compile ("-(\\ d+(\\ .|$))" ).matcher (jn );
273+ if (matcher .find ()) {
274+ jn = jn .substring (0 , matcher .start ());
275+ }
276+ jn = jn .replaceAll ("[^A-Za-z0-9]" , "." );
277+ return jn ;
278+ }
279+
256280 /**
257281 * find the first class and module when the class path entry is a directory
258282 */
0 commit comments