jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
changeset 39050 9de41b79ec7e
parent 37779 7c84df693837
child 39646 2bf99fe5023b
equal deleted inserted replaced
39049:1108f7f41940 39050:9de41b79ec7e
    53 import java.util.regex.Pattern;
    53 import java.util.regex.Pattern;
    54 import java.util.stream.Collectors;
    54 import java.util.stream.Collectors;
    55 import java.util.zip.ZipEntry;
    55 import java.util.zip.ZipEntry;
    56 import java.util.zip.ZipFile;
    56 import java.util.zip.ZipFile;
    57 
    57 
    58 import jdk.internal.module.Checks;
       
    59 import jdk.internal.module.ConfigurableModuleFinder;
    58 import jdk.internal.module.ConfigurableModuleFinder;
    60 import jdk.internal.perf.PerfCounter;
    59 import jdk.internal.perf.PerfCounter;
    61 
    60 
    62 
    61 
    63 /**
    62 /**
   341         int index = cf.lastIndexOf("/") + 1;
   340         int index = cf.lastIndexOf("/") + 1;
   342         if (index < cf.length()) {
   341         if (index < cf.length()) {
   343             String prefix = cf.substring(0, index);
   342             String prefix = cf.substring(0, index);
   344             if (prefix.equals(SERVICES_PREFIX)) {
   343             if (prefix.equals(SERVICES_PREFIX)) {
   345                 String sn = cf.substring(index);
   344                 String sn = cf.substring(index);
   346                 if (Checks.isJavaIdentifier(sn))
   345                 return Optional.of(sn);
   347                     return Optional.of(sn);
       
   348             }
   346             }
   349         }
   347         }
   350         return Optional.empty();
   348         return Optional.empty();
   351     }
   349     }
   352 
   350 
   376      * 3. It has no module-private/concealed packages
   374      * 3. It has no module-private/concealed packages
   377      * 4. The contents of any META-INF/services configuration files are mapped
   375      * 4. The contents of any META-INF/services configuration files are mapped
   378      *    to "provides" declarations
   376      *    to "provides" declarations
   379      * 5. The Main-Class attribute in the main attributes of the JAR manifest
   377      * 5. The Main-Class attribute in the main attributes of the JAR manifest
   380      *    is mapped to the module descriptor mainClass
   378      *    is mapped to the module descriptor mainClass
   381      *
       
   382      * @apiNote This needs to move to somewhere where it can be used by tools,
       
   383      * maybe even a standard API if automatic modules are a Java SE feature.
       
   384      */
   379      */
   385     private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
   380     private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
   386         throws IOException
   381         throws IOException
   387     {
   382     {
   388         // Derive module name and version from JAR file name
   383         // Derive module name and version from JAR file name
   395         // drop .jar
   390         // drop .jar
   396         String mn = fn.substring(0, fn.length()-4);
   391         String mn = fn.substring(0, fn.length()-4);
   397         String vs = null;
   392         String vs = null;
   398 
   393 
   399         // find first occurrence of -${NUMBER}. or -${NUMBER}$
   394         // find first occurrence of -${NUMBER}. or -${NUMBER}$
   400         Matcher matcher = Pattern.compile("-(\\d+(\\.|$))").matcher(mn);
   395         Matcher matcher = Patterns.DASH_VERSION.matcher(mn);
   401         if (matcher.find()) {
   396         if (matcher.find()) {
   402             int start = matcher.start();
   397             int start = matcher.start();
   403 
   398 
   404             // attempt to parse the tail as a version string
   399             // attempt to parse the tail as a version string
   405             try {
   400             try {
   410 
   405 
   411             mn = mn.substring(0, start);
   406             mn = mn.substring(0, start);
   412         }
   407         }
   413 
   408 
   414         // finally clean up the module name
   409         // finally clean up the module name
   415         mn =  mn.replaceAll("[^A-Za-z0-9]", ".")  // replace non-alphanumeric
   410         mn = cleanModuleName(mn);
   416                 .replaceAll("(\\.)(\\1)+", ".")   // collapse repeating dots
       
   417                 .replaceAll("^\\.", "")           // drop leading dots
       
   418                 .replaceAll("\\.$", "");          // drop trailing dots
       
   419 
       
   420 
   411 
   421         // Builder throws IAE if module name is empty or invalid
   412         // Builder throws IAE if module name is empty or invalid
   422         ModuleDescriptor.Builder builder
   413         ModuleDescriptor.Builder builder
   423             = new ModuleDescriptor.Builder(mn, true)
   414             = new ModuleDescriptor.Builder(mn)
   424                 .requires(Requires.Modifier.MANDATED, "java.base");
   415                 .automatic()
       
   416                 .requires(Set.of(Requires.Modifier.MANDATED), "java.base");
   425         if (vs != null)
   417         if (vs != null)
   426             builder.version(vs);
   418             builder.version(vs);
   427 
   419 
   428         // scan the entries in the JAR file to locate the .class and service
   420         // scan the entries in the JAR file to locate the .class and service
   429         // configuration file
   421         // configuration file
   455             try (InputStream in = jf.getInputStream(entry)) {
   447             try (InputStream in = jf.getInputStream(entry)) {
   456                 BufferedReader reader
   448                 BufferedReader reader
   457                     = new BufferedReader(new InputStreamReader(in, "UTF-8"));
   449                     = new BufferedReader(new InputStreamReader(in, "UTF-8"));
   458                 String cn;
   450                 String cn;
   459                 while ((cn = nextLine(reader)) != null) {
   451                 while ((cn = nextLine(reader)) != null) {
   460                     if (Checks.isJavaIdentifier(cn)) {
   452                     if (cn.length() > 0) {
   461                         providerClasses.add(cn);
   453                         providerClasses.add(cn);
   462                     }
   454                     }
   463                 }
   455                 }
   464             }
   456             }
   465             if (!providerClasses.isEmpty())
   457             if (!providerClasses.isEmpty())
   474             if (mainClass != null)
   466             if (mainClass != null)
   475                 builder.mainClass(mainClass);
   467                 builder.mainClass(mainClass);
   476         }
   468         }
   477 
   469 
   478         return builder.build();
   470         return builder.build();
       
   471     }
       
   472 
       
   473     /**
       
   474      * Patterns used to derive the module name from a JAR file name.
       
   475      */
       
   476     private static class Patterns {
       
   477         static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))");
       
   478         static final Pattern NON_ALPHANUM = Pattern.compile("[^A-Za-z0-9]");
       
   479         static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+");
       
   480         static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
       
   481         static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
       
   482     }
       
   483 
       
   484     /**
       
   485      * Clean up candidate module name derived from a JAR file name.
       
   486      */
       
   487     private static String cleanModuleName(String mn) {
       
   488         // replace non-alphanumeric
       
   489         mn = Patterns.NON_ALPHANUM.matcher(mn).replaceAll(".");
       
   490 
       
   491         // collapse repeating dots
       
   492         mn = Patterns.REPEATING_DOTS.matcher(mn).replaceAll(".");
       
   493 
       
   494         // drop leading dots
       
   495         if (mn.length() > 0 && mn.charAt(0) == '.')
       
   496             mn = Patterns.LEADING_DOTS.matcher(mn).replaceAll("");
       
   497 
       
   498         // drop trailing dots
       
   499         int len = mn.length();
       
   500         if (len > 0 && mn.charAt(len-1) == '.')
       
   501             mn = Patterns.TRAILING_DOTS.matcher(mn).replaceAll("");
       
   502 
       
   503         return mn;
   479     }
   504     }
   480 
   505 
   481     private Set<String> jarPackages(JarFile jf) {
   506     private Set<String> jarPackages(JarFile jf) {
   482         return jf.stream()
   507         return jf.stream()
   483             .filter(e -> e.getName().endsWith(".class"))
   508             .filter(e -> e.getName().endsWith(".class"))