8202462: {@index} may cause duplicate labels
authorpmuthuswamy
Tue, 25 Sep 2018 13:58:54 +0530
changeset 51862 c319db69099c
parent 51861 f7d40158eb2f
child 51863 bc38c75eed57
child 56904 c43e7cdbee31
8202462: {@index} may cause duplicate labels Reviewed-by: jjg
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java
test/langtools/jdk/javadoc/doclet/testIndexTaglet/TestIndexTaglet.java
test/langtools/jdk/javadoc/doclet/testModules/TestModules.java
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java	Tue Sep 25 12:36:45 2018 +0530
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java	Tue Sep 25 13:58:54 2018 +0530
@@ -355,6 +355,7 @@
     /**
      * Returns a TagletWriter that knows how to write HTML.
      *
+     * @param isFirstSentence  true if we want to write the first sentence
      * @return a TagletWriter that knows how to write HTML.
      */
     public TagletWriter getTagletWriterInstance(boolean isFirstSentence) {
@@ -362,6 +363,17 @@
     }
 
     /**
+     * Returns a TagletWriter that knows how to write HTML.
+     *
+     * @param isFirstSentence  true if we want to write the first sentence
+     * @param inSummary  true if tags are to be added in a summary section
+     * @return a TagletWriter
+     */
+    public TagletWriter getTagletWriterInstance(boolean isFirstSentence, boolean inSummary) {
+        return new TagletWriterImpl(this, isFirstSentence, inSummary);
+    }
+
+    /**
      * Get Package link, with target frame.
      *
      * @param pkg The link will be to the "package-summary.html" page for this package
@@ -1127,7 +1139,7 @@
     public void addInlineComment(Element element, DocTree tag, Content htmltree) {
         CommentHelper ch = utils.getCommentHelper(element);
         List<? extends DocTree> description = ch.getDescription(configuration, tag);
-        addCommentTags(element, tag, description, false, false, htmltree);
+        addCommentTags(element, tag, description, false, false, false, htmltree);
     }
 
     /**
@@ -1151,7 +1163,7 @@
      */
     public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) {
         CommentHelper ch = utils.getCommentHelper(e);
-        addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree);
+        addCommentTags(e, ch.getBody(configuration, tag), true, false, false, htmltree);
     }
 
     /**
@@ -1172,13 +1184,13 @@
      * @param htmltree the documentation tree to which the summary will be added
      */
     public void addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree) {
-        addCommentTags(element, firstSentenceTags, false, true, htmltree);
+        addCommentTags(element, firstSentenceTags, false, true, true, htmltree);
     }
 
     public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) {
         CommentHelper ch = utils.getCommentHelper(element);
         List<? extends DocTree> body = ch.getBody(configuration, tag);
-        addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree);
+        addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, true, htmltree);
     }
 
     /**
@@ -1188,7 +1200,7 @@
      * @param htmltree the documentation tree to which the inline comments will be added
      */
     public void addInlineComment(Element element, Content htmltree) {
-        addCommentTags(element, utils.getFullBody(element), false, false, htmltree);
+        addCommentTags(element, utils.getFullBody(element), false, false, false, htmltree);
     }
 
     /**
@@ -1198,11 +1210,12 @@
      * @param tags the first sentence tags for the doc
      * @param depr true if it is deprecated
      * @param first true if the first sentence tags should be added
+     * @param inSummary true if the comment tags are added into the summary section
      * @param htmltree the documentation tree to which the comment tags will be added
      */
     private void addCommentTags(Element element, List<? extends DocTree> tags, boolean depr,
-            boolean first, Content htmltree) {
-        addCommentTags(element, null, tags, depr, first, htmltree);
+            boolean first, boolean inSummary, Content htmltree) {
+        addCommentTags(element, null, tags, depr, first, inSummary, htmltree);
     }
 
     /**
@@ -1213,15 +1226,16 @@
      * @param tags the first sentence tags for the doc
      * @param depr true if it is deprecated
      * @param first true if the first sentence tags should be added
+     * @param inSummary true if the comment tags are added into the summary section
      * @param htmltree the documentation tree to which the comment tags will be added
      */
     private void addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr,
-            boolean first, Content htmltree) {
+            boolean first, boolean inSummary, Content htmltree) {
         if(configuration.nocomment){
             return;
         }
         Content div;
-        Content result = commentTagsToContent(null, element, tags, first);
+        Content result = commentTagsToContent(null, element, tags, first, inSummary);
         if (depr) {
             div = HtmlTree.DIV(HtmlStyle.deprecationComment, result);
             htmltree.addContent(div);
@@ -1267,10 +1281,10 @@
     private boolean commentRemoved = false;
 
     /**
-     * Converts inline tags and text to text strings, expanding the
+     * Converts inline tags and text to Content, expanding the
      * inline tags along the way.  Called wherever text can contain
      * an inline tag, such as in comments or in free-form text arguments
-     * to non-inline tags.
+     * to block tags.
      *
      * @param holderTag    specific tag where comment resides
      * @param element    specific element where comment resides
@@ -1281,6 +1295,25 @@
      */
     public Content commentTagsToContent(DocTree holderTag, Element element,
             List<? extends DocTree> tags, boolean isFirstSentence) {
+        return commentTagsToContent(holderTag, element, tags, isFirstSentence, false);
+    }
+
+    /**
+     * Converts inline tags and text to text strings, expanding the
+     * inline tags along the way.  Called wherever text can contain
+     * an inline tag, such as in comments or in free-form text arguments
+     * to block tags.
+     *
+     * @param holderTag    specific tag where comment resides
+     * @param element    specific element where comment resides
+     * @param tags   array of text tags and inline tags (often alternating)
+    present in the text of interest for this element
+     * @param isFirstSentence  true if text is first sentence
+     * @param inSummary   if the comment tags are added into the summary section
+     * @return a Content object
+     */
+    public Content commentTagsToContent(DocTree holderTag, Element element,
+            List<? extends DocTree> tags, boolean isFirstSentence, boolean inSummary) {
 
         final Content result = new ContentBuilder() {
             @Override
@@ -1445,7 +1478,7 @@
                 public Boolean visitIndex(IndexTree node, Content p) {
                     Content output = TagletWriter.getInlineTagOutput(element,
                             configuration.tagletManager, holderTag, tag,
-                            getTagletWriterInstance(isFirstSentence));
+                            getTagletWriterInstance(isFirstSentence, inSummary));
                     if (output != null) {
                         result.addContent(output);
                     }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java	Tue Sep 25 12:36:45 2018 +0530
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java	Tue Sep 25 13:58:54 2018 +0530
@@ -396,14 +396,14 @@
         (utils.getBlockTags(mdle, DocTree.Kind.PROVIDES)).forEach((tree) -> {
             TypeElement t = ch.getServiceType(configuration, tree);
             if (t != null) {
-                providesTrees.put(t, commentTagsToContent(tree, mdle, ch.getDescription(configuration, tree), false));
+                providesTrees.put(t, commentTagsToContent(tree, mdle, ch.getDescription(configuration, tree), false, true));
             }
         });
         // Generate the map of all services listed using @uses, and the description.
         (utils.getBlockTags(mdle, DocTree.Kind.USES)).forEach((tree) -> {
             TypeElement t = ch.getServiceType(configuration, tree);
             if (t != null) {
-                usesTrees.put(t, commentTagsToContent(tree, mdle, ch.getDescription(configuration, tree), false));
+                usesTrees.put(t, commentTagsToContent(tree, mdle, ch.getDescription(configuration, tree), false, true));
             }
         });
     }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java	Tue Sep 25 12:36:45 2018 +0530
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java	Tue Sep 25 13:58:54 2018 +0530
@@ -71,12 +71,18 @@
     private final HtmlDocletWriter htmlWriter;
     private final HtmlConfiguration configuration;
     private final Utils utils;
+    private final boolean inSummary;
 
     public TagletWriterImpl(HtmlDocletWriter htmlWriter, boolean isFirstSentence) {
+        this(htmlWriter, isFirstSentence, false);
+    }
+
+    public TagletWriterImpl(HtmlDocletWriter htmlWriter, boolean isFirstSentence, boolean inSummary) {
         super(isFirstSentence);
         this.htmlWriter = htmlWriter;
         configuration = htmlWriter.configuration;
         this.utils = configuration.utils;
+        this.inSummary = inSummary;
     }
 
     /**
@@ -107,53 +113,58 @@
         String desc = ch.getText(itt.getDescription());
 
         String anchorName = htmlWriter.links.getName(tagText);
-        Content result = HtmlTree.A_ID(HtmlStyle.searchTagResult, anchorName, new StringContent(tagText));
-        if (configuration.createindex && !tagText.isEmpty()) {
-            SearchIndexItem si = new SearchIndexItem();
-            si.setLabel(tagText);
-            si.setDescription(desc);
-            DocPaths docPaths = configuration.docPaths;
-            new SimpleElementVisitor9<Void, Void>() {
-                @Override
-                public Void visitModule(ModuleElement e, Void p) {
-                    si.setUrl(docPaths.moduleSummary(e).getPath() + "#" + anchorName);
-                    si.setHolder(utils.getFullyQualifiedName(element));
-                    return null;
-                }
+        Content result = null;
+        if (isFirstSentence && inSummary) {
+            result = new StringContent(tagText);
+        } else {
+            result = HtmlTree.A_ID(HtmlStyle.searchTagResult, anchorName, new StringContent(tagText));
+            if (configuration.createindex && !tagText.isEmpty()) {
+                SearchIndexItem si = new SearchIndexItem();
+                si.setLabel(tagText);
+                si.setDescription(desc);
+                DocPaths docPaths = configuration.docPaths;
+                new SimpleElementVisitor9<Void, Void>() {
+                    @Override
+                    public Void visitModule(ModuleElement e, Void p) {
+                        si.setUrl(docPaths.moduleSummary(e).getPath() + "#" + anchorName);
+                        si.setHolder(utils.getFullyQualifiedName(element));
+                        return null;
+                    }
 
-                @Override
-                public Void visitPackage(PackageElement e, Void p) {
-                    si.setUrl(docPaths.forPackage(e).getPath()
-                            + "/" + DocPaths.PACKAGE_SUMMARY.getPath() + "#" + anchorName);
-                    si.setHolder(utils.getSimpleName(element));
-                    return null;
-                }
+                    @Override
+                    public Void visitPackage(PackageElement e, Void p) {
+                        si.setUrl(docPaths.forPackage(e).getPath()
+                                + "/" + DocPaths.PACKAGE_SUMMARY.getPath() + "#" + anchorName);
+                        si.setHolder(utils.getSimpleName(element));
+                        return null;
+                    }
 
-                @Override
-                public Void visitType(TypeElement e, Void p) {
-                    si.setUrl(docPaths.forClass(e).getPath() + "#" + anchorName);
-                    si.setHolder(utils.getFullyQualifiedName(e));
-                    return null;
-                }
+                    @Override
+                    public Void visitType(TypeElement e, Void p) {
+                        si.setUrl(docPaths.forClass(e).getPath() + "#" + anchorName);
+                        si.setHolder(utils.getFullyQualifiedName(e));
+                        return null;
+                    }
 
-                @Override
-                public Void visitVariable(VariableElement e, Void p) {
-                    TypeElement te = utils.getEnclosingTypeElement(e);
-                    si.setUrl(docPaths.forClass(te).getPath() + "#" + anchorName);
-                    si.setHolder(utils.getFullyQualifiedName(e) + "." + utils.getSimpleName(e));
-                    return null;
-                }
+                    @Override
+                    public Void visitVariable(VariableElement e, Void p) {
+                        TypeElement te = utils.getEnclosingTypeElement(e);
+                        si.setUrl(docPaths.forClass(te).getPath() + "#" + anchorName);
+                        si.setHolder(utils.getFullyQualifiedName(e) + "." + utils.getSimpleName(e));
+                        return null;
+                    }
 
-                @Override
-                protected Void defaultAction(Element e, Void p) {
-                    TypeElement te = utils.getEnclosingTypeElement(e);
-                    si.setUrl(docPaths.forClass(te).getPath() + "#" + anchorName);
-                    si.setHolder(utils.getFullyQualifiedName(e));
-                    return null;
-                }
-            }.visit(element);
-            si.setCategory(configuration.getContent("doclet.SearchTags").toString());
-            configuration.tagSearchIndex.add(si);
+                    @Override
+                    protected Void defaultAction(Element e, Void p) {
+                        TypeElement te = utils.getEnclosingTypeElement(e);
+                        si.setUrl(docPaths.forClass(te).getPath() + "#" + anchorName);
+                        si.setHolder(utils.getFullyQualifiedName(e));
+                        return null;
+                    }
+                }.visit(element);
+                si.setCategory(configuration.getContent("doclet.SearchTags").toString());
+                configuration.tagSearchIndex.add(si);
+            }
         }
         return result;
     }
@@ -236,7 +247,7 @@
         body.addContent(HtmlTree.CODE(new RawHtml(paramName)));
         body.addContent(" - ");
         List<? extends DocTree> description = ch.getDescription(configuration, paramTag);
-        body.addContent(htmlWriter.commentTagsToContent(paramTag, element, description, false));
+        body.addContent(htmlWriter.commentTagsToContent(paramTag, element, description, false, inSummary));
         HtmlTree result = HtmlTree.DD(body);
         return result;
     }
@@ -264,7 +275,7 @@
         result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.returnLabel,
                 new StringContent(configuration.getText("doclet.Returns")))));
         result.addContent(HtmlTree.DD(htmlWriter.commentTagsToContent(
-                returnTag, element, ch.getDescription(configuration, returnTag), false)));
+                returnTag, element, ch.getDescription(configuration, returnTag), false, inSummary)));
         return result;
     }
 
@@ -333,7 +344,7 @@
                 body.addContent(", ");
             }
             List<? extends DocTree> bodyTags = ch.getBody(configuration, simpleTag);
-            body.addContent(htmlWriter.commentTagsToContent(simpleTag, element, bodyTags, false));
+            body.addContent(htmlWriter.commentTagsToContent(simpleTag, element, bodyTags, false, inSummary));
             many = true;
         }
         result.addContent(HtmlTree.DD(body));
@@ -348,7 +359,7 @@
         result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.simpleTagLabel, new RawHtml(header))));
         CommentHelper ch = utils.getCommentHelper(element);
         List<? extends DocTree> description = ch.getDescription(configuration, simpleTag);
-        Content body = htmlWriter.commentTagsToContent(simpleTag, element, description, false);
+        Content body = htmlWriter.commentTagsToContent(simpleTag, element, description, false, inSummary);
         result.addContent(HtmlTree.DD(body));
         return result;
     }
@@ -382,7 +393,7 @@
         }
         body.addContent(HtmlTree.CODE(excName));
         List<? extends DocTree> description = ch.getDescription(configuration, throwsTag);
-        Content desc = htmlWriter.commentTagsToContent(throwsTag, element, description, false);
+        Content desc = htmlWriter.commentTagsToContent(throwsTag, element, description, false, inSummary);
         if (desc != null && !desc.isEmpty()) {
             body.addContent(" - ");
             body.addContent(desc);
@@ -429,7 +440,7 @@
     public Content commentTagsToOutput(DocTree holderTag,
         Element holder, List<? extends DocTree> tags, boolean isFirstSentence) {
         return htmlWriter.commentTagsToContent(holderTag, holder,
-                tags, isFirstSentence);
+                tags, isFirstSentence, inSummary);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/jdk/javadoc/doclet/testIndexTaglet/TestIndexTaglet.java	Tue Sep 25 13:58:54 2018 +0530
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018, 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.
+ */
+
+/*
+ * @test
+ * @bug 8202462
+ * @summary {@index} may cause duplicate labels
+ * @library /tools/lib ../lib
+ * @modules jdk.javadoc/jdk.javadoc.internal.tool
+ * @build JavadocTester toolbox.ToolBox builder.ClassBuilder
+ * @run main TestIndexTaglet
+ */
+
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import builder.ClassBuilder;
+import builder.ClassBuilder.MethodBuilder;
+import toolbox.ToolBox;
+
+public class TestIndexTaglet extends JavadocTester {
+
+    final ToolBox tb;
+
+    public static void main(String... args) throws Exception {
+        TestIndexTaglet tester = new TestIndexTaglet();
+        tester.runTests(m -> new Object[]{Paths.get(m.getName())});
+    }
+
+    TestIndexTaglet() {
+        tb = new ToolBox();
+    }
+
+    @Test
+    void test(Path base) throws Exception {
+        Path srcDir = base.resolve("src");
+        createTestClass(srcDir);
+
+        Path outDir = base.resolve("out");
+        javadoc("-d", outDir.toString(),
+                "-sourcepath", srcDir.toString(),
+                "pkg");
+
+        checkExit(Exit.OK);
+
+        checkOrder("pkg/A.html",
+                "<h3>Method Detail</h3>\n",
+                "<div class=\"block\">test description with <a id=\"search_phrase_a\" "
+                 +    "class=\"searchTagResult\">search_phrase_a</a></div>");
+
+        checkOrder("pkg/A.html",
+                "<h3>Method Summary</h3>\n",
+                "<div class=\"block\">test description with search_phrase_a</div>");
+    }
+
+    void createTestClass(Path srcDir) throws Exception {
+        MethodBuilder method = MethodBuilder
+                .parse("public void func(A a) {}")
+                .setComments("test description with {@index search_phrase_a class a}");
+
+        new ClassBuilder(tb, "pkg.A")
+                .setModifiers("public", "class")
+                .addMembers(method)
+                .write(srcDir);
+
+    }
+}
--- a/test/langtools/jdk/javadoc/doclet/testModules/TestModules.java	Tue Sep 25 12:36:45 2018 +0530
+++ b/test/langtools/jdk/javadoc/doclet/testModules/TestModules.java	Tue Sep 25 13:58:54 2018 +0530
@@ -1071,7 +1071,7 @@
                 + "<th class=\"colSecond\" scope=\"row\"><a href=\"../moduleA/module-summary.html\">moduleA</a></th>\n"
                 + "<td class=\"colLast\">\n"
                 + "<div class=\"block\">This is a test description for the moduleA module with a Search "
-                + "phrase <a id=\"searchphrase\" class=\"searchTagResult\">search phrase</a>.</div>\n"
+                + "phrase search phrase.</div>\n"
                 + "</td>\n"
                 + "</tr>\n"
                 + "<tr class=\"rowColor\">\n"
@@ -1164,7 +1164,7 @@
                 + "<dt><a href=\"moduleA/module-summary.html\">moduleA</a> - module moduleA</dt>\n"
                 + "<dd>\n"
                 + "<div class=\"block\">This is a test description for the moduleA module with a Search "
-                + "phrase <a id=\"searchphrase\" class=\"searchTagResult\">search phrase</a>.</div>\n"
+                + "phrase search phrase.</div>\n"
                 + "</dd>\n"
                 + "<dt><a href=\"moduleB/module-summary.html\">moduleB</a> - module moduleB</dt>\n"
                 + "<dd>\n"
@@ -1193,7 +1193,7 @@
                 "<th class=\"colFirst\" scope=\"row\"><a href=\"moduleA/module-summary.html\">moduleA</a></th>\n"
                 + "<td class=\"colLast\">\n"
                 + "<div class=\"block\">This is a test description for the moduleA module with a Search "
-                + "phrase <a id=\"searchphrase\" class=\"searchTagResult\">search phrase</a>.</div>\n"
+                + "phrase search phrase.</div>\n"
                 + "</td>",
                 "<th class=\"colFirst\" scope=\"row\"><a href=\"moduleB/module-summary.html\">moduleB</a></th>\n"
                 + "<td class=\"colLast\">\n"
@@ -1235,7 +1235,7 @@
                 + "<th class=\"colSecond\" scope=\"row\"><a href=\"../moduleA/module-summary.html\">moduleA</a></th>\n"
                 + "<td class=\"colLast\">\n"
                 + "<div class=\"block\">This is a test description for the moduleA module with a Search "
-                + "phrase <a id=\"searchphrase\" class=\"searchTagResult\">search phrase</a>.</div>\n"
+                + "phrase search phrase.</div>\n"
                 + "</td>",
                 "<table class=\"requiresSummary\">\n"
                 + "<caption><span>Requires</span><span class=\"tabEnd\">&nbsp;</span></caption>\n"