8154817: Fix the click-through navigation for modules
authorbpatel
Tue, 02 Aug 2016 13:14:12 -0700
changeset 40229 09f4478d07e5
parent 39922 e613affb88d1
child 40230 3e8975c92291
8154817: Fix the click-through navigation for modules Reviewed-by: jjg, ksrini
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleFrameWriter.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexFrameWriter.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlAttr.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocWriter.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/script.js
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java
langtools/test/jdk/javadoc/doclet/testModules/TestModules.java
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java	Wed Jul 05 22:01:28 2017 +0200
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java	Tue Aug 02 13:14:12 2016 -0700
@@ -275,6 +275,7 @@
             int i = 0;
             for (ModuleElement mdle : mdles) {
                 ModulePackageIndexFrameWriter.generate(configuration, mdle);
+                ModuleFrameWriter.generate(configuration, mdle);
                 nextModule = (i + 1 < mdles.size()) ? mdles.get(i + 1) : null;
                 AbstractBuilder moduleSummaryBuilder =
                         configuration.getBuilderFactory().getModuleSummaryBuilder(
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleFrameWriter.java	Tue Aug 02 13:14:12 2016 -0700
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.javadoc.internal.doclets.formats.html;
+
+import java.io.*;
+import java.util.*;
+
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementFilter;
+
+import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants;
+import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
+import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
+import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
+import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
+import jdk.javadoc.internal.doclets.toolkit.Content;
+import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
+import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException;
+
+
+/**
+ * Class to generate file for each module contents in the left-hand bottom
+ * frame. This will list all the Class Kinds in the module. A click on any
+ * class-kind will update the right-hand frame with the clicked class-kind page.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ *
+ * @author Bhavesh Patel
+ */
+public class ModuleFrameWriter extends HtmlDocletWriter {
+
+    /**
+     * The module being documented.
+     */
+    private ModuleElement mdle;
+
+    /**
+     * The classes to be documented.  Use this to filter out classes
+     * that will not be documented.
+     */
+    private SortedSet<TypeElement> documentedClasses;
+
+    /**
+     * Constructor to construct ModuleFrameWriter object and to generate
+     * "module_name-type-frame.html" file. For example for module "java.base" this will generate file
+     * "java.base-type-frame.html" file.
+     *
+     * @param configuration the configuration of the doclet.
+     * @param moduleElement moduleElement under consideration.
+     */
+    public ModuleFrameWriter(ConfigurationImpl configuration, ModuleElement moduleElement)
+            throws IOException {
+        super(configuration, DocPaths.moduleTypeFrame(moduleElement));
+        this.mdle = moduleElement;
+        if (utils.getSpecifiedPackages().isEmpty()) {
+            documentedClasses = new TreeSet<>(utils.makeGeneralPurposeComparator());
+            documentedClasses.addAll(configuration.docEnv.getIncludedClasses());
+        }
+    }
+
+    /**
+     * Generate a module type summary page for the left-hand bottom frame.
+     *
+     * @param configuration the current configuration of the doclet.
+     * @param moduleElement The package for which "module_name-type-frame.html" is to be generated.
+     */
+    public static void generate(ConfigurationImpl configuration, ModuleElement moduleElement) {
+        ModuleFrameWriter mdlgen;
+        try {
+            mdlgen = new ModuleFrameWriter(configuration, moduleElement);
+            String mdlName = moduleElement.getQualifiedName().toString();
+            Content mdlLabel = new StringContent(mdlName);
+            HtmlTree body = mdlgen.getBody(false, mdlgen.getWindowTitle(mdlName));
+            HtmlTree htmlTree = (configuration.allowTag(HtmlTag.MAIN))
+                    ? HtmlTree.MAIN()
+                    : body;
+            Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, HtmlStyle.bar,
+                    mdlgen.getHyperLink(DocPaths.moduleSummary(moduleElement), mdlLabel, "", "classFrame"));
+            htmlTree.addContent(heading);
+            HtmlTree div = new HtmlTree(HtmlTag.DIV);
+            div.addStyle(HtmlStyle.indexContainer);
+            mdlgen.addClassListing(div);
+            htmlTree.addContent(div);
+            if (configuration.allowTag(HtmlTag.MAIN)) {
+                body.addContent(htmlTree);
+            }
+            mdlgen.printHtmlDocument(
+                    configuration.metakeywords.getMetaKeywordsForModule(moduleElement), false, body);
+            mdlgen.close();
+        } catch (IOException exc) {
+            configuration.standardmessage.error(
+                    "doclet.exception_encountered",
+                    exc.toString(), DocPaths.moduleTypeFrame(moduleElement).getPath());
+            throw new DocletAbortException(exc);
+        }
+    }
+
+    /**
+     * Add class listing for all the classes in this module. Divide class
+     * listing as per the class kind and generate separate listing for
+     * Classes, Interfaces, Exceptions and Errors.
+     *
+     * @param contentTree the content tree to which the listing will be added
+     */
+    protected void addClassListing(HtmlTree contentTree) {
+        List<PackageElement> packagesIn = ElementFilter.packagesIn(mdle.getEnclosedElements());
+        SortedSet<TypeElement> interfaces = new TreeSet<>(utils.makeGeneralPurposeComparator());
+        SortedSet<TypeElement> classes = new TreeSet<>(utils.makeGeneralPurposeComparator());
+        SortedSet<TypeElement> enums = new TreeSet<>(utils.makeGeneralPurposeComparator());
+        SortedSet<TypeElement> exceptions = new TreeSet<>(utils.makeGeneralPurposeComparator());
+        SortedSet<TypeElement> errors = new TreeSet<>(utils.makeGeneralPurposeComparator());
+        SortedSet<TypeElement> annotationTypes = new TreeSet<>(utils.makeGeneralPurposeComparator());
+        for (PackageElement pkg : packagesIn) {
+            if (utils.isIncluded(pkg)) {
+                interfaces.addAll(utils.getInterfaces(pkg));
+                classes.addAll(utils.getOrdinaryClasses(pkg));
+                enums.addAll(utils.getEnums(pkg));
+                exceptions.addAll(utils.getExceptions(pkg));
+                errors.addAll(utils.getErrors(pkg));
+                annotationTypes.addAll(utils.getAnnotationTypes(pkg));
+            }
+        }
+        addClassKindListing(interfaces, getResource("doclet.Interfaces"), contentTree);
+        addClassKindListing(classes, getResource("doclet.Classes"), contentTree);
+        addClassKindListing(enums, getResource("doclet.Enums"), contentTree);
+        addClassKindListing(exceptions, getResource("doclet.Exceptions"), contentTree);
+        addClassKindListing(errors, getResource("doclet.Errors"), contentTree);
+        addClassKindListing(annotationTypes, getResource("doclet.AnnotationTypes"), contentTree);
+    }
+
+    /**
+     * Add specific class kind listing. Also add label to the listing.
+     *
+     * @param list Iterable list of TypeElements
+     * @param labelContent content tree of the label to be added
+     * @param contentTree the content tree to which the class kind listing will be added
+     */
+    protected void addClassKindListing(Iterable<TypeElement> list, Content labelContent,
+            HtmlTree contentTree) {
+        SortedSet<TypeElement> tset = utils.filterOutPrivateClasses(list, configuration.javafx);
+        if (!tset.isEmpty()) {
+            boolean printedHeader = false;
+            HtmlTree htmlTree = (configuration.allowTag(HtmlTag.SECTION))
+                    ? HtmlTree.SECTION()
+                    : contentTree;
+            HtmlTree ul = new HtmlTree(HtmlTag.UL);
+            ul.setTitle(labelContent);
+            for (TypeElement typeElement : tset) {
+                if (documentedClasses != null && !documentedClasses.contains(typeElement)) {
+                    continue;
+                }
+                if (!utils.isCoreClass(typeElement) || !configuration.isGeneratedDoc(typeElement)) {
+                    continue;
+                }
+                if (!printedHeader) {
+                    Content heading = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING,
+                            true, labelContent);
+                    htmlTree.addContent(heading);
+                    printedHeader = true;
+                }
+                Content arr_i_name = new StringContent(utils.getSimpleName(typeElement));
+                if (utils.isInterface(typeElement)) {
+                    arr_i_name = HtmlTree.SPAN(HtmlStyle.interfaceName, arr_i_name);
+                }
+                Content link = getLink(new LinkInfoImpl(configuration,
+                        LinkInfoImpl.Kind.ALL_CLASSES_FRAME, typeElement).label(arr_i_name).target("classFrame"));
+                Content li = HtmlTree.LI(link);
+                ul.addContent(li);
+            }
+            htmlTree.addContent(ul);
+            if (configuration.allowTag(HtmlTag.SECTION)) {
+                contentTree.addContent(htmlTree);
+            }
+        }
+    }
+}
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexFrameWriter.java	Wed Jul 05 22:01:28 2017 +0200
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexFrameWriter.java	Tue Aug 02 13:14:12 2016 -0700
@@ -110,15 +110,13 @@
     /**
      * Returns each module name as a separate link.
      *
-     * @param moduleName the module being documented
+     * @param mdle the module being documented
      * @return content for the module link
      */
     protected Content getModuleLink(ModuleElement mdle) {
         Content moduleLinkContent;
-        Content moduleLabel;
-        moduleLabel = new StringContent(mdle.getQualifiedName().toString());
-        moduleLinkContent = getHyperLink(DocPaths.moduleFrame(mdle),
-                moduleLabel, "", "packageListFrame");
+        Content mdlLabel = new StringContent(mdle.getQualifiedName());
+        moduleLinkContent = getModuleFramesHyperLink(mdle, mdlLabel, "packageListFrame");
         Content li = HtmlTree.LI(moduleLinkContent);
         return li;
     }
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlAttr.java	Wed Jul 05 22:01:28 2017 +0200
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlAttr.java	Tue Aug 02 13:14:12 2016 -0700
@@ -49,6 +49,7 @@
     ID,
     LANG,
     NAME,
+    ONCLICK,
     ONLOAD,
     REL,
     ROLE,
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocWriter.java	Wed Jul 05 22:01:28 2017 +0200
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocWriter.java	Tue Aug 02 13:14:12 2016 -0700
@@ -28,6 +28,7 @@
 import java.io.*;
 import java.util.*;
 
+import javax.lang.model.element.ModuleElement;
 import javax.lang.model.element.PackageElement;
 import javax.lang.model.element.TypeElement;
 
@@ -282,6 +283,21 @@
         return anchor;
     }
 
+    public Content getModuleFramesHyperLink(ModuleElement mdle, Content label, String target) {
+        DocLink mdlLink = new DocLink(DocPaths.moduleFrame(mdle));
+        DocLink mtFrameLink = new DocLink(DocPaths.moduleTypeFrame(mdle));
+        DocLink cFrameLink = new DocLink(DocPaths.moduleSummary(mdle));
+        HtmlTree anchor = HtmlTree.A(mdlLink.toString(), label);
+        StringBuilder onclickStr = new StringBuilder("updateModuleFrame('")
+                .append(mtFrameLink.toString())
+                .append("','")
+                .append(cFrameLink.toString())
+                .append("');");
+        anchor.addAttr(HtmlAttr.TARGET, target);
+        anchor.addAttr(HtmlAttr.ONCLICK, onclickStr.toString());
+        return anchor;
+    }
+
     /**
      * Get the enclosed name of the package
      *
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/script.js	Wed Jul 05 22:01:28 2017 +0200
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/script.js	Tue Aug 02 13:14:12 2016 -0700
@@ -107,3 +107,9 @@
         }
     }
 }
+
+function updateModuleFrame(pFrame, cFrame)
+{
+    top.packageFrame.location = pFrame;
+    top.classFrame.location = cFrame;
+}
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java	Wed Jul 05 22:01:28 2017 +0200
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java	Tue Aug 02 13:14:12 2016 -0700
@@ -155,6 +155,11 @@
         return DocPath.create(mdle.getQualifiedName() + "-summary.html");
     }
 
+    /** The name of the file for the module frame. */
+    public static DocPath moduleTypeFrame(ModuleElement mdle) {
+        return DocPath.create(mdle.getQualifiedName() + "-type-frame.html");
+    }
+
     /** The name of the file for the module overview frame. */
     public static final DocPath MODULE_OVERVIEW_FRAME = DocPath.create("module-overview-frame.html");
 
--- a/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java	Wed Jul 05 22:01:28 2017 +0200
+++ b/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java	Tue Aug 02 13:14:12 2016 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8154119 8154262 8156077 8157987 8154261
+ * @bug 8154119 8154262 8156077 8157987 8154261 8154817
  * @summary Test modules support in javadoc.
  * @author bpatel
  * @library ../lib
@@ -50,6 +50,8 @@
         testNoDescription(false);
         testOverviewSummaryModules();
         testModuleLink();
+        testModuleClickThroughLinks();
+        testModuleClickThrough(true);
     }
 
     @Test
@@ -63,6 +65,8 @@
         testHtml5NoDescription(false);
         testHtml5OverviewSummaryModules();
         testModuleLink();
+        testModuleClickThroughLinks();
+        testModuleClickThrough(true);
     }
 
     @Test
@@ -96,6 +100,7 @@
                 "testpkgnomodule", "testpkgnomodule1");
         checkExit(Exit.OK);
         testOverviewSummaryPackages();
+        testModuleClickThrough(false);
     }
 
    @Test
@@ -442,4 +447,27 @@
                 + "<!--   -->\n"
                 + "</a>");
     }
+
+     void testModuleClickThroughLinks() {
+        checkOutput("module-overview-frame.html", true,
+                "<li><a href=\"module1-frame.html\" target=\"packageListFrame\" "
+                + "onclick=\"updateModuleFrame('module1-type-frame.html','module1-summary.html');"
+                + "\">module1</a></li>");
+        checkOutput("module-overview-frame.html", true,
+                "<li><a href=\"module2-frame.html\" target=\"packageListFrame\" "
+                + "onclick=\"updateModuleFrame('module2-type-frame.html','module2-summary.html');"
+                + "\">module2</a></li>");
+        checkOutput("script.js", true,
+                 "function updateModuleFrame(pFrame, cFrame)\n"
+                 + "{\n"
+                 + "    top.packageFrame.location = pFrame;\n"
+                 + "    top.classFrame.location = cFrame;\n"
+                 + "}");
 }
+
+     void testModuleClickThrough(boolean found) {
+        checkFiles(found,
+                "module1-type-frame.html",
+                "module2-type-frame.html");
+     }
+}