8178067: support for @uses/@provides tags is broken
authorksrini
Wed, 12 Apr 2017 11:42:50 -0700
changeset 44684 6ce4d52084e8
parent 44683 610dc2b48954
child 44685 8888c1a60279
8178067: support for @uses/@provides tags is broken Reviewed-by: jjg
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java
langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java
langtools/test/jdk/javadoc/doclet/testModules/TestModuleServices.java
langtools/test/jdk/javadoc/doclet/testModules/TestModules.java
langtools/test/tools/lib/toolbox/ModuleBuilder.java
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java	Tue Apr 11 17:26:52 2017 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java	Wed Apr 12 11:42:50 2017 -0700
@@ -409,7 +409,7 @@
      * @param section set of elements
      * @return true if there are elements to be displayed
      */
-    public boolean display(SortedSet<? extends Element> section) {
+    public boolean display(Set<? extends Element> section) {
         return section != null && !section.isEmpty();
     }
 
@@ -423,6 +423,25 @@
         return section != null && !section.isEmpty();
     }
 
+    /*
+     * Returns true, in API mode, if at least one type element in
+     * the typeElements set is referenced by a javadoc tag in tagsMap.
+     */
+    private boolean displayServices(Set<TypeElement> typeElements,
+                                    Map<TypeElement, Content> tagsMap) {
+        return typeElements != null &&
+                typeElements.stream().anyMatch((v) -> displayServiceDirective(v, tagsMap));
+    }
+
+    /*
+     * Returns true, in API mode, if the type element is referenced
+     * from a javadoc tag in tagsMap.
+     */
+    private boolean displayServiceDirective(TypeElement typeElement,
+                                            Map<TypeElement, Content> tagsMap) {
+        return moduleMode == ModuleMode.ALL || tagsMap.containsKey(typeElement);
+    }
+
     /**
      * Add the summary header.
      *
@@ -768,14 +787,18 @@
      * {@inheritDoc}
      */
     public void addServicesSummary(Content summaryContentTree) {
-        if (display(uses) || display(provides)) {
+
+        boolean haveUses = displayServices(uses, usesTrees);
+        boolean haveProvides = displayServices(provides.keySet(), providesTrees);
+
+        if (haveProvides || haveUses) {
             HtmlTree li = new HtmlTree(HtmlTag.LI);
             li.addStyle(HtmlStyle.blockList);
             addSummaryHeader(HtmlConstants.START_OF_SERVICES_SUMMARY, SectionName.SERVICES,
                     contents.navServices, li);
             String text;
             String tableSummary;
-            if (display(provides)) {
+            if (haveProvides) {
                 text = configuration.getText("doclet.Provides_Summary");
                 tableSummary = configuration.getText("doclet.Member_Table_Summary",
                         configuration.getText("doclet.Provides_Summary"),
@@ -788,7 +811,7 @@
                     li.addContent(table);
                 }
             }
-            if (display(uses)) {
+            if (haveUses){
                 text = configuration.getText("doclet.Uses_Summary");
                 tableSummary = configuration.getText("doclet.Member_Table_Summary",
                         configuration.getText("doclet.Uses_Summary"),
@@ -818,17 +841,13 @@
         HtmlTree tdSummary;
         Content description;
         for (TypeElement t : uses) {
-            // For each uses directive in the module declaration, if we are in the "api" mode and
-            // if there are service types listed using @uses javadoc tag, check if the service type in
-            // the uses directive is specified using the @uses tag. If not, we do not display the
-            // service type in the "api" mode.
-            if (moduleMode == ModuleMode.API && display(usesTrees) && !usesTrees.containsKey(t)) {
+            if (!displayServiceDirective(t, usesTrees)) {
                 continue;
             }
             typeLinkContent = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, t));
             thType = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst, typeLinkContent);
             tdSummary = new HtmlTree(HtmlTag.TD);
-        tdSummary.addStyle(HtmlStyle.colLast);
+            tdSummary.addStyle(HtmlStyle.colLast);
             if (display(usesTrees)) {
                 description = usesTrees.get(t);
                 if (description != null) {
@@ -837,9 +856,9 @@
             }
             addSummaryComment(t, tdSummary);
             HtmlTree tr = HtmlTree.TR(thType);
-        tr.addContent(tdSummary);
-        tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
-        tbody.addContent(tr);
+            tr.addContent(tdSummary);
+            tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
+            tbody.addContent(tr);
             altColor = !altColor;
         }
     }
@@ -851,18 +870,13 @@
      */
     public void addProvidesList(Content tbody) {
         boolean altColor = true;
-        TypeElement srv;
         SortedSet<TypeElement> implSet;
         Content description;
         for (Map.Entry<TypeElement, SortedSet<TypeElement>> entry : provides.entrySet()) {
-            srv = entry.getKey();
-            // For each provides directive in the module declaration, if we are in the "api" mode and
-            // if there are service types listed using @provides javadoc tag, check if the service type in
-            // the provides directive is specified using the @provides tag. If not, we do not display the
-            // service type in the "api" mode.
-            if (moduleMode == ModuleMode.API && display(providesTrees) && !providesTrees.containsKey(srv)) {
+            TypeElement srv = entry.getKey();
+            if (!displayServiceDirective(srv, providesTrees)) {
                 continue;
-    }
+            }
             implSet = entry.getValue();
             Content srvLinkContent = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, srv));
             HtmlTree thType = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst, srvLinkContent);
--- a/langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java	Tue Apr 11 17:26:52 2017 -0700
+++ b/langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java	Wed Apr 12 11:42:50 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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
@@ -46,6 +46,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 
 
 /**
@@ -237,12 +238,23 @@
      * @throws Exception if any errors occurred
      */
     public void runTests() throws Exception {
+        runTests(m -> new Object[0]);
+    }
+
+    /**
+     * Run all methods annotated with @Test, followed by printSummary.
+     * Typically called on a tester object in main()
+     * @param f a function which will be used to provide arguments to each
+     *          invoked method
+     * @throws Exception if any errors occurred
+     */
+    public void runTests(Function<Method, Object[]> f) throws Exception {
         for (Method m: getClass().getDeclaredMethods()) {
             Annotation a = m.getAnnotation(Test.class);
             if (a != null) {
                 try {
                     out.println("Running test " + m.getName());
-                    m.invoke(this, new Object[] { });
+                    m.invoke(this, f.apply(m));
                 } catch (InvocationTargetException e) {
                     Throwable cause = e.getCause();
                     throw (cause instanceof Exception) ? ((Exception) cause) : e;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/javadoc/doclet/testModules/TestModuleServices.java	Wed Apr 12 11:42:50 2017 -0700
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8176901
+ * @summary tests the module's services, such as provides and uses
+ * @modules jdk.javadoc/jdk.javadoc.internal.api
+ *          jdk.javadoc/jdk.javadoc.internal.tool
+ *          jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ * @library ../lib /tools/lib
+ * @build toolbox.ToolBox toolbox.ModuleBuilder JavadocTester
+ * @run main TestModuleServices
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import toolbox.*;
+
+public class TestModuleServices extends JavadocTester {
+
+    public final ToolBox tb;
+    public static void main(String... args) throws Exception {
+        TestModuleServices tester = new TestModuleServices();
+        tester.runTests(m -> new Object[] { Paths.get(m.getName()) });
+    }
+
+    public TestModuleServices() {
+        tb = new ToolBox();
+    }
+
+    @Test
+    public void checkUsesNoApiTagModuleModeDefault(Path base) throws Exception {
+        ModuleBuilder mb = new ModuleBuilder(tb, "m")
+                .comment("module m.\n@provides p1.A abc") // bogus tag
+                .uses("p1.A")
+                .uses("p1.B")
+                .exports("p1")
+                .classes("package p1; public class A {}")
+                .classes("package p1; public class B {}");
+                mb.write(base);
+
+        javadoc("-d", base.toString() + "/out",
+                "-quiet",
+                "--module-source-path", base.toString(),
+                "--module", "m");
+        checkExit(Exit.OK);
+
+        checkOutput("m-summary.html", false,
+                "<h3>Services</h3>");
+    }
+
+    @Test
+    public void checkUsesNoApiTagModuleModeAll(Path base) throws Exception {
+        ModuleBuilder mb = new ModuleBuilder(tb, "m")
+                .uses("p1.A")
+                .uses("p1.B")
+                .exports("p1")
+                .classes("package p1; public class A {}")
+                .classes("package p1; public class B {}");
+        mb.write(base);
+
+        javadoc("-d", base.toString() + "/out",
+                "-quiet",
+                "--show-module-contents", "all",
+                "--module-source-path", base.toString(),
+                "--module", "m");
+        checkExit(Exit.OK);
+
+        checkOutput("m-summary.html", true,
+                "<h3>Services</h3>");
+
+        checkOutput("m-summary.html", true,
+                "<table class=\"usesSummary\" summary=\"Uses table, listing types, and an explanation\">\n" +
+                "<caption><span>Uses</span><span class=\"tabEnd\">&nbsp;</span></caption>\n" +
+                "<tr>\n" +
+                "<th class=\"colFirst\" scope=\"col\">Type</th>\n" +
+                "<th class=\"colLast\" scope=\"col\">Description</th>\n" +
+                "</tr>\n" +
+                "<tbody>\n" +
+                "<tr class=\"altColor\">\n" +
+                "<th class=\"colFirst\" scope=\"row\"><a href=\"p1/A.html\" title=\"class in p1\">A</a></th>\n" +
+                "<td class=\"colLast\">&nbsp;</td>\n" +
+                "</tr>\n" +
+                "<tr class=\"rowColor\">\n" +
+                "<th class=\"colFirst\" scope=\"row\"><a href=\"p1/B.html\" title=\"class in p1\">B</a></th>\n" +
+                "<td class=\"colLast\">&nbsp;</td>\n" +
+                "</tr>\n" +
+                "</tbody>\n" +
+                "</table>\n");
+    }
+
+    @Test
+    public void checkUsesWithApiTagModuleModeDefault(Path base) throws Exception {
+        ModuleBuilder mb = new ModuleBuilder(tb, "m")
+                .comment("module m.\n@uses p1.A")
+                .uses("p1.A")
+                .uses("p1.B")
+                .exports("p1")
+                .classes("package p1; public class A {}")
+                .classes("package p1; public class B {}");
+        mb.write(base);
+
+        javadoc("-d", base.toString() + "/out",
+                "-quiet",
+                "--module-source-path", base.toString(),
+                "--module", "m");
+        checkExit(Exit.OK);
+
+        checkOutput("m-summary.html", true,
+                "<h3>Services</h3>");
+
+        checkOutput("m-summary.html", true,
+                "<table class=\"usesSummary\" summary=\"Uses table, listing types, and an explanation\">\n" +
+                "<caption><span>Uses</span><span class=\"tabEnd\">&nbsp;</span></caption>\n" +
+                "<tr>\n" +
+                "<th class=\"colFirst\" scope=\"col\">Type</th>\n" +
+                "<th class=\"colLast\" scope=\"col\">Description</th>\n" +
+                "</tr>\n" +
+                "<tbody>\n" +
+                "<tr class=\"altColor\">\n" +
+                "<th class=\"colFirst\" scope=\"row\"><a href=\"p1/A.html\" title=\"class in p1\">A</a></th>\n" +
+                "<td class=\"colLast\">&nbsp;</td>\n" +
+                "</tr>\n" +
+                "</tbody>\n" +
+                "</table>\n");
+    }
+
+    @Test
+    public void checkProvidesNoApiTagModuleModeDefault(Path base) throws Exception {
+        ModuleBuilder mb = new ModuleBuilder(tb, "m")
+                .comment("module m.\n@uses p1.A")
+                .provides("p1.A", "p1.B")
+                .exports("p1")
+                .classes("package p1; public interface A {}")
+                .classes("package p1; public class B implements A {}")
+                .provides("p2.A", "p2.B")
+                .exports("p2")
+                .classes("package p2; public interface A {}")
+                .classes("package p2; public class B implements A {}");
+        mb.write(base);
+
+        javadoc("-d", base.toString() + "/out",
+                "-quiet",
+                "--module-source-path", base.toString(),
+                "--module", "m");
+
+        checkExit(Exit.OK);
+
+        checkOutput("m-summary.html", false,
+                "<h3>Services</h3>");
+    }
+
+    @Test
+    public void checkProvidesNoApiTagModuleModeAll(Path base) throws Exception {
+        ModuleBuilder mb = new ModuleBuilder(tb, "m")
+                .comment("module m.\n@uses p1.A") // bogus uses tag
+                .provides("p1.A", "p1.B")
+                .exports("p1")
+                .classes("package p1; public interface A {}")
+                .classes("package p1; public class B implements A {}")
+                .provides("p2.A", "p2.B")
+                .exports("p2")
+                .classes("package p2; public interface A {}")
+                .classes("package p2; public class B implements A {}");
+        mb.write(base);
+
+        javadoc("-d", base.toString() + "/out",
+                "-quiet",
+                "--show-module-contents", "all",
+                "--module-source-path", base.toString(),
+                "--module", "m");
+
+        checkExit(Exit.OK);
+
+        checkOutput("m-summary.html", true,
+                "<h3>Services</h3>");
+
+        checkOutput("m-summary.html", true,
+                "<table class=\"providesSummary\" summary=\"Provides table, listing types, and an explanation\">\n" +
+                "<caption><span>Provides</span><span class=\"tabEnd\">&nbsp;</span></caption>\n" +
+                "<tr>\n" +
+                "<th class=\"colFirst\" scope=\"col\">Type</th>\n" +
+                "<th class=\"colLast\" scope=\"col\">Description</th>\n" +
+                "</tr>\n" +
+                "<tbody>\n" +
+                "<tr class=\"altColor\">\n" +
+                "<th class=\"colFirst\" scope=\"row\"><a href=\"p1/A.html\" title=\"interface in p1\">A</a></th>\n" +
+                "<td class=\"colLast\">&nbsp;<br>(<span class=\"implementationLabel\">Implementation(s):</span>&nbsp;<a href=\"p1/B.html\" title=\"class in p1\">B</a>)</td>\n" +
+                "</tr>\n" +
+                "<tr class=\"rowColor\">\n" +
+                "<th class=\"colFirst\" scope=\"row\"><a href=\"p2/A.html\" title=\"interface in p2\">A</a></th>\n" +
+                "<td class=\"colLast\">&nbsp;<br>(<span class=\"implementationLabel\">Implementation(s):</span>&nbsp;<a href=\"p2/B.html\" title=\"class in p2\">B</a>)</td>\n" +
+                "</tr>\n" +
+                "</tbody>\n");
+    }
+
+    @Test
+    public void checkProvidesWithApiTagModuleModeDefault(Path base) throws Exception {
+        ModuleBuilder mb = new ModuleBuilder(tb, "m")
+                .comment("module m.\n@provides p1.A abc")
+                .provides("p1.A", "p1.B")
+                .exports("p1")
+                .classes("package p1; public interface A {}")
+                .classes("package p1; public class B implements A {}")
+                .provides("p2.A", "p2.B")
+                .exports("p2")
+                .classes("package p2; public interface A {}")
+                .classes("package p2; public class B implements A {}");
+        mb.write(base);
+
+        javadoc("-d", base.toString() + "/out",
+                "-quiet",
+                "--module-source-path", base.toString(),
+                "--module", "m");
+
+        checkExit(Exit.OK);
+
+        checkOutput("m-summary.html", true,
+                "<h3>Services</h3>");
+
+        checkOutput("m-summary.html", true,
+                "<table class=\"providesSummary\" summary=\"Provides table, listing types, and an explanation\">\n" +
+                "<caption><span>Provides</span><span class=\"tabEnd\">&nbsp;</span></caption>\n" +
+                "<tr>\n" +
+                "<th class=\"colFirst\" scope=\"col\">Type</th>\n" +
+                "<th class=\"colLast\" scope=\"col\">Description</th>\n" +
+                "</tr>\n" +
+                "<tbody>\n" +
+                "<tr class=\"altColor\">\n" +
+                "<th class=\"colFirst\" scope=\"row\"><a href=\"p1/A.html\" title=\"interface in p1\">A</a></th>\n" +
+                "<td class=\"colLast\">abc&nbsp;</td>\n" +
+                "</tr>\n" +
+                "</tbody>\n" +
+                "</table>\n");
+    }
+
+    @Test
+    public void checkUsesProvidesWithApiTagsModeDefault(Path base) throws Exception {
+        ModuleBuilder mb = new ModuleBuilder(tb, "m")
+                .comment("module m.\n@provides p1.A abc\n@uses p2.B def")
+                .provides("p1.A", "p1.B")
+                .exports("p1")
+                .classes("package p1; public interface A {}")
+                .classes("package p1; public class B implements A {}")
+                .provides("p2.A", "p2.B")
+                .uses("p2.B")
+                .exports("p2")
+                .classes("package p2; public interface A {}")
+                .classes("package p2; public class B implements A {}");
+        mb.write(base);
+
+        javadoc("-d", base.toString() + "/out",
+                "-quiet",
+                "--module-source-path", base.toString(),
+                "--module", "m");
+
+        checkExit(Exit.OK);
+
+        checkOutput("m-summary.html", true,
+                "<h3>Services</h3>");
+
+        checkOutput("m-summary.html", true,
+                "<table class=\"providesSummary\" summary=\"Provides table, listing types, and an explanation\">\n" +
+                "<caption><span>Provides</span><span class=\"tabEnd\">&nbsp;</span></caption>\n" +
+                "<tr>\n" +
+                "<th class=\"colFirst\" scope=\"col\">Type</th>\n" +
+                "<th class=\"colLast\" scope=\"col\">Description</th>\n" +
+                "</tr>\n" +
+                "<tbody>\n" +
+                "<tr class=\"altColor\">\n" +
+                "<th class=\"colFirst\" scope=\"row\"><a href=\"p1/A.html\" title=\"interface in p1\">A</a></th>\n" +
+                "<td class=\"colLast\">abc&nbsp;</td>\n" +
+                "</tr>\n" +
+                "</tbody>\n" +
+                "</table>",
+                "<table class=\"usesSummary\" summary=\"Uses table, listing types, and an explanation\">\n" +
+                "<caption><span>Uses</span><span class=\"tabEnd\">&nbsp;</span></caption>\n" +
+                "<tr>\n" +
+                "<th class=\"colFirst\" scope=\"col\">Type</th>\n" +
+                "<th class=\"colLast\" scope=\"col\">Description</th>\n" +
+                "</tr>\n" +
+                "<tbody>\n" +
+                "<tr class=\"altColor\">\n" +
+                "<th class=\"colFirst\" scope=\"row\"><a href=\"p2/B.html\" title=\"class in p2\">B</a></th>\n" +
+                "<td class=\"colLast\">def&nbsp;</td>\n" +
+                "</tr>\n" +
+                "</tbody>\n" +
+                "</table>\n");
+    }
+
+}
--- a/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java	Tue Apr 11 17:26:52 2017 -0700
+++ b/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java	Wed Apr 12 11:42:50 2017 -0700
@@ -591,10 +591,6 @@
                 + "<th class=\"colFirst\" scope=\"row\"><a href=\"testpkgmdlB/TestClassInModuleB.html\" title=\"class in testpkgmdlB\">TestClassInModuleB</a></th>\n"
                 + "<td class=\"colLast\">With a test description for uses.&nbsp;</td>\n"
                 + "</tr>",
-                "<tr class=\"altColor\">\n"
-                + "<th class=\"colFirst\" scope=\"row\"><a href=\"testpkg2mdlB/TestInterface2InModuleB.html\" title=\"interface in testpkg2mdlB\">TestInterface2InModuleB</a></th>\n"
-                + "<td class=\"colLast\">&nbsp;</td>\n"
-                + "</tr>",
                 "<caption><span>Opens</span><span class=\"tabEnd\">&nbsp;</span></caption>\n"
                 + "<tr>\n"
                 + "<th class=\"colFirst\" scope=\"col\">Package</th>\n"
--- a/langtools/test/tools/lib/toolbox/ModuleBuilder.java	Tue Apr 11 17:26:52 2017 -0700
+++ b/langtools/test/tools/lib/toolbox/ModuleBuilder.java	Wed Apr 12 11:42:50 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -247,7 +247,9 @@
         List<String> sources = new ArrayList<>();
         StringBuilder sb = new StringBuilder();
         if (!comment.isEmpty()) {
-            sb.append("/**\n").append(comment.replace("\n", " *")).append(" */\n");
+            sb.append("/**\n * ")
+                    .append(comment.replace("\n", "\n * "))
+                    .append("\n */\n");
         }
         sb.append("module ").append(name).append(" {\n");
         requires.forEach(r -> sb.append("    " + r + "\n"));