# HG changeset patch # User ksrini # Date 1492022570 25200 # Node ID 6ce4d52084e833b35fce2140ab3351151fbedf9b # Parent 610dc2b489543b6bd76dc9a053d1231b81c38090 8178067: support for @uses/@provides tags is broken Reviewed-by: jjg diff -r 610dc2b48954 -r 6ce4d52084e8 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.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 section) { + public boolean display(Set 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 typeElements, + Map 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 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 implSet; Content description; for (Map.Entry> 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); diff -r 610dc2b48954 -r 6ce4d52084e8 langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java --- 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 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; diff -r 610dc2b48954 -r 6ce4d52084e8 langtools/test/jdk/javadoc/doclet/testModules/TestModuleServices.java --- /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, + "

Services

"); + } + + @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, + "

Services

"); + + checkOutput("m-summary.html", true, + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
Uses 
TypeDescription
A 
B 
\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, + "

Services

"); + + checkOutput("m-summary.html", true, + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
Uses 
TypeDescription
A 
\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, + "

Services

"); + } + + @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, + "

Services

"); + + checkOutput("m-summary.html", true, + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\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, + "

Services

"); + + checkOutput("m-summary.html", true, + "
Provides 
TypeDescription
A 
(Implementation(s): B)
A 
(Implementation(s): B)
\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
Provides 
TypeDescription
Aabc 
\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, + "

Services

"); + + checkOutput("m-summary.html", true, + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
Provides 
TypeDescription
Aabc 
", + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
Uses 
TypeDescription
Bdef 
\n"); + } + +} diff -r 610dc2b48954 -r 6ce4d52084e8 langtools/test/jdk/javadoc/doclet/testModules/TestModules.java --- 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 @@ + "TestClassInModuleB\n" + "With a test description for uses. \n" + "", - "\n" - + "TestInterface2InModuleB\n" - + " \n" - + "", "Opens \n" + "\n" + "Package\n" diff -r 610dc2b48954 -r 6ce4d52084e8 langtools/test/tools/lib/toolbox/ModuleBuilder.java --- 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 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"));