Skip to content

Commit 3bcf8e2

Browse files
Roman MarchenkoPaul Hohensee
authored andcommitted
8297437: javadoc cannot link to old docs (with old style anchors)
Reviewed-by: phh Backport-of: 15a14884013a975707008f648b8e4864e16006ed
1 parent 290cfae commit 3bcf8e2

File tree

6 files changed

+144
-40
lines changed

6 files changed

+144
-40
lines changed

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

Lines changed: 93 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2023, 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
@@ -108,6 +108,11 @@ private static class Item {
108108
*/
109109
final boolean relative;
110110

111+
/**
112+
* Indicates that docs use old-form of anchors.
113+
*/
114+
final boolean useOldFormId;
115+
111116
/**
112117
* Constructor to build a Extern Item object and map it with the element name.
113118
* If the same element name is found in the map, then the first mapped
@@ -118,10 +123,11 @@ private static class Item {
118123
* file is picked.
119124
* @param relative True if path is URL, false if directory path.
120125
*/
121-
Item(String elementName, DocPath path, boolean relative) {
126+
Item(String elementName, DocPath path, boolean relative, boolean useOldFormId) {
122127
this.elementName = elementName;
123128
this.path = path;
124129
this.relative = relative;
130+
this.useOldFormId = useOldFormId;
125131
}
126132

127133
/**
@@ -191,7 +197,7 @@ public DocLink getExternalLink(Element element, DocPath relativepath, String fil
191197
DocPath p = fnd.relative ?
192198
relativepath.resolve(fnd.path).resolve(filename) :
193199
fnd.path.resolve(filename);
194-
return new DocLink(p, memberName);
200+
return new DocLink(p, fnd.useOldFormId ? getOldFormHtmlName(memberName) : memberName);
195201
}
196202

197203
/**
@@ -261,7 +267,7 @@ public void checkPlatformLinks(String linkPlatformProperties, Reporter reporter)
261267
reporter.print(Kind.WARNING, resources.getText("doclet.Resource_error", elementListPath.getPath()));
262268
} else {
263269
try (InputStream in = open(elementListUrl)) {
264-
readElementList(in, docUrl, false, versionNumber);
270+
readElementList(in, docUrl, false, versionNumber, isOldFormPlatformDocs(versionNumber));
265271
} catch (IOException exc) {
266272
throw new Fault(resources.getText(
267273
"doclet.Resource_error", elementListPath.getPath()), exc);
@@ -272,6 +278,18 @@ public void checkPlatformLinks(String linkPlatformProperties, Reporter reporter)
272278
}
273279
}
274280

281+
/**
282+
* Checks if platform docs for the specified version use old-form anchors.
283+
* Old-form anchors are used by Oracle docs for JDKs 8 and 9.
284+
* It can be checked on https://docs.oracle.com/javase/<version>/docs/api
285+
*
286+
* @param version
287+
* @return True if docs use old-form anchors
288+
*/
289+
private boolean isOldFormPlatformDocs(int version) {
290+
return 8 == version || 9 == version;
291+
}
292+
275293
/**
276294
* Return the resource path for the package or element list for the given {@code version}.
277295
* @param version the platform version number
@@ -424,7 +442,7 @@ private void readElementListFromURL(String urlpath, URL elemlisturlpath) throws
424442
try {
425443
URL link = elemlisturlpath.toURI().resolve(DocPaths.ELEMENT_LIST.getPath()).toURL();
426444
try (InputStream in = open(link)) {
427-
readElementList(in, urlpath, false, 0);
445+
readElementList(in, urlpath, false, 0, false);
428446
}
429447
} catch (URISyntaxException | MalformedURLException exc) {
430448
throw new Fault(resources.getText("doclet.MalformedURL", elemlisturlpath.toString()), exc);
@@ -443,7 +461,7 @@ private void readPackageListFromURL(String urlpath, URL elemlisturlpath) throws
443461
try {
444462
URL link = elemlisturlpath.toURI().resolve(DocPaths.PACKAGE_LIST.getPath()).toURL();
445463
try (InputStream in = open(link)) {
446-
readElementList(in, urlpath, false, 0);
464+
readElementList(in, urlpath, false, 0, true);
447465
}
448466
} catch (URISyntaxException | MalformedURLException exc) {
449467
throw new Fault(resources.getText("doclet.MalformedURL", elemlisturlpath.toString()), exc);
@@ -467,27 +485,27 @@ private void readElementListFromFile(String path, DocFile elemListPath)
467485
file = file.resolveAgainst(DocumentationTool.Location.DOCUMENTATION_OUTPUT);
468486
}
469487
if (file.exists()) {
470-
readElementList(file, path);
488+
readElementList(file, path, false);
471489
} else {
472490
DocFile file1 = elemListPath.resolve(DocPaths.PACKAGE_LIST);
473491
if (!(file1.isAbsolute() || linkoffline)) {
474492
file1 = file1.resolveAgainst(DocumentationTool.Location.DOCUMENTATION_OUTPUT);
475493
}
476494
if (file1.exists()) {
477-
readElementList(file1, path);
495+
readElementList(file1, path, true);
478496
} else {
479497
throw new Fault(resources.getText("doclet.File_error", file.getPath()), null);
480498
}
481499
}
482500
}
483501

484-
private void readElementList(DocFile file, String path) throws Fault, DocFileIOException {
502+
private void readElementList(DocFile file, String path, boolean isOldFormDoc) throws Fault, DocFileIOException {
485503
try {
486504
if (file.canRead()) {
487505
boolean pathIsRelative
488506
= !isUrl(path)
489507
&& !DocFile.createFileForInput(configuration, path).isAbsolute();
490-
readElementList(file.openInputStream(), path, pathIsRelative, 0);
508+
readElementList(file.openInputStream(), path, pathIsRelative, 0, isOldFormDoc);
491509
} else {
492510
throw new Fault(resources.getText("doclet.File_error", file.getPath()), null);
493511
}
@@ -507,7 +525,8 @@ private void readElementList(DocFile file, String path) throws Fault, DocFileIOE
507525
* or {@code 0} if it does not belong to a platform libraries doc bundle.
508526
* @throws IOException if there is a problem reading or closing the stream
509527
*/
510-
private void readElementList(InputStream input, String path, boolean relative, int platformVersion)
528+
private void readElementList(InputStream input, String path, boolean relative, int platformVersion,
529+
boolean isOldFormDoc)
511530
throws IOException {
512531
try (BufferedReader in = new BufferedReader(new InputStreamReader(input))) {
513532
String elemname;
@@ -520,7 +539,7 @@ private void readElementList(InputStream input, String path, boolean relative, i
520539
elempath = basePath;
521540
if (elemname.startsWith(DocletConstants.MODULE_PREFIX)) {
522541
moduleName = elemname.replace(DocletConstants.MODULE_PREFIX, "");
523-
Item item = new Item(moduleName, elempath, relative);
542+
Item item = new Item(moduleName, elempath, relative, isOldFormDoc);
524543
moduleItems.put(moduleName, item);
525544
} else {
526545
DocPath pkgPath = DocPath.create(elemname.replace('.', '/'));
@@ -538,7 +557,7 @@ private void readElementList(InputStream input, String path, boolean relative, i
538557
} else {
539558
actualModuleName = moduleName == null ? DocletConstants.DEFAULT_ELEMENT_NAME : moduleName;
540559
}
541-
Item item = new Item(elemname, elempath, relative);
560+
Item item = new Item(elemname, elempath, relative, isOldFormDoc);
542561
packageItems.computeIfAbsent(actualModuleName, k -> new TreeMap<>())
543562
.putIfAbsent(elemname, item); // first-one-wins semantics
544563
issueWarning = false;
@@ -659,4 +678,65 @@ private InputStream open(URL url) throws IOException {
659678

660679
return in;
661680
}
681+
682+
/**
683+
* Converts a name to an old-form HTML name (old-form id).
684+
*
685+
* @param name the string that needs to be converted to a valid HTML name
686+
* @return old-form HTML name
687+
*/
688+
private String getOldFormHtmlName(String name) {
689+
/* The HTML 4 spec at http://www.w3.org/TR/html4/types.html#h-6.2 mentions
690+
* that the name/id should begin with a letter followed by other valid characters.
691+
* The HTML 5 spec (draft) is more permissive on names/ids where the only restriction
692+
* is that it should be at least one character long and should not contain spaces.
693+
* The spec draft is @ http://www.w3.org/html/wg/drafts/html/master/dom.html#the-id-attribute.
694+
*
695+
* For HTML 4, we need to check for non-characters at the beginning of the name and
696+
* substitute it accordingly, "_" and "$" can appear at the beginning of a member name.
697+
* The method substitutes "$" with "Z:Z:D" and will prefix "_" with "Z:Z".
698+
*/
699+
700+
if (null == name)
701+
return name;
702+
703+
StringBuilder sb = new StringBuilder();
704+
for (int i = 0; i < name.length(); i++) {
705+
char ch = name.charAt(i);
706+
switch (ch) {
707+
case '(':
708+
case ')':
709+
case '<':
710+
case '>':
711+
case ',':
712+
sb.append('-');
713+
break;
714+
case ' ':
715+
case '[':
716+
break;
717+
case ']':
718+
sb.append(":A");
719+
break;
720+
// Any appearance of $ needs to be substituted with ":D" and not with hyphen
721+
// since a field name "P$$ and a method P(), both valid member names, can end
722+
// up as "P--". A member name beginning with $ needs to be substituted with
723+
// "Z:Z:D".
724+
case '$':
725+
if (i == 0)
726+
sb.append("Z:Z");
727+
sb.append(":D");
728+
break;
729+
// A member name beginning with _ needs to be prefixed with "Z:Z" since valid anchor
730+
// names can only begin with a letter.
731+
case '_':
732+
if (i == 0)
733+
sb.append("Z:Z");
734+
sb.append(ch);
735+
break;
736+
default:
737+
sb.append(ch);
738+
}
739+
}
740+
return sb.toString();
741+
}
662742
}

test/langtools/jdk/javadoc/doclet/testClassCrossReferences/TestClassCrossReferences.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2002, 2023, 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
@@ -65,7 +65,7 @@ public void test() {
6565
java/math/BigDecimal.html" title="class or interface in java.math" class="extern\
6666
al-link"><code>Link to external class BigDecimal</code></a>""",
6767
"<a href=\"" + uri + """
68-
java/math/BigInteger.html#gcd(java.math.BigInteger)" title="class or interface i\
68+
java/math/BigInteger.html#gcd-java.math.BigInteger-" title="class or interface i\
6969
n java.math" class="external-link"><code>Link to external member gcd</code></a>""",
7070
"<a href=\"" + uri + """
7171
javax/tools/SimpleJavaFileObject.html#uri" title="class or interface in javax.to\
@@ -102,7 +102,7 @@ public void test_warning() {
102102
java/math/BigDecimal.html" title="class or interface in java.math" class="extern\
103103
al-link"><code>Link to external class BigDecimal</code></a>""",
104104
"<a href=\"" + uri + """
105-
java/math/BigInteger.html#gcd(java.math.BigInteger)" title="class or interface i\
105+
java/math/BigInteger.html#gcd-java.math.BigInteger-" title="class or interface i\
106106
n java.math" class="external-link"><code>Link to external member gcd</code></a>""",
107107
"<a href=\"" + uri + """
108108
javax/tools/SimpleJavaFileObject.html#uri" title="class or interface in javax.to\

test/langtools/jdk/javadoc/doclet/testExternalOverriddenMethod/TestExternalOverriddenMethod.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2023, 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
@@ -56,13 +56,13 @@ public void test() {
5656
"""
5757
<dt>Overrides:</dt>
5858
<dd><code><a href=\"""" + uri + """
59-
/java/io/FilterReader.html#read()" title="class or interface in java.io" class="\
59+
/java/io/FilterReader.html#read--" title="class or interface in java.io" class="\
6060
external-link">read</a></code>&nbsp;in class&nbsp;<code><a href=\"""" + uri + """
6161
/java/io/FilterReader.html" title="class or interface in java.io" class="external-link">FilterReader</a></code></dd>""",
6262
"""
6363
<dt>Specified by:</dt>
6464
<dd><code><a href=\"""" + uri + """
65-
/java/io/DataInput.html#readInt()" title="class or interface in java.io" class="\
65+
/java/io/DataInput.html#readInt--" title="class or interface in java.io" class="\
6666
external-link">readInt</a></code>&nbsp;in interface&nbsp;<code><a href=\"""" + uri + """
6767
/java/io/DataInput.html" title="class or interface in java.io" class="external-link">DataInput</a></code></dd>"""
6868
);

test/langtools/jdk/javadoc/doclet/testHref/TestHref.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2023, 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
@@ -53,7 +53,7 @@ public void test() {
5353
checkOutput("pkg/C1.html", true,
5454
//External link.
5555
"""
56-
href="http://java.sun.com/j2se/1.4/docs/api/java/lang/Object.html#wait(long,int)\"""",
56+
href="http://java.sun.com/j2se/1.4/docs/api/java/lang/Object.html#wait-long-int-\"""",
5757
//Member summary table link.
5858
"""
5959
href="#method(int,int,java.util.ArrayList)\"""",

test/langtools/jdk/javadoc/doclet/testLinkOption/TestLinkOption.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2002, 2023, 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
@@ -89,7 +89,7 @@ public void test() {
8989
checkOutput("pkg/B.html", true,
9090
"""
9191
<div class="block">A method with html tag the method <a href=\"""" + url + """
92-
java/lang/ClassLoader.html#getSystemClassLoader()" title="class or interface in \
92+
java/lang/ClassLoader.html#getSystemClassLoader--" title="class or interface in \
9393
java.lang" class="external-link"><code><b>getSystemClassLoader()</b></code></a> \
9494
as the parent class loader.</div>""",
9595
"""

0 commit comments

Comments
 (0)