diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java index a8cc7f7b2ec..5fa8157a121 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -29,6 +29,7 @@ import jdk.javadoc.internal.doclets.toolkit.Content; import jdk.javadoc.internal.doclets.toolkit.util.DocLink; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.Extern; /** * Factory for HTML A elements, both links (with a {@code href} attribute) @@ -334,44 +335,7 @@ public String getName(String name) { return name.replaceAll(" +", ""); } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < name.length(); i++) { - char ch = name.charAt(i); - switch (ch) { - case '(': - case ')': - case '<': - case '>': - case ',': - sb.append('-'); - break; - case ' ': - case '[': - break; - case ']': - sb.append(":A"); - break; - // Any appearance of $ needs to be substituted with ":D" and not with hyphen - // since a field name "P$$ and a method P(), both valid member names, can end - // up as "P--". A member name beginning with $ needs to be substituted with - // "Z:Z:D". - case '$': - if (i == 0) - sb.append("Z:Z"); - sb.append(":D"); - break; - // A member name beginning with _ needs to be prefixed with "Z:Z" since valid anchor - // names can only begin with a letter. - case '_': - if (i == 0) - sb.append("Z:Z"); - sb.append(ch); - break; - default: - sb.append(ch); - } - } - return sb.toString(); + return Extern.getOldFormHtmlName(name); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Extern.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Extern.java index 9007bf8ed88..aa35b2cd9d7 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Extern.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Extern.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023, 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 @@ -98,6 +98,11 @@ private static class Item { */ final boolean relative; + /** + * Indicates that docs use old-form of anchors. + */ + final boolean useOldFormId; + /** * Constructor to build a Extern Item object and map it with the element name. * If the same element name is found in the map, then the first mapped @@ -108,10 +113,11 @@ private static class Item { * file is picked. * @param relative True if path is URL, false if directory path. */ - Item(String elementName, DocPath path, boolean relative) { + Item(String elementName, DocPath path, boolean relative, boolean useOldFormId) { this.elementName = elementName; this.path = path; this.relative = relative; + this.useOldFormId = useOldFormId; } /** @@ -179,7 +185,7 @@ public DocLink getExternalLink(Element element, DocPath relativepath, String fil DocPath p = fnd.relative ? relativepath.resolve(fnd.path).resolve(filename) : fnd.path.resolve(filename); - return new DocLink(p, "is-external=true", memberName); + return new DocLink(p, "is-external=true", fnd.useOldFormId ? getOldFormHtmlName(memberName) : memberName); } /** @@ -212,6 +218,18 @@ public boolean link(String url, String elemlisturl, Reporter reporter) throws Do return link(url, elemlisturl, reporter, true); } + /** + * Checks if platform docs for the specified version use old-form anchors. + * Old-form anchors are used by Oracle docs for JDKs 8 and 9. + * It can be checked on https://docs.oracle.com/javase//docs/api + * + * @param version + * @return True if docs use old-form anchors + */ + private boolean isOldFormPlatformDocs(int version) { + return 8 == version || 9 == version; + } + /* * Build the extern element list from given URL or the directory path. * Flag error if the "-link" or "-linkoffline" option is already used. @@ -292,7 +310,7 @@ private String adjustEndFileSeparator(String url) { private void readElementListFromURL(String urlpath, URL elemlisturlpath) throws Fault { try { URL link = elemlisturlpath.toURI().resolve(DocPaths.ELEMENT_LIST.getPath()).toURL(); - readElementList(link.openStream(), urlpath, false); + readElementList(link.openStream(), urlpath, false, false); } catch (URISyntaxException | MalformedURLException exc) { throw new Fault(configuration.getText("doclet.MalformedURL", elemlisturlpath.toString()), exc); } catch (IOException exc) { @@ -309,7 +327,7 @@ private void readElementListFromURL(String urlpath, URL elemlisturlpath) throws private void readAlternateURL(String urlpath, URL elemlisturlpath) throws Fault { try { URL link = elemlisturlpath.toURI().resolve(DocPaths.PACKAGE_LIST.getPath()).toURL(); - readElementList(link.openStream(), urlpath, false); + readElementList(link.openStream(), urlpath, false, true); } catch (URISyntaxException | MalformedURLException exc) { throw new Fault(configuration.getText("doclet.MalformedURL", elemlisturlpath.toString()), exc); } catch (IOException exc) { @@ -332,27 +350,27 @@ private void readElementListFromFile(String path, DocFile elemListPath) file = file.resolveAgainst(DocumentationTool.Location.DOCUMENTATION_OUTPUT); } if (file.exists()) { - readElementList(file, path); + readElementList(file, path, false); } else { DocFile file1 = elemListPath.resolve(DocPaths.PACKAGE_LIST); if (!(file1.isAbsolute() || linkoffline)) { file1 = file1.resolveAgainst(DocumentationTool.Location.DOCUMENTATION_OUTPUT); } if (file1.exists()) { - readElementList(file1, path); + readElementList(file1, path, true); } else { throw new Fault(configuration.getText("doclet.File_error", file.getPath()), null); } } } - private void readElementList(DocFile file, String path) throws Fault, DocFileIOException { + private void readElementList(DocFile file, String path, boolean isOldFormDoc) throws Fault, DocFileIOException { try { if (file.canRead()) { boolean pathIsRelative = !isUrl(path) && !DocFile.createFileForInput(configuration, path).isAbsolute(); - readElementList(file.openInputStream(), path, pathIsRelative); + readElementList(file.openInputStream(), path, pathIsRelative, isOldFormDoc); } else { throw new Fault(configuration.getText("doclet.File_error", file.getPath()), null); } @@ -370,7 +388,7 @@ private void readElementList(DocFile file, String path) throws Fault, DocFileIOE * @param relative Is path relative? * @throws IOException if there is a problem reading or closing the stream */ - private void readElementList(InputStream input, String path, boolean relative) + private void readElementList(InputStream input, String path, boolean relative, boolean isOldFormDoc) throws IOException { try (BufferedReader in = new BufferedReader(new InputStreamReader(input))) { String elemname = null; @@ -382,7 +400,7 @@ private void readElementList(InputStream input, String path, boolean relative) elempath = basePath; if (elemname.startsWith(DocletConstants.MODULE_PREFIX)) { moduleName = elemname.replace(DocletConstants.MODULE_PREFIX, ""); - Item item = new Item(moduleName, elempath, relative); + Item item = new Item(moduleName, elempath, relative, isOldFormDoc); moduleItems.put(moduleName, item); } else { DocPath pkgPath = DocPath.create(elemname.replace('.', '/')); @@ -392,7 +410,7 @@ private void readElementList(InputStream input, String path, boolean relative) elempath = elempath.resolve(pkgPath); } String actualModuleName = checkLinkCompatibility(elemname, moduleName, path); - Item item = new Item(elemname, elempath, relative); + Item item = new Item(elemname, elempath, relative, isOldFormDoc); packageItems.computeIfAbsent(actualModuleName, k -> new TreeMap<>()) .put(elemname, item); } @@ -455,4 +473,65 @@ private String checkLinkCompatibility(String packageName, String moduleName, Str } return moduleName == null ? DocletConstants.DEFAULT_ELEMENT_NAME : moduleName; } + + /** + * Converts a name to an old-form HTML name (old-form id). + * + * @param name the string that needs to be converted to a valid HTML name + * @return old-form HTML name + */ + public static String getOldFormHtmlName(String name) { + /* The HTML 4 spec at http://www.w3.org/TR/html4/types.html#h-6.2 mentions + * that the name/id should begin with a letter followed by other valid characters. + * The HTML 5 spec (draft) is more permissive on names/ids where the only restriction + * is that it should be at least one character long and should not contain spaces. + * The spec draft is @ http://www.w3.org/html/wg/drafts/html/master/dom.html#the-id-attribute. + * + * For HTML 4, we need to check for non-characters at the beginning of the name and + * substitute it accordingly, "_" and "$" can appear at the beginning of a member name. + * The method substitutes "$" with "Z:Z:D" and will prefix "_" with "Z:Z". + */ + + if (null == name) + return name; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char ch = name.charAt(i); + switch (ch) { + case '(': + case ')': + case '<': + case '>': + case ',': + sb.append('-'); + break; + case ' ': + case '[': + break; + case ']': + sb.append(":A"); + break; + // Any appearance of $ needs to be substituted with ":D" and not with hyphen + // since a field name "P$$ and a method P(), both valid member names, can end + // up as "P--". A member name beginning with $ needs to be substituted with + // "Z:Z:D". + case '$': + if (i == 0) + sb.append("Z:Z"); + sb.append(":D"); + break; + // A member name beginning with _ needs to be prefixed with "Z:Z" since valid anchor + // names can only begin with a letter. + case '_': + if (i == 0) + sb.append("Z:Z"); + sb.append(ch); + break; + default: + sb.append(ch); + } + } + return sb.toString(); + } } diff --git a/test/langtools/jdk/javadoc/doclet/testClassCrossReferences/TestClassCrossReferences.java b/test/langtools/jdk/javadoc/doclet/testClassCrossReferences/TestClassCrossReferences.java index b4cf2753233..ca5115788eb 100644 --- a/test/langtools/jdk/javadoc/doclet/testClassCrossReferences/TestClassCrossReferences.java +++ b/test/langtools/jdk/javadoc/doclet/testClassCrossReferences/TestClassCrossReferences.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2023, 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 @@ -59,7 +59,7 @@ void test() { + "title=\"class or interface in javax.swing.text\" class=\"externalLink\">Link to AttributeContext innerclass", "Link to external class BigDecimal", - "Link to external member gcd", "Link to external member URI", @@ -90,7 +90,7 @@ void test_warning() { + "title=\"class or interface in javax.swing.text\" class=\"externalLink\">Link to AttributeContext innerclass", "Link to external class BigDecimal", - "Link to external member gcd", "Link to external member URI", diff --git a/test/langtools/jdk/javadoc/doclet/testExternalOverridenMethod/TestExternalOverridenMethod.java b/test/langtools/jdk/javadoc/doclet/testExternalOverridenMethod/TestExternalOverridenMethod.java index 57389873191..30c29e32258 100644 --- a/test/langtools/jdk/javadoc/doclet/testExternalOverridenMethod/TestExternalOverridenMethod.java +++ b/test/langtools/jdk/javadoc/doclet/testExternalOverridenMethod/TestExternalOverridenMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, 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 @@ -53,12 +53,12 @@ void test() { checkOutput("pkg/XReader.html", true, "
Overrides:
\n" - + "
read in class " + "FilterReader
", "
Specified by:
\n" - + "
readInt in interface " + "DataInput
" diff --git a/test/langtools/jdk/javadoc/doclet/testHref/TestHref.java b/test/langtools/jdk/javadoc/doclet/testHref/TestHref.java index a860663482e..3d4a1595a16 100644 --- a/test/langtools/jdk/javadoc/doclet/testHref/TestHref.java +++ b/test/langtools/jdk/javadoc/doclet/testHref/TestHref.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, 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 @@ -51,7 +51,7 @@ void test() { checkOutput("pkg/C1.html", true, //External link. - "href=\"http://java.sun.com/j2se/1.4/docs/api/java/lang/Object.html?is-external=true#wait(long,int)\"", + "href=\"http://java.sun.com/j2se/1.4/docs/api/java/lang/Object.html?is-external=true#wait-long-int-\"", //Member summary table link. "href=\"#method(int,int,java.util.ArrayList)\"", //Anchor test. diff --git a/test/langtools/jdk/javadoc/doclet/testLinkOption/TestLinkOption.java b/test/langtools/jdk/javadoc/doclet/testLinkOption/TestLinkOption.java index 42f983cd1a4..18fd1e535c3 100644 --- a/test/langtools/jdk/javadoc/doclet/testLinkOption/TestLinkOption.java +++ b/test/langtools/jdk/javadoc/doclet/testLinkOption/TestLinkOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2023, 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 @@ -85,7 +85,7 @@ void test() { checkOutput("pkg/B.html", true, "
A method with html tag the method " - + "getSystemClassLoader()" + " as the parent class loader.
", "
is equivalent to invoking "