Skip to content

Commit eee48bd

Browse files
hnspull[bot]
authored andcommitted
8313931: Javadoc: links to type parameters actually generate links to classes
Reviewed-by: jjg
1 parent 53de25d commit eee48bd

File tree

26 files changed

+336
-91
lines changed

26 files changed

+336
-91
lines changed

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import javax.lang.model.element.Name;
3838
import javax.lang.model.element.PackageElement;
3939
import javax.lang.model.element.TypeElement;
40+
import javax.lang.model.element.TypeParameterElement;
4041
import javax.lang.model.element.VariableElement;
4142
import javax.lang.model.type.TypeMirror;
4243
import javax.lang.model.util.SimpleElementVisitor8;
@@ -155,7 +156,7 @@ protected void buildClassTree(Content classContent) {
155156
* @param target the content to which the documentation will be added
156157
*/
157158
protected void buildClassInfo(Content target) {
158-
Content c = HtmlTree.DIV(HtmlStyles.horizontalScroll);
159+
var c = new ContentBuilder();
159160
buildParamInfo(c);
160161
buildSuperInterfacesInfo(c);
161162
buildImplementedInterfacesInfo(c);
@@ -164,11 +165,13 @@ protected void buildClassInfo(Content target) {
164165
buildInterfaceUsageInfo(c);
165166
buildNestedClassInfo(c);
166167
buildFunctionalInterfaceInfo(c);
167-
buildClassSignature(c);
168-
buildDeprecationInfo(c);
169-
buildClassDescription(c);
170-
buildClassTagInfo(c);
171-
168+
c.add(new HtmlTree(HtmlTag.HR));
169+
var div = HtmlTree.DIV(HtmlStyles.horizontalScroll);
170+
buildClassSignature(div);
171+
buildDeprecationInfo(div);
172+
buildClassDescription(div);
173+
buildClassTagInfo(div);
174+
c.add(div);
172175
target.add(getClassInfo(c));
173176
}
174177

@@ -432,19 +435,45 @@ private void setRecordDocumentation(TypeElement elem) {
432435
protected Content getHeader(String header) {
433436
HtmlTree body = getBody(getWindowTitle(utils.getSimpleName(typeElement)));
434437
var div = HtmlTree.DIV(HtmlStyles.header);
435-
HtmlLinkInfo linkInfo = new HtmlLinkInfo(configuration,
436-
HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_AND_BOUNDS, typeElement)
437-
.linkToSelf(false); // Let's not link to ourselves in the header
438438
var heading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING,
439439
HtmlStyles.title, Text.of(header));
440-
heading.add(getTypeParameterLinks(linkInfo));
440+
heading.add(getTypeParameters());
441441
div.add(heading);
442442
bodyContents.setHeader(getHeader(PageMode.CLASS, typeElement))
443443
.addMainContent(MarkerComments.START_OF_CLASS_DATA)
444444
.addMainContent(div);
445445
return body;
446446
}
447447

448+
// Renders type parameters for the class heading, creating id attributes
449+
// if @param block tags are missing in doc comment.
450+
private Content getTypeParameters() {
451+
var content = new ContentBuilder();
452+
var typeParams = typeElement.getTypeParameters();
453+
if (!typeParams.isEmpty()) {
454+
// Generate id attributes if @param tags are missing for type parameters.
455+
// Note that this does not handle the case where some but not all @param tags are missing.
456+
var needsId = !utils.hasBlockTag(typeElement, DocTree.Kind.PARAM);
457+
var linkInfo = new HtmlLinkInfo(configuration,
458+
HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_AND_BOUNDS, typeElement)
459+
.linkToSelf(false); // Let's not link to ourselves in the header
460+
content.add("<");
461+
var first = true;
462+
for (TypeParameterElement t : typeParams) {
463+
if (!first) {
464+
content.add(",").add(new HtmlTree(HtmlTag.WBR));
465+
}
466+
var typeParamLink = getLink(linkInfo.forType(t.asType()));
467+
content.add(needsId
468+
? HtmlTree.SPAN_ID(htmlIds.forTypeParam(t.getSimpleName().toString(), typeElement), typeParamLink)
469+
: typeParamLink);
470+
first = false;
471+
}
472+
content.add(">");
473+
}
474+
return content;
475+
}
476+
448477
protected Content getClassContentHeader() {
449478
return getContentHeader();
450479
}
@@ -473,7 +502,6 @@ public TypeElement getCurrentPageElement() {
473502
}
474503

475504
protected void addClassSignature(Content classInfo) {
476-
classInfo.add(new HtmlTree(HtmlTag.HR));
477505
classInfo.add(new Signatures.TypeSignature(typeElement, this)
478506
.toContent());
479507
}

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,22 @@ public static HtmlId forText(String text, Map<String, Integer> counts) {
462462
return HtmlId.of(count == 0 ? base : base + "-" + count);
463463
}
464464

465+
/**
466+
* Returns an id for text documenting a type parameter of a class or method.
467+
*
468+
* @param paramName the name of the type parameter
469+
* @param owner the enclosing element
470+
*
471+
* @return the id
472+
*/
473+
public HtmlId forTypeParam(String paramName, Element owner) {
474+
if (utils.isExecutableElement(owner)) {
475+
return HtmlId.of(forMember((ExecutableElement) owner).getFirst().name()
476+
+ "-type-param-" + paramName);
477+
}
478+
return HtmlId.of("type-param-" + paramName);
479+
}
480+
465481
/**
466482
* Returns an id for one of the kinds of section in the pages for item group summaries.
467483
*

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,11 @@ public Content visitTypeVariable(TypeVariable type, HtmlLinkInfo linkInfo) {
162162
Element owner = typevariable.asElement().getEnclosingElement();
163163
if (linkInfo.linkTypeParameters() && utils.isTypeElement(owner)) {
164164
linkInfo.setTypeElement((TypeElement) owner);
165-
Content label = newContent();
166-
label.add(utils.getTypeName(type, false));
167-
linkInfo.label(label).skipPreview(true);
165+
if (linkInfo.getLabel() == null || linkInfo.getLabel().isEmpty()) {
166+
Content label = newContent();
167+
label.add(utils.getTypeName(type, false));
168+
linkInfo.label(label).skipPreview(true);
169+
}
168170
link.add(getClassLink(linkInfo));
169171
} else {
170172
// No need to link method type parameters.
@@ -242,6 +244,11 @@ protected Content getClassLink(HtmlLinkInfo linkInfo) {
242244
boolean isTypeLink = linkInfo.getType() != null &&
243245
utils.isTypeVariable(utils.getComponentType(linkInfo.getType()));
244246
title = getClassToolTip(typeElement, isTypeLink);
247+
if (isTypeLink) {
248+
linkInfo.fragment(m_writer.configuration.htmlIds.forTypeParam(
249+
utils.getTypeName(utils.getComponentType(linkInfo.getType()), false),
250+
typeElement).name());
251+
}
245252
}
246253
Content label = linkInfo.getClassLinkLabel(configuration);
247254
if (linkInfo.getContext() == HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_IN_LABEL) {

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,20 @@ document.addEventListener("readystatechange", (e) => {
232232
});
233233
document.addEventListener("DOMContentLoaded", function(e) {
234234
setTopMargin();
235+
// Reset animation for type parameter target highlight
236+
document.querySelectorAll("a").forEach((link) => {
237+
link.addEventListener("click", (e) => {
238+
const href = e.currentTarget.getAttribute("href");
239+
if (href && href.startsWith("#") && href.indexOf("type-param-") > -1) {
240+
const target = document.getElementById(decodeURI(href.substring(1)));
241+
if (target) {
242+
target.style.animation = "none";
243+
void target.offsetHeight;
244+
target.style.removeProperty("animation");
245+
}
246+
}
247+
})
248+
});
235249
// Make sure current element is visible in breadcrumb navigation on small displays
236250
const subnav = document.querySelector("ol.sub-nav-list");
237251
if (subnav && subnav.lastElementChild) {
@@ -286,7 +300,7 @@ document.addEventListener("DOMContentLoaded", function(e) {
286300
});
287301
var expanded = false;
288302
var windowWidth;
289-
function collapse() {
303+
function collapse(e) {
290304
if (expanded) {
291305
mainnav.removeAttribute("style");
292306
if (toc) {
@@ -336,7 +350,7 @@ document.addEventListener("DOMContentLoaded", function(e) {
336350
document.querySelectorAll("h1, h2, h3, h4, h5, h6")
337351
.forEach((hdr, idx) => {
338352
// Create anchor links for headers with an associated id attribute
339-
var id = hdr.getAttribute("id") || hdr.parentElement.getAttribute("id")
353+
var id = hdr.parentElement.getAttribute("id") || hdr.getAttribute("id")
340354
|| (hdr.querySelector("a") && hdr.querySelector("a").getAttribute("id"));
341355
if (id) {
342356
var template = document.createElement('template');

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
--search-input-text-color: #000000;
6464
--search-input-placeholder-color: #909090;
6565
/* Highlight color for active search tag target */
66-
--search-tag-highlight-color: #ffff00;
66+
--search-tag-highlight-color: #ffff66;
6767
/* Adjustments for icon and active background colors of copy-to-clipboard buttons */
6868
--copy-icon-brightness: 100%;
6969
--copy-button-background-color-active: rgba(168, 168, 176, 0.3);
@@ -307,7 +307,7 @@ ol.sub-nav-list a.current-selection {
307307
*/
308308
.title {
309309
color:var(--title-color);
310-
margin:10px 0;
310+
margin:10px 0 12px 0;
311311
}
312312
.sub-title {
313313
margin:5px 0 0 0;
@@ -988,6 +988,22 @@ input::placeholder {
988988
.search-tag-result:target {
989989
background-color:var(--search-tag-highlight-color);
990990
}
991+
dd > span:target,
992+
h1 > span:target {
993+
animation: 2.4s ease-out highlight;
994+
}
995+
section.class-description dd > span:target,
996+
section.class-description h1 > span:target {
997+
scroll-margin-top: 20em;
998+
}
999+
@keyframes highlight {
1000+
from {
1001+
background-color: var(--search-tag-highlight-color);
1002+
}
1003+
60% {
1004+
background-color: var(--search-tag-highlight-color);
1005+
}
1006+
}
9911007
details.page-search-details {
9921008
display: inline-block;
9931009
}
@@ -1040,7 +1056,7 @@ span#page-search-link {
10401056
z-index: 5;
10411057
}
10421058
.inherited-list {
1043-
margin: 10px 0 10px 0;
1059+
margin: 10px 0;
10441060
}
10451061
.horizontal-scroll {
10461062
overflow: auto hidden;

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -176,7 +176,7 @@ Content linkSeeReferenceOutput(Element holder,
176176
return htmlWriter.getPackageLink(refPackage, labelContent, refFragment);
177177
} else {
178178
// @see is not referencing an included class, module or package. Check for cross-links.
179-
String refModuleName = ch.getReferencedModuleName(refSignature);
179+
String refModuleName = ch.getReferencedModuleName(refSignature);
180180
DocLink elementCrossLink = (refPackage != null) ? htmlWriter.getCrossPackageLink(refPackage) :
181181
(config.extern.isModule(refModuleName))
182182
? htmlWriter.getCrossModuleLink(utils.elementUtils.getModuleElement(refModuleName))
@@ -190,12 +190,28 @@ Content linkSeeReferenceOutput(Element holder,
190190
if (!config.isDocLintReferenceGroupEnabled()) {
191191
reportWarning.accept(
192192
"doclet.link.see.reference_not_found",
193-
new Object[] { refSignature});
193+
new Object[] {refSignature});
194194
}
195195
return htmlWriter.invalidTagOutput(resources.getText("doclet.link.see.reference_invalid"),
196-
Optional.of(labelContent.isEmpty() ? text: labelContent));
196+
Optional.of(labelContent.isEmpty() ? text : labelContent));
197197
}
198198
}
199+
} else if (utils.isTypeParameterElement(ref)) {
200+
// This is a type parameter of a generic class, method or constructor
201+
if (labelContent.isEmpty()) {
202+
labelContent = plainOrCode(isPlain, Text.of(utils.getSimpleName(ref)));
203+
}
204+
if (refMem == null) {
205+
return htmlWriter.getLink(
206+
new HtmlLinkInfo(config, HtmlLinkInfo.Kind.LINK_TYPE_PARAMS, ref.asType())
207+
.label(labelContent));
208+
} else {
209+
// HtmlLinkFactory does not render type parameters of generic methods as links, so instead of
210+
// teaching it how to do it (making the code even more complex) just create the link directly.
211+
return htmlWriter.getLink(new HtmlLinkInfo(config, HtmlLinkInfo.Kind.PLAIN, refClass)
212+
.fragment(config.htmlIds.forTypeParam(ref.getSimpleName().toString(), refMem).name())
213+
.label((labelContent)));
214+
}
199215
} else if (refFragment == null) {
200216
// Must be a class reference since refClass is not null and refFragment is null.
201217
if (labelContent.isEmpty() && refTree != null) {

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ParamTaglet.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -278,7 +278,9 @@ private Content paramTagOutput(Element element, ParamTree paramTag, String param
278278
body.add(" - ");
279279
List<? extends DocTree> description = ch.getDescription(paramTag);
280280
body.add(htmlWriter.commentTagsToContent(element, description, context.within(paramTag)));
281-
return HtmlTree.DD(body);
281+
return HtmlTree.DD(paramTag.isTypeParameter()
282+
? HtmlTree.SPAN_ID(config.htmlIds.forTypeParam(paramName, element), body)
283+
: body);
282284
}
283285

284286
private record Documentation(ParamTree paramTree, ExecutableElement method) { }

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ public Element getReferencedMember(Element e) {
188188
Utils utils = configuration.utils;
189189
if (e == null) {
190190
return null;
191+
} else if (utils.isTypeParameterElement(e)) {
192+
// Return the enclosing member for type parameters of generic methods or constructors.
193+
Element encl = e.getEnclosingElement();
194+
if (utils.isExecutableElement(encl)) {
195+
return encl;
196+
}
191197
}
192198
return (utils.isExecutableElement(e) || utils.isVariableElement(e)) ? e : null;
193199
}

test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -102,7 +102,6 @@ public void test() {
102102

103103
checkOutput("pkg/TestAnnotationType.html", true,
104104
"""
105-
<hr>
106105
<div class="type-signature"><span class="annotations">@Deprecated(forRemoval=true)
107106
@Documented
108107
</span><span class="modifiers">public @interface </span><span class="element-name type-n\
@@ -135,7 +134,6 @@ public void test() {
135134

136135
checkOutput("pkg/TestClass.html", true,
137136
"""
138-
<hr>
139137
<div class="type-signature"><span class="annotations">@Deprecated(forRemoval=true)
140138
</span><span class="modifiers">public class </span><span class="element-name type-name-label">TestClass</span>
141139
<span class="extends-implements">extends java.lang.Object</span></div>
@@ -212,7 +210,6 @@ public void test() {
212210

213211
checkOutput("pkg/TestEnum.html", true,
214212
"""
215-
<hr>
216213
<div class="type-signature"><span class="annotations">@Deprecated(forRemoval=true)
217214
</span><span class="modifiers">public enum </span><span class="element-name type-name-label">TestEnum</span>
218215
<span class="extends-implements">extends java.lang.Enum&lt;<a href="TestEnum.htm\
@@ -233,7 +230,6 @@ public void test() {
233230

234231
checkOutput("pkg/TestError.html", true,
235232
"""
236-
<hr>
237233
<div class="type-signature"><span class="annotations">@Deprecated(forRemoval=true)
238234
</span><span class="modifiers">public class </span><span class="element-name type-name-label">TestError</span>
239235
<span class="extends-implements">extends java.lang.Error</span></div>
@@ -244,7 +240,6 @@ public void test() {
244240

245241
checkOutput("pkg/TestException.html", true,
246242
"""
247-
<hr>
248243
<div class="type-signature"><span class="annotations">@Deprecated(forRemoval=true)
249244
</span><span class="modifiers">public class </span><span class="element-name type-name-label">TestException</span>
250245
<span class="extends-implements">extends java.lang.Exception</span></div>
@@ -255,7 +250,6 @@ public void test() {
255250

256251
checkOutput("pkg/TestInterface.html", true,
257252
"""
258-
<hr>
259253
<div class="type-signature"><span class="annotations">@Deprecated(forRemoval=true)
260254
</span><span class="modifiers">public class </span><span class="element-name type-name-label">TestInterface</span>
261255
<span class="extends-implements">extends java.lang.Object</span></div>

test/langtools/jdk/javadoc/doclet/testDirectedInheritance/TestDirectedInheritance.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -154,8 +154,8 @@ public interface E1 extends I1, I2 {
154154
<div class="block">I2: main description</div>
155155
""", """
156156
<dt>Type Parameters:</dt>
157-
<dd><code>E</code> - I2: first type parameter</dd>
158-
<dd><code>F</code> - I2: second type parameter</dd>
157+
<dd><span id="m(E)-type-param-E"><code>E</code> - I2: first type parameter</span></dd>
158+
<dd><span id="m(E)-type-param-F"><code>F</code> - I2: second type parameter</span></dd>
159159
<dt>Parameters:</dt>
160160
<dd><code>eObj</code> - I2: parameter</dd>
161161
<dt>Returns:</dt>

0 commit comments

Comments
 (0)