jdk/make/src/classes/build/tools/docs/GenDocsBundlePage.java
changeset 45259 e09145bcfcc5
parent 45139 7be55dfa1742
equal deleted inserted replaced
45258:a72369e2e758 45259:e09145bcfcc5
    29 import java.io.BufferedWriter;
    29 import java.io.BufferedWriter;
    30 import java.io.IOException;
    30 import java.io.IOException;
    31 import java.io.InputStream;
    31 import java.io.InputStream;
    32 import java.io.InputStreamReader;
    32 import java.io.InputStreamReader;
    33 import java.io.PrintWriter;
    33 import java.io.PrintWriter;
       
    34 import java.lang.module.ModuleDescriptor;
    34 import java.lang.module.ModuleFinder;
    35 import java.lang.module.ModuleFinder;
       
    36 import java.lang.module.ModuleReference;
    35 import java.nio.charset.StandardCharsets;
    37 import java.nio.charset.StandardCharsets;
    36 import java.nio.file.Files;
    38 import java.nio.file.Files;
    37 import java.nio.file.Path;
    39 import java.nio.file.Path;
    38 import java.nio.file.Paths;
    40 import java.nio.file.Paths;
       
    41 import java.util.Arrays;
       
    42 import java.util.Comparator;
    39 import java.util.HashMap;
    43 import java.util.HashMap;
    40 import java.util.Locale;
    44 import java.util.Locale;
    41 import java.util.Map;
    45 import java.util.Map;
    42 import java.util.Properties;
    46 import java.util.Properties;
    43 import java.util.Set;
    47 import java.util.Set;
    44 import java.util.stream.Collectors;
    48 import java.util.function.Predicate;
    45 import java.util.stream.Stream;
    49 import java.util.stream.Stream;
       
    50 import static java.util.stream.Collectors.*;
    46 
    51 
    47 /**
    52 /**
    48  * Build tool to generate the docs bundle index page.
    53  * Build tool to generate the docs bundle index page.
    49  */
    54  */
    50 public class GenDocsBundlePage {
    55 public class GenDocsBundlePage {
   102             return GenDocsBundlePage.class.getResourceAsStream(DOCS_BUNDLE_PAGE);
   107             return GenDocsBundlePage.class.getResourceAsStream(DOCS_BUNDLE_PAGE);
   103         }
   108         }
   104     }
   109     }
   105 
   110 
   106     private static final String HEADER_TITLE = "@HEADER_TITLE@";
   111     private static final String HEADER_TITLE = "@HEADER_TITLE@";
       
   112 
       
   113 
   107     final Path outputfile;
   114     final Path outputfile;
   108     final String title;
   115     final String title;
   109     final Map<String, String> moduleGroups;
   116     final Map<String, Set<ModuleDescriptor>> moduleGroups = new HashMap<>();
   110 
       
   111     GenDocsBundlePage(String title, Path outputfile) throws IOException
   117     GenDocsBundlePage(String title, Path outputfile) throws IOException
   112     {
   118     {
   113         this.outputfile = outputfile;
   119         this.outputfile = outputfile;
   114         this.title = title;
   120         this.title = title;
   115         this.moduleGroups = moduleGroups();
   121 
   116     }
   122         // read module groups
   117 
       
   118     static Map<String, String> moduleGroups() throws IOException {
       
   119         ModuleFinder finder = ModuleFinder.ofSystem();
   123         ModuleFinder finder = ModuleFinder.ofSystem();
   120         Map<String, String> groups = new HashMap<>();
       
   121         try (InputStream in = GenDocsBundlePage.class.getResourceAsStream(MODULE_GROUPS_PROPS)) {
   124         try (InputStream in = GenDocsBundlePage.class.getResourceAsStream(MODULE_GROUPS_PROPS)) {
   122             Properties props = new Properties();
   125             Properties props = new Properties();
   123             props.load(in);
   126             props.load(in);
   124             for (String key: props.stringPropertyNames()) {
   127             for (String key: props.stringPropertyNames()) {
   125                 Set<String> mods = Stream.of(props.getProperty(key).split("\\s+"))
   128                 Set<ModuleDescriptor> mods =
   126                                          .filter(mn -> finder.find(mn).isPresent())
   129                     Stream.of(props.getProperty(key).split("\\s+"))
   127                                          .map(String::trim)
   130                           .map(String::trim)
   128                                          .collect(Collectors.toSet());
   131                           .flatMap(mn -> finder.find(mn).stream())
   129 
   132                           .map(ModuleReference::descriptor)
   130                 // divide into 3 columns: Java SE, JDK, JavaFX
   133                           .collect(toSet());
   131                 StringBuilder sb = new StringBuilder();
   134 
   132                 sb.append(mods.stream()
       
   133                               .filter(mn -> mn.startsWith("java."))
       
   134                               .sorted()
       
   135                               .map(GenDocsBundlePage::toHRef)
       
   136                               .collect(Collectors.joining("\n")));
       
   137                 sb.append("</td>\n<td>")
       
   138                   .append(mods.stream()
       
   139                               .filter(mn -> mn.startsWith("jdk."))
       
   140                               .sorted()
       
   141                               .map(GenDocsBundlePage::toHRef)
       
   142                               .collect(Collectors.joining("\n")));
       
   143                 sb.append("</td>\n<td>");
       
   144                 if (mods.stream().anyMatch(mn -> mn.startsWith("javafx."))) {
       
   145                     sb.append(mods.stream()
       
   146                                   .filter(mn -> mn.startsWith("javafx."))
       
   147                                   .sorted()
       
   148                                   .map(GenDocsBundlePage::toHRef)
       
   149                                   .collect(Collectors.joining("\n")));
       
   150                 }
       
   151                 String name = "@" + key.toUpperCase(Locale.ENGLISH) + "@";
   135                 String name = "@" + key.toUpperCase(Locale.ENGLISH) + "@";
   152                 groups.put(name, sb.toString());
   136                 moduleGroups.put(name, mods);
   153             }
   137             };
   154         }
   138         }
   155         return groups;
       
   156     }
       
   157 
       
   158     static String toHRef(String mn) {
       
   159         return String.format("<a href=\"api/%s-summary.html\">%s</a><br>", mn, mn);
       
   160     }
   139     }
   161 
   140 
   162     void run(BufferedReader reader) throws IOException {
   141     void run(BufferedReader reader) throws IOException {
   163         if (Files.notExists(outputfile.getParent())) {
   142         if (Files.notExists(outputfile.getParent())) {
   164             Files.createDirectories(outputfile.getParent());
   143             Files.createDirectories(outputfile.getParent());
   172 
   151 
   173     String genOutputLine(String line) {
   152     String genOutputLine(String line) {
   174         if (line.contains(HEADER_TITLE)) {
   153         if (line.contains(HEADER_TITLE)) {
   175             line = line.replace(HEADER_TITLE, title);
   154             line = line.replace(HEADER_TITLE, title);
   176         }
   155         }
   177         if (line.contains("@")) {
   156         int i = line.indexOf('@');
   178             for (Map.Entry<String,String> e: moduleGroups.entrySet()) {
   157         int j = line.indexOf('@', i+1);
   179                 if (line.contains(e.getKey())) {
   158         if (i >= 0 && i < j) {
   180                     line = line.replace(e.getKey(), e.getValue());
   159             String name = line.substring(i, j+1);
       
   160             if (moduleGroups.containsKey(name)) {
       
   161                 line = line.replace(name, formatModuleGroup(name));
       
   162             }
       
   163         }
       
   164         return line;
       
   165     }
       
   166 
       
   167     String toHRef(ModuleDescriptor md) {
       
   168         String mn = md.name();
       
   169         String formattedName;
       
   170         if (hasExportedAPIs(md)) {
       
   171             // has exported APIs
       
   172             formattedName = mn;
       
   173         } else if (!md.provides().isEmpty()) {
       
   174             // a provider
       
   175             formattedName = "<i>" + mn + "</i>";
       
   176         } else {
       
   177             // a tool
       
   178             formattedName = "<i>" + mn + "</i>";
       
   179         }
       
   180         return String.format("<a href=\"api/%s-summary.html\">%s</a>",
       
   181                              mn, formattedName);
       
   182     }
       
   183 
       
   184     String formatModuleGroup(String groupName) {
       
   185         StringBuilder sb = new StringBuilder();
       
   186         // organize in Java SE, JDK, JavaFX, JCP groups
       
   187         Set<ModuleDescriptor> modules = moduleGroups.get(groupName);
       
   188         Arrays.stream(ModuleGroup.values())
       
   189             .forEach(g -> {
       
   190                 Set<ModuleDescriptor> mods = modules.stream()
       
   191                     .filter(md -> g.predicate.test(md.name()))
       
   192                     .collect(toSet());
       
   193                 if (!mods.isEmpty()) {
       
   194                     sb.append("<div class=" + g.cssClass + ">\n");
       
   195                     // modules with exported API
       
   196                     mods.stream()
       
   197                         .filter(this::hasExportedAPIs)
       
   198                         .sorted(Comparator.comparing(ModuleDescriptor::name))
       
   199                         .map(this::toHRef)
       
   200                         .forEach(m -> sb.append(m).append("\n"));
       
   201 
       
   202                     // tools and providers
       
   203                     mods.stream()
       
   204                         .filter(md -> !hasExportedAPIs(md))
       
   205                         .sorted(Comparator.comparing(ModuleDescriptor::name))
       
   206                         .map(this::toHRef)
       
   207                         .forEach(m -> sb.append(m).append("\n"));
       
   208                     sb.append("</div>");
   181                 }
   209                 }
   182             }
   210             });
   183         }
   211         return sb.toString();
   184         return line;
   212     }
       
   213 
       
   214     private boolean hasExportedAPIs(ModuleDescriptor md) {
       
   215         if (md.exports().stream().anyMatch(e -> !e.isQualified())) {
       
   216             return true;
       
   217         }
       
   218         // this should check if any indirect exports
       
   219         // checking requires transitive would be sufficient for JDK modules
       
   220         if (md.requires().stream()
       
   221               .map(ModuleDescriptor.Requires::modifiers)
       
   222               .anyMatch(mods -> mods.contains(ModuleDescriptor.Requires.Modifier.TRANSITIVE))) {
       
   223             return true;
       
   224         }
       
   225         return false;
       
   226     }
       
   227 
       
   228     private static final Set<String> NON_JAVA_SE_MODULES =
       
   229         Set.of("java.jnlp", "java.smartcardio");
       
   230 
       
   231     /**
       
   232      * CSS class names are defined in docs-bundle-page.html
       
   233      */
       
   234     enum ModuleGroup {
       
   235         JAVA_SE("javase", mn -> mn.startsWith("java.") && !NON_JAVA_SE_MODULES.contains(mn)),
       
   236         JDK("jdk", mn -> mn.startsWith("jdk.")),
       
   237         JAVAFX("javafx", mn -> mn.startsWith("javafx.")),
       
   238         NON_JAVA_SE("jcp", NON_JAVA_SE_MODULES::contains);
       
   239 
       
   240         final String cssClass;
       
   241         final Predicate<String> predicate;
       
   242         ModuleGroup(String cssClass, Predicate<String> predicate) {
       
   243             this.cssClass = cssClass;
       
   244             this.predicate = predicate;
       
   245         }
   185     }
   246     }
   186 }
   247 }