From a6dc4bc2b83c7240e573ac43f9b7a10191c58ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Fri, 8 Mar 2024 20:59:00 +0000 Subject: [PATCH] 8326332: Unclosed inline tags cause misalignment in summary tables Reviewed-by: gli, jjg --- .../formats/html/HtmlDocletWriter.java | 39 +++++++++++++------ .../testBreakIterator/TestBreakIterator.java | 7 +++- .../pkg/BreakIteratorTest.java | 5 +++ 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java index 4eb05aeb695..3962631fb49 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java @@ -113,6 +113,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.Utils.PreviewSummary; import jdk.javadoc.internal.doclint.HtmlTag; import static com.sun.source.doctree.DocTree.Kind.COMMENT; +import static com.sun.source.doctree.DocTree.Kind.START_ELEMENT; import static com.sun.source.doctree.DocTree.Kind.TEXT; @@ -1171,21 +1172,28 @@ public abstract class HtmlDocletWriter { } } - boolean ignoreNonInlineTag(DocTree dtree) { + boolean ignoreNonInlineTag(DocTree dtree, List openTags) { Name name = null; - if (dtree.getKind() == Kind.START_ELEMENT) { - StartElementTree setree = (StartElementTree)dtree; - name = setree.getName(); - } else if (dtree.getKind() == Kind.END_ELEMENT) { - EndElementTree eetree = (EndElementTree)dtree; - name = eetree.getName(); + Kind kind = dtree.getKind(); + if (kind == Kind.START_ELEMENT) { + name = ((StartElementTree)dtree).getName(); + } else if (kind == Kind.END_ELEMENT) { + name = ((EndElementTree)dtree).getName(); } if (name != null) { HtmlTag htmlTag = HtmlTag.get(name); - if (htmlTag != null && - htmlTag.blockType != jdk.javadoc.internal.doclint.HtmlTag.BlockType.INLINE) { - return true; + if (htmlTag != null) { + if (htmlTag.blockType != HtmlTag.BlockType.INLINE) { + return true; + } + // Keep track of open inline tags that need to be closed, see 8326332 + if (kind == START_ELEMENT && htmlTag.endKind == HtmlTag.EndKind.REQUIRED) { + openTags.add(name); + } else if (kind == Kind.END_ELEMENT && !openTags.isEmpty() + && openTags.getLast().equals(name)) { + openTags.removeLast(); + } } } return false; @@ -1257,6 +1265,7 @@ public abstract class HtmlDocletWriter { CommentHelper ch = utils.getCommentHelper(element); configuration.tagletManager.checkTags(element, trees); commentRemoved = false; + List openTags = new ArrayList<>(); for (ListIterator iterator = trees.listIterator(); iterator.hasNext();) { boolean isFirstNode = !iterator.hasPrevious(); @@ -1265,14 +1274,16 @@ public abstract class HtmlDocletWriter { if (context.isFirstSentence) { // Ignore block tags - if (ignoreNonInlineTag(tag)) + if (ignoreNonInlineTag(tag, openTags)) { continue; + } // Ignore any trailing whitespace OR whitespace after removed html comment if ((isLastNode || commentRemoved) && tag.getKind() == TEXT - && ((tag instanceof TextTree tt) && tt.getBody().isBlank())) + && ((tag instanceof TextTree tt) && tt.getBody().isBlank())) { continue; + } // Ignore any leading html comments if ((isFirstNode || commentRemoved) && tag.getKind() == COMMENT) { @@ -1466,6 +1477,10 @@ public abstract class HtmlDocletWriter { if (allDone) break; } + // Close any open inline tags + while (!openTags.isEmpty()) { + result.add(RawHtml.endElement(openTags.removeLast())); + } return result; } diff --git a/test/langtools/jdk/javadoc/doclet/testBreakIterator/TestBreakIterator.java b/test/langtools/jdk/javadoc/doclet/testBreakIterator/TestBreakIterator.java index 72b1336dc59..31f1d3a66da 100644 --- a/test/langtools/jdk/javadoc/doclet/testBreakIterator/TestBreakIterator.java +++ b/test/langtools/jdk/javadoc/doclet/testBreakIterator/TestBreakIterator.java @@ -23,7 +23,7 @@ /* * @test - * @bug 4165985 + * @bug 4165985 8326332 * @summary Determine the end of the first sentence using BreakIterator. * If the first sentence of "method" is parsed correctly, the test passes. * Correct Answer: "This is a class (i.e. it is indeed a class)." @@ -76,5 +76,10 @@ public class TestBreakIterator extends JavadocTester { """
A constant indicating that the keyLocation is indeterminate or not relevant.
"""); + + checkOutput("pkg/BreakIteratorTest.html", true, + """ + """); } } diff --git a/test/langtools/jdk/javadoc/doclet/testBreakIterator/pkg/BreakIteratorTest.java b/test/langtools/jdk/javadoc/doclet/testBreakIterator/pkg/BreakIteratorTest.java index 9370d162808..3ea965377f6 100644 --- a/test/langtools/jdk/javadoc/doclet/testBreakIterator/pkg/BreakIteratorTest.java +++ b/test/langtools/jdk/javadoc/doclet/testBreakIterator/pkg/BreakIteratorTest.java @@ -56,4 +56,9 @@ public class BreakIteratorTest { */ public void fe(){} + /** + * Inline tags extending + * beyond the first sentence. Tags are closed here. + */ + public void meh(){} }