undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,197 @@
/*
* Copyright (c) 2024, 2025, 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.
*
* 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.
*/
import doccheckutils.FileChecker;
import doccheckutils.FileProcessor;
import doccheckutils.HtmlFileChecker;
import doccheckutils.checkers.BadCharacterChecker;
import doccheckutils.checkers.DocTypeChecker;
import doccheckutils.checkers.LinkChecker;
import doccheckutils.checkers.TidyChecker;
import doccheckutils.checkers.ExtLinkChecker;
import toolbox.TestRunner;
import java.nio.file.Path;
import java.util.*;
/**
* DocCheck
* <p>
* For the sake of brevity, to run all of these checkers use
* <p>
* `make test-docs_all TEST_DEPS=docs-jdk`
* <p>
* This collection of tests provide a variety of checks for JDK documentation bundle.
* <p>
* It is meant to provide a convenient way to alert users of any errors in their documentation
* before a push and verify the quality of the documentation.
* It is not meant to replace more authoritative checkers; instead,
* it is more focused on providing a convenient, easy overview of any possible issues.
* <p>
* It supports the following checks:
* <p>
* *HTML* -- We use the standard `tidy` utility to check for HTML compliance,
* according to the declared version of HTML.
* The output from `tidy` is analysed to generate a report summarizing any issues that were found.
* <p>
* Version `5.9.20` of `tidy` is expected, or the output from the `--version` option should contain the string `version 5`.
* The test warns the user if he is using an earlier version.
* <p>
* *Bad Characters* -- We assumee that HTML files are encoded in UTF-8,
* and reports any character encoding issues that it finds.
* <p>
* *DocType* -- We assume that HTML files should use HTML5, and reports
* any files for which that is not the case.
* <p>
* *Links* -- We check links within a set of files, and reports on links
* to external resources, without otherwise checking them.
* <p>
* *External Links* -- We scan the files for URLs that refer to
* external resources, and validates those references using a "golden file" that includes a list of vetted links.
* <p>
* Each external reference is only checked once; but if an issue is found, all the files containing the
* reference will be reported.
*/
public class DocCheck extends TestRunner {
private static final String DOCCHECK_DIR = System.getProperty("doccheck.dir");
private static final Path DIR = Path.of(DOCCHECK_DIR != null ? DOCCHECK_DIR : "");
private static final Set<String> CHECKS_LIST = new HashSet<>();
private static Path DOCS_DIR;
private static boolean html;
private static boolean links;
private static boolean badchars;
private static boolean doctype;
private static boolean extlinks;
private List<Path> files;
public DocCheck() {
super(System.err);
init();
}
public static void main(String... args) throws Exception {
chooseCheckers();
DocCheck docCheck = new DocCheck();
docCheck.runTests();
}
private static void chooseCheckers() {
final String checks = System.getProperty("doccheck.checks");
if (!checks.isEmpty()) {
if (checks.contains(",")) {
CHECKS_LIST.addAll(Arrays.asList(checks.split(",")));
} else {
CHECKS_LIST.add(checks);
}
}
if (CHECKS_LIST.contains("all")) {
html = true;
links = true;
badchars = true;
doctype = true;
extlinks = true;
} else {
if (CHECKS_LIST.contains("html")) {
html = true;
}
if (CHECKS_LIST.contains("links")) {
links = true;
}
if (CHECKS_LIST.contains("badchars")) {
badchars = true;
}
if (CHECKS_LIST.contains("doctype")) {
doctype = true;
}
if (CHECKS_LIST.contains("extlinks")) {
extlinks = true;
}
}
}
public void init() {
var fileTester = new FileProcessor();
DOCS_DIR = DocTester.resolveDocs();
var baseDir = DOCS_DIR.resolve(DIR);
fileTester.processFiles(baseDir);
files = fileTester.getFiles();
if (html) {
new TidyChecker();
}
}
public List<FileChecker> getCheckers() {
List<FileChecker> checkers = new ArrayList<>();
if (html) {
checkers.add(new TidyChecker());
}
if (links) {
var linkChecker = new LinkChecker();
linkChecker.setBaseDir(DOCS_DIR);
checkers.add(new HtmlFileChecker(linkChecker, DOCS_DIR));
}
if (extlinks) {
checkers.add(new HtmlFileChecker(new ExtLinkChecker(), DOCS_DIR));
}
// there should be almost nothing reported from these two checkers
// most reports should be broken anchors/links, missing files and errors in html
if (badchars) {
checkers.add(new BadCharacterChecker());
}
if (doctype) {
checkers.add(new HtmlFileChecker(new DocTypeChecker(), DOCS_DIR));
}
return checkers;
}
@Test
public void test() throws Exception {
List<FileChecker> checkers = getCheckers();
runCheckersSequentially(checkers);
}
private void runCheckersSequentially(List<FileChecker> checkers) throws Exception {
List<Throwable> exceptions = new ArrayList<>();
for (FileChecker checker : checkers) {
try (checker) {
checker.checkFiles(files);
} catch (Exception e) {
exceptions.add(e);
}
}
if (!exceptions.isEmpty()) {
throw new Exception("One or more HTML checkers failed: " + exceptions);
}
}
}

View file

@ -0,0 +1,772 @@
# This file is used to check external links in the JDK generated documentation
# to prevent broken links from backsliding into the JDK source.
#
# The file serves as a "whitelist" of links that have been checked to be working as intended
# and JDK developers should add external links to this file whenever they add them to their documentation.
#
#
# The links in this file are checked before every release.
#
#
#
http://cldr.unicode.org/
http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
http://docs.oracle.com/javase/feedback.html
http://docs.oracle.com/javase/jndi/tutorial/index.html
http://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-12.html
http://docs.oracle.com/javase/tutorial/collections/index.html
http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html
http://docs.oracle.com/javase/tutorial/i18n/format/simpleDateFormat.html
http://docs.oracle.com/javase/tutorial/jdbc/
http://docs.oracle.com/javase/tutorial/jdbc/basics/index.html
http://docs.oracle.com/javase/tutorial/jdbc/basics/rowset.html
http://docs.oracle.com/javase/tutorial/uiswing/dnd/index.html
http://en.wikipedia.org/wiki/Skip_list
http://jclark.com/xml/xmlns.htm
http://jcp.org/en/jsr/detail?id=173
http://jcp.org/en/jsr/detail?id=268
http://relaxng.org/spec-20011203.html
http://sax.sourceforge.net/?selected=get-set
http://standards.iso.org/iso/9075/2002/12/sqlxml.xsd
http://svn.python.org/projects/python/trunk/Objects/listsort.txt
http://tools.ietf.org/html/rfc1421
http://tools.ietf.org/html/rfc5869
http://unicode.org/reports/tr35/
http://unicode.org/reports/tr35/tr35-numbers.html
http://web.archive.org/web/20051219043731/http://archive.ncsa.uiuc.edu/SDG/Software/Mosaic/Demo/url-primer.html
http://www.cl.cam.ac.uk/~mgk25/time/utc-sls/
http://www.cs.rochester.edu/~scott/papers/1996_PODC_queues.pdf
http://www.iana.org/
http://www.iana.org/assignments/character-sets
http://www.iana.org/assignments/character-sets/character-sets.xhtml
http://www.iana.org/assignments/media-types/
http://www.iana.org/assignments/uri-schemes.html
http://www.ietf.org/
https://www.ietf.org/rfc/rfc793.txt
https://www.ietf.org/rfc/rfc822.txt
http://www.ietf.org/rfc/rfc1122.txt
http://www.ietf.org/rfc/rfc1123.txt
http://www.ietf.org/rfc/rfc1323.txt
http://www.ietf.org/rfc/rfc1349.txt
http://www.ietf.org/rfc/rfc1521.txt
http://www.ietf.org/rfc/rfc1522.txt
http://www.ietf.org/rfc/rfc1918.txt
http://www.ietf.org/rfc/rfc1950.txt
http://www.ietf.org/rfc/rfc1950.txt.pdf
http://www.ietf.org/rfc/rfc1951.txt
http://www.ietf.org/rfc/rfc1951.txt.pdf
http://www.ietf.org/rfc/rfc1952.txt
http://www.ietf.org/rfc/rfc1952.txt.pdf
http://www.ietf.org/rfc/rfc1964.txt
http://www.ietf.org/rfc/rfc2045.txt
http://www.ietf.org/rfc/rfc2046.txt
http://www.ietf.org/rfc/rfc2078.txt
http://www.ietf.org/rfc/rfc2104.txt
http://www.ietf.org/rfc/rfc2109.txt
http://www.ietf.org/rfc/rfc2222.txt
http://www.ietf.org/rfc/rfc2236.txt
http://www.ietf.org/rfc/rfc2245.txt
http://www.ietf.org/rfc/rfc2246.txt
http://www.ietf.org/rfc/rfc2251.txt
http://www.ietf.org/rfc/rfc2253.txt
http://www.ietf.org/rfc/rfc2254.txt
http://www.ietf.org/rfc/rfc2255.txt
http://www.ietf.org/rfc/rfc2268.txt
http://www.ietf.org/rfc/rfc2278.txt
http://www.ietf.org/rfc/rfc2279.txt
http://www.ietf.org/rfc/rfc2296.txt
http://www.ietf.org/rfc/rfc2365.txt
http://www.ietf.org/rfc/rfc2373.txt
http://www.ietf.org/rfc/rfc2396.txt
http://www.ietf.org/rfc/rfc2440.txt
http://www.ietf.org/rfc/rfc2474.txt
http://www.ietf.org/rfc/rfc2609.txt
http://www.ietf.org/rfc/rfc2616.txt
https://www.ietf.org/rfc/rfc2696.txt
http://www.ietf.org/rfc/rfc2710.txt
http://www.ietf.org/rfc/rfc2732.txt
http://www.ietf.org/rfc/rfc2743.txt
http://www.ietf.org/rfc/rfc2781.txt
http://www.ietf.org/rfc/rfc2782.txt
http://www.ietf.org/rfc/rfc2830.txt
http://www.ietf.org/rfc/rfc2831.txt
http://www.ietf.org/rfc/rfc2853.txt
http://www.ietf.org/rfc/rfc2891.txt
http://www.ietf.org/rfc/rfc2898.txt
http://www.ietf.org/rfc/rfc2965.txt
http://www.ietf.org/rfc/rfc3023.txt
http://www.ietf.org/rfc/rfc3111.txt
http://www.ietf.org/rfc/rfc3275.txt
http://www.ietf.org/rfc/rfc3279.txt
http://www.ietf.org/rfc/rfc3296.txt
http://www.ietf.org/rfc/rfc3330.txt
http://www.ietf.org/rfc/rfc3376.txt
http://www.ietf.org/rfc/rfc3454.txt
http://www.ietf.org/rfc/rfc3490.txt
http://www.ietf.org/rfc/rfc3491.txt
http://www.ietf.org/rfc/rfc3492.txt
http://www.ietf.org/rfc/rfc3530.txt
http://www.ietf.org/rfc/rfc3720.txt
http://www.ietf.org/rfc/rfc3720.txt.pdf
http://www.ietf.org/rfc/rfc3758.txt
http://www.ietf.org/rfc/rfc3810.txt
http://www.ietf.org/rfc/rfc3986.txt
http://www.ietf.org/rfc/rfc4120.txt
http://www.ietf.org/rfc/rfc4122.txt
http://www.ietf.org/rfc/rfc4366.txt
http://www.ietf.org/rfc/rfc4512.txt
http://www.ietf.org/rfc/rfc4648.txt
http://www.ietf.org/rfc/rfc5116.txt
http://www.ietf.org/rfc/rfc5280.txt
http://www.ietf.org/rfc/rfc5890.txt
http://www.ietf.org/rfc/rfc6066.txt
http://www.ietf.org/rfc/rfc7301.txt
http://www.ietf.org/rfc/rfc790.txt
http://www.ietf.org/rfc/rfc793.txt
http://www.ietf.org/rfc/rfc822.txt
http://www.ietf.org/rfc/rfc919.txt
http://www.info-zip.org/doc/appnote-19970311-iz.zip
http://www.ioplex.com/utilities/keytab.txt
http://www.iso.org/iso/home/standards/currency_codes.htm
http://www.jcp.org
http://www.jcp.org/en/jsr/detail?id=203
http://www.jpeg.org
http://www.libpng.org/pub/png/spec/
http://www.microsoft.com/typography/otspec/
http://www.midi.org
http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=dss
http://www.opengroup.org
http://www.oracle.com/technetwork/articles/java/mixing-components-433992.html
http://www.oracle.com/technetwork/java/architecture-142923.html
http://www.oracle.com/technetwork/java/javase/documentation/serialized-criteria-137781.html
http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html
http://www.oracle.com/technetwork/java/javase/javasecarootcertsprogram-1876540.html
http://www.oracle.com/technetwork/java/javase/tech/javamanagement-140525.html
http://www.oracle.com/technetwork/java/painting-140037.html
http://www.oracle.com/technetwork/java/persistence2-141443.html
http://www.oracle.com/technetwork/java/persistence3-139471.html
http://www.oracle.com/technetwork/java/persistence4-140124.html
http://www.oreilly.com/catalog/regex/
http://www.oreilly.com/catalog/regex3/
http://www.reactive-streams.org/
http://www.relaxng.org/
http://www.rfc-editor.org/rfc/bcp/bcp47.txt
http://www.saxproject.org
http://www.saxproject.org/
http://www.unicode.org
http://www.unicode.org/
http://www.unicode.org/glossary/
http://www.unicode.org/reports/tr15/
http://www.unicode.org/reports/tr18/
http://www.unicode.org/reports/tr24/
http://www.unicode.org/reports/tr27/
http://www.unicode.org/reports/tr36/
http://www.unicode.org/reports/tr44/
http://www.unicode.org/standard/standard.html
http://www.w3.org/2000/09/xmldsig
http://www.w3.org/2000/xmlns/
http://www.w3.org/2001/04/xmldsig-more
http://www.w3.org/2001/04/xmlenc
http://www.w3.org/2001/05/xmlschema-errata
http://www.w3.org/2001/10/xml-exc-c14n
http://www.w3.org/2002/06/xmldsig-filter2
http://www.w3.org/2007/05/xmldsig-more
http://www.w3.org/2009/xmldsig11
http://www.w3.org/2021/04/xmldsig-more
http://www.w3.org/Graphics/GIF/spec-gif89a.txt
http://www.w3.org/TR/1998/REC-CSS2-19980512
http://www.w3.org/TR/1999/REC-html401-19991224/
http://www.w3.org/TR/1999/REC-xml-names-19990114/
http://www.w3.org/TR/1999/REC-xpath-19991116
http://www.w3.org/TR/1999/REC-xslt-19991116
http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510
http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113
http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113
http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113
http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113
http://www.w3.org/TR/2000/REC-DOM-Level-2-Views-20001113
http://www.w3.org/TR/2001/REC-xml-c14n-20010315
http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107
http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109
http://www.w3.org/TR/2003/REC-SVG11-20030114/
http://www.w3.org/TR/2003/REC-xptr-framework-20030325/
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html
http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407
http://www.w3.org/TR/2004/REC-DOM-Level-3-Val-20040127/
http://www.w3.org/TR/2004/REC-xml-20040204
http://www.w3.org/TR/2004/REC-xml-infoset-20040204
http://www.w3.org/TR/2004/REC-xml-infoset-20040204/
http://www.w3.org/TR/2004/REC-xml-names11-20040204/
http://www.w3.org/TR/2004/REC-xml11-20040204/
http://www.w3.org/TR/DOM-Level-2
http://www.w3.org/TR/DOM-Level-2-Core/
http://www.w3.org/TR/DOM-Level-3-Core
http://www.w3.org/TR/DOM-Level-3-LS
http://www.w3.org/TR/ElementTraversal/
http://www.w3.org/TR/NOTE-datetime
http://www.w3.org/TR/REC-CSS1
http://www.w3.org/TR/REC-html32.html
http://www.w3.org/TR/REC-xml
http://www.w3.org/TR/REC-xml-names
http://www.w3.org/TR/REC-xml-names/
http://www.w3.org/TR/REC-xml/
http://www.w3.org/TR/html4/
http://www.w3.org/TR/html40/appendix/notes.html
http://www.w3.org/TR/xinclude/
http://www.w3.org/TR/xml-exc-c14n/
http://www.w3.org/TR/xml-names11/
http://www.w3.org/TR/xml-stylesheet/
http://www.w3.org/TR/xml11/
http://www.w3.org/TR/xmldsig-core/
http://www.w3.org/TR/xmldsig-filter2
http://www.w3.org/TR/xmldsig-filter2/
http://www.w3.org/TR/xmlschema-1
http://www.w3.org/TR/xmlschema-1/
http://www.w3.org/TR/xmlschema-2/
http://www.w3.org/TR/xpath
http://www.w3.org/TR/xpath-datamodel
http://www.w3.org/TR/xpath/
http://www.w3.org/TR/xslt
http://www.w3.org/XML/1998/namespace
http://www.w3.org/XML/Schema
http://www.w3.org/XML/xml-V10-2e-errata
http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html
http://www.w3.org/pub/WWW/Protocols/
http://xmlns.jcp.org/xml/ns//jdbc/webrowset.xsd
https://bugreport.java.com/bugreport/
https://bugs.openjdk.org/secure/attachment/75649/JVM_CodeHeap_StateAnalytics_V2.pdf
https://cldr.unicode.org/index/downloads
https://csrc.nist.gov/publications/PubsFIPS.html
https://csrc.nist.gov/publications/fips/archive/fips186-2/fips186-2.pdf
https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
https://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
https://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf
https://csrc.nist.gov/publications/fips/fips81/fips81.htm
https://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf
https://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
https://csrc.nist.gov/pubs/fips/203/final
https://csrc.nist.gov/pubs/fips/204/final
https://datatracker.ietf.org/doc/html/rfc5646
https://datatracker.ietf.org/doc/html/rfc8017
https://developer.apple.com/documentation
https://docs.oracle.com/en/java/javase/11/tools/java.html
https://docs.oracle.com/en/java/javase/12/language/index.html
https://docs.oracle.com/en/java/javase/12/tools/java.html
https://docs.oracle.com/en/java/javase/12/vm/compiler-control1.html
https://docs.oracle.com/en/java/javase/13/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/14/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/15/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/16/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/18/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/19/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/20/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/21/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/22/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/Double.html
https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/math/BigDecimal.html
https://docs.oracle.com/en/java/javase/23/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/24/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/@@JAVASE_VERSION@@/docs/api/java.base/java/lang/String.html
https://docs.oracle.com/en/java/javase/@@JAVASE_VERSION@@/docs/specs/javadoc/javadoc-search-spec.html
https://docs.oracle.com/en/java/javase/@@JAVASE_VERSION@@/docs/specs/man/javadoc.html
https://docs.oracle.com/en/java/javase/index.html
https://docs.oracle.com/javase/10/tools/java.htm
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html
https://docs.oracle.com/javase/9/tools/java.htm
https://docs.oracle.com/javase/specs/
https://docs.oracle.com/javase/specs/jls/se10/html/index.html
https://docs.oracle.com/javase/specs/jls/se11/html/index.html
https://docs.oracle.com/javase/specs/jls/se12/html/index.html
https://docs.oracle.com/javase/specs/jls/se13/html/index.html
https://docs.oracle.com/javase/specs/jls/se14/html/index.html
https://docs.oracle.com/javase/specs/jls/se15/html/index.html
https://docs.oracle.com/javase/specs/jls/se16/html/index.html
https://docs.oracle.com/javase/specs/jls/se17/html/index.html
https://docs.oracle.com/javase/specs/jls/se18/html/index.html
https://docs.oracle.com/javase/specs/jls/se19/html/index.html
https://docs.oracle.com/javase/specs/jls/se20/html/index.html
https://docs.oracle.com/javase/specs/jls/se21/html/index.html
https://docs.oracle.com/javase/specs/jls/se22/html/index.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-13.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-14.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-15.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-17.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-3.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-4.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-5.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-6.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-8.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-9.html
https://docs.oracle.com/javase/specs/jls/se23/html
https://docs.oracle.com/javase/specs/jls/se23/html/index.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-10.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-11.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-12.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-14.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-15.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-16.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-18.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-2.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-3.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-7.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-9.html
https://docs.oracle.com/javase/specs/jls/se24/html/index.html
https://docs.oracle.com/javase/specs/jls/se24/html/jls-9.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/index.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-10.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-11.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-12.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-13.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-14.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-15.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-17.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-18.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-3.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-4.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-5.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-6.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-7.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-8.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-9.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/jls@@JAVASE_VERSION@@.pdf
https://docs.oracle.com/javase/specs/jls/se6/html/j3TOC.html
https://docs.oracle.com/javase/specs/jls/se7/html/index.html
https://docs.oracle.com/javase/specs/jls/se8/html/index.html
https://docs.oracle.com/javase/specs/jls/se9/html/index.html
https://docs.oracle.com/javase/specs/jvms/se10/html/index.html
https://docs.oracle.com/javase/specs/jvms/se11/html/index.html
https://docs.oracle.com/javase/specs/jvms/se12/html/index.html
https://docs.oracle.com/javase/specs/jvms/se13/html/index.html
https://docs.oracle.com/javase/specs/jvms/se14/html/index.html
https://docs.oracle.com/javase/specs/jvms/se15/html/index.html
https://docs.oracle.com/javase/specs/jvms/se16/html/index.html
https://docs.oracle.com/javase/specs/jvms/se17/html/index.html
https://docs.oracle.com/javase/specs/jvms/se18/html/index.html
https://docs.oracle.com/javase/specs/jvms/se19/html/index.html
https://docs.oracle.com/javase/specs/jvms/se20/html/index.html
https://docs.oracle.com/javase/specs/jvms/se21/html/index.html
https://docs.oracle.com/javase/specs/jvms/se22/html/index.html
https://docs.oracle.com/javase/specs/jvms/se23/html
https://docs.oracle.com/javase/specs/jvms/se23/html/index.html
https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-5.html
https://docs.oracle.com/javase/specs/jvms/se24/html/index.html
https://docs.oracle.com/javase/specs/jvms/se24/html/jvms-4.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/index.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-1.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-2.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-3.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-4.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-5.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-6.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/jvms@@JAVASE_VERSION@@.pdf
https://docs.oracle.com/javase/specs/jvms/se7/html/index.html
https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
https://docs.oracle.com/javase/specs/jvms/se9/html/index.html
https://docs.oracle.com/javase/tutorial/
https://docs.oracle.com/javase/tutorial/2d/text/fonts.html
https://docs.oracle.com/javase/tutorial/extra/fullscreen/index.html
https://docs.oracle.com/javase/tutorial/index.html
https://docs.oracle.com/javase/tutorial/javabeans/
https://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html
https://docs.oracle.com/javase/tutorial/sound/
https://docs.oracle.com/javase/tutorial/uiswing/
https://docs.oracle.com/javase/tutorial/uiswing/components/applet.html
https://docs.oracle.com/javase/tutorial/uiswing/components/border.html
https://docs.oracle.com/javase/tutorial/uiswing/components/button.html
https://docs.oracle.com/javase/tutorial/uiswing/components/colorchooser.html
https://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html
https://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html
https://docs.oracle.com/javase/tutorial/uiswing/components/filechooser.html
https://docs.oracle.com/javase/tutorial/uiswing/components/frame.html
https://docs.oracle.com/javase/tutorial/uiswing/components/generaltext.html
https://docs.oracle.com/javase/tutorial/uiswing/components/icon.html
https://docs.oracle.com/javase/tutorial/uiswing/components/internalframe.html
https://docs.oracle.com/javase/tutorial/uiswing/components/jcomponent.html
https://docs.oracle.com/javase/tutorial/uiswing/components/label.html
https://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html
https://docs.oracle.com/javase/tutorial/uiswing/components/list.html
https://docs.oracle.com/javase/tutorial/uiswing/components/menu.html
https://docs.oracle.com/javase/tutorial/uiswing/components/panel.html
https://docs.oracle.com/javase/tutorial/uiswing/components/progress.html
https://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html
https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html
https://docs.oracle.com/javase/tutorial/uiswing/components/slider.html
https://docs.oracle.com/javase/tutorial/uiswing/components/spinner.html
https://docs.oracle.com/javase/tutorial/uiswing/components/splitpane.html
https://docs.oracle.com/javase/tutorial/uiswing/components/tabbedpane.html
https://docs.oracle.com/javase/tutorial/uiswing/components/table.html
https://docs.oracle.com/javase/tutorial/uiswing/components/text.html
https://docs.oracle.com/javase/tutorial/uiswing/components/textfield.html
https://docs.oracle.com/javase/tutorial/uiswing/components/toolbar.html
https://docs.oracle.com/javase/tutorial/uiswing/components/tooltip.html
https://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html
https://docs.oracle.com/javase/tutorial/uiswing/components/tree.html
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html
https://docs.oracle.com/javase/tutorial/uiswing/dnd/index.html
https://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/componentlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/containerlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/focuslistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/index.html
https://docs.oracle.com/javase/tutorial/uiswing/events/internalframelistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/itemlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/keylistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/mousemotionlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/treeexpansionlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/treemodellistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/treeselectionlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/treewillexpandlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/windowlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/index.html
https://docs.oracle.com/javase/tutorial/uiswing/layout/box.html
https://docs.oracle.com/javase/tutorial/uiswing/layout/spring.html
https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
https://docs.oracle.com/javase/tutorial/uiswing/misc/action.html
https://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html
https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=GUID-FE2D2E28-C991-4EF9-9DBE-2A4982726313
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=homepage
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=i18n_overview
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=imf_overview
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=jndi_ldap_gl_prop
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=jndi_overview
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=logging_overview
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=monitoring_and_management_using_jmx_technology
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=rmi_guide
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=secure_coding_guidelines_javase
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_impl_provider
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_jca
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_jca_provider
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_jdk_providers
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_jgss_tutorial
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_overview
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_pki
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_sasl
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_tools
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=serialization_filter_guide
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=serialver_tool_reference
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=using_jconsole
https://ftp.pwg.org/pub/pwg/candidates/cs-ippoutputbin10-20010207-5100.2.pdf
https://ftp.pwg.org/pub/pwg/standards/temp_archive/pwg5100.3.pdf
https://github.github.com/gfm/
https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles
https://html.spec.whatwg.org
https://html.spec.whatwg.org/multipage/
https://html.spec.whatwg.org/multipage/introduction.html
https://html.spec.whatwg.org/multipage/sections.html
https://html.spec.whatwg.org/multipage/semantics.html
https://jcp.org/aboutJava/communityprocess/maintenance/jsr924/index.html
https://jcp.org/aboutJava/communityprocess/maintenance/jsr924/index2.html
https://jcp.org/aboutJava/communityprocess/mrel/jsr160/index2.html
https://jcp.org/en/jsr/detail?id=14
https://jcp.org/en/jsr/detail?id=175
https://jcp.org/en/jsr/detail?id=201
https://jcp.org/en/jsr/detail?id=221
https://jcp.org/en/jsr/detail?id=269
https://jcp.org/en/jsr/detail?id=334
https://jcp.org/en/jsr/detail?id=335
https://jcp.org/en/jsr/detail?id=376
https://jcp.org/en/jsr/detail?id=41
https://jcp.org/en/procedures/jcp2
https://mermaid.js.org
https://msdn.microsoft.com/en-us/library/cc236621.aspx
https://msdn.microsoft.com/en-us/library/dd183391.aspx
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf
https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf
https://openjdk.org/jeps/11
https://openjdk.org/jeps/12
https://openjdk.org/jeps/181
https://openjdk.org/jeps/213
https://openjdk.org/jeps/225
https://openjdk.org/jeps/261
https://openjdk.org/jeps/286
https://openjdk.org/jeps/306
https://openjdk.org/jeps/323
https://openjdk.org/jeps/361
https://openjdk.org/jeps/371
https://openjdk.org/jeps/378
https://openjdk.org/jeps/394
https://openjdk.org/jeps/395
https://openjdk.org/jeps/396
https://openjdk.org/jeps/403
https://openjdk.org/jeps/409
https://openjdk.org/jeps/421
https://openjdk.org/jeps/440
https://openjdk.org/jeps/441
https://openjdk.org/jeps/454
https://openjdk.org/jeps/456
https://openjdk.org/jeps/458
https://openjdk.org/jeps/467
https://openjdk.org/jeps/478
https://openjdk.org/jeps/487
https://openjdk.org/jeps/488
https://openjdk.org/jeps/492
https://openjdk.org/jeps/494
https://openjdk.org/jeps/495
https://openjdk.org/jeps/499
https://prismjs.com
https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_addr.html
https://relaxng.org/
https://reproducible-builds.org/
https://spec.commonmark.org/0.31.2
https://spec.commonmark.org/0.31.2/
https://standards.ieee.org/ieee/754/6210/
https://standards.iso.org/ittf/PubliclyAvailableStandards/c055982_ISO_IEC_19757-3_2016.zip
https://standards.iso.org/ittf/PubliclyAvailableStandards/index.html
https://support.pkware.com/pkzip/appnote
https://tools.ietf.org/html/rfc1319
https://tools.ietf.org/html/rfc1321
https://tools.ietf.org/html/rfc1779
https://tools.ietf.org/html/rfc2040
https://tools.ietf.org/html/rfc2104
https://tools.ietf.org/html/rfc2195
https://tools.ietf.org/html/rfc2222
https://tools.ietf.org/html/rfc2246
https://tools.ietf.org/html/rfc2253
https://tools.ietf.org/html/rfc2595
https://tools.ietf.org/html/rfc2616
https://tools.ietf.org/html/rfc2712
https://tools.ietf.org/html/rfc2818
https://tools.ietf.org/html/rfc2830
https://tools.ietf.org/html/rfc2831
https://tools.ietf.org/html/rfc3217
https://tools.ietf.org/html/rfc3278
https://tools.ietf.org/html/rfc3394
https://tools.ietf.org/html/rfc3986
https://tools.ietf.org/html/rfc4086
https://tools.ietf.org/html/rfc4121
https://tools.ietf.org/html/rfc4162
https://tools.ietf.org/html/rfc4178
https://tools.ietf.org/html/rfc4234
https://tools.ietf.org/html/rfc4279
https://tools.ietf.org/html/rfc4346
https://tools.ietf.org/html/rfc4347
https://tools.ietf.org/html/rfc4492
https://tools.ietf.org/html/rfc4512
https://tools.ietf.org/html/rfc4647
https://tools.ietf.org/html/rfc4785
https://tools.ietf.org/html/rfc4960
https://tools.ietf.org/html/rfc5054
https://tools.ietf.org/html/rfc5061
https://tools.ietf.org/html/rfc5084
https://tools.ietf.org/html/rfc5246
https://tools.ietf.org/html/rfc5280
https://tools.ietf.org/html/rfc5288
https://tools.ietf.org/html/rfc5289
https://tools.ietf.org/html/rfc5469
https://tools.ietf.org/html/rfc5487
https://tools.ietf.org/html/rfc5489
https://tools.ietf.org/html/rfc5639
https://tools.ietf.org/html/rfc5646
https://tools.ietf.org/html/rfc5649
https://tools.ietf.org/html/rfc5746
https://tools.ietf.org/html/rfc5932
https://tools.ietf.org/html/rfc6209
https://tools.ietf.org/html/rfc6347
https://tools.ietf.org/html/rfc6367
https://tools.ietf.org/html/rfc6454
https://tools.ietf.org/html/rfc6455
https://tools.ietf.org/html/rfc6655
https://tools.ietf.org/html/rfc6931
https://tools.ietf.org/html/rfc7230
https://tools.ietf.org/html/rfc7231
https://tools.ietf.org/html/rfc7251
https://tools.ietf.org/html/rfc7292
https://tools.ietf.org/html/rfc7507
https://tools.ietf.org/html/rfc7539
https://tools.ietf.org/html/rfc7540
https://tools.ietf.org/html/rfc7748
https://tools.ietf.org/html/rfc7905
https://tools.ietf.org/html/rfc7919
https://tools.ietf.org/html/rfc8017
https://tools.ietf.org/html/rfc8018
https://tools.ietf.org/html/rfc8032
https://tools.ietf.org/html/rfc8103
https://tools.ietf.org/html/rfc8353
https://tools.ietf.org/html/rfc8422
https://tools.ietf.org/html/rfc8446
https://tools.ietf.org/html/rfc8554
https://tools.ietf.org/id/draft-kaukonen-cipher-arcfour-03.txt
https://tools.ietf.org/rfc/rfc5280.txt
https://tools.ietf.org/rfc/rfc8017.txt
https://unicode.org/reports/tr31/
https://unicode.org/reports/tr35/
https://unicode.org/reports/tr35/tr35-dates.html
https://unicode.org/reports/tr35/tr35-numbers.html
https://unicode.org/reports/tr51/
https://web.mit.edu/kerberos/
https://webhome.phy.duke.edu/~rgb/General/dieharder.php
https://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf
https://www.color.org
https://www.color.org/ICC1V42.pdf
https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml
https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
https://www.iana.org/time-zones
https://www.ietf.org/rfc/rfc2616.txt
https://www.ietf.org/rfc/rfc2818.txt
https://www.ietf.org/rfc/rfc6931.txt
https://www.ietf.org/rfc/rfc6943.html
https://www.iso.org/home.html
https://www.iso.org/iso-4217-currency-codes.html
https://www.iso.org/iso-8601-date-and-time-format.html
https://www.iso.org/standard/18114.html
https://www.itu.int/itudoc/itu-t/com16/tiff-fx/docs/tiff6.pdf
https://www.itu.int/rec/T-REC-X.509/en
https://www.netlib.org/fdlibm/
https://www.oasis-open.org
https://www.oasis-open.org/committees/download.php/14809/xml-catalogs.html
https://www.oracle.com/java/javase/terms/license/java@@JAVASE_VERSION@@speclicense.html
https://www.oracle.com/java/technologies/a-swing-architecture.html
https://www.oracle.com/java/technologies/javase/seccodeguide.html
https://www.oracle.com/java/technologies/javase/training-support.html
https://www.oracle.com/pls/topic/lookup?ctx=en/java/javase&id=security_guide_implement_provider_jca
https://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html
https://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html
https://www.oracle.com/technetwork/java/redist-137594.html
https://www.oracle.com/technetwork/java/seccodeguide-139067.html
https://www.owasp.org
https://www.rfc-editor.org/info/rfc1122
https://www.rfc-editor.org/info/rfc1123
https://www.rfc-editor.org/info/rfc1323
https://www.rfc-editor.org/info/rfc1349
https://www.rfc-editor.org/info/rfc1738
https://www.rfc-editor.org/info/rfc1779
https://www.rfc-editor.org/info/rfc1918
https://www.rfc-editor.org/info/rfc1950
https://www.rfc-editor.org/info/rfc1951
https://www.rfc-editor.org/info/rfc1952
https://www.rfc-editor.org/info/rfc2040
https://www.rfc-editor.org/info/rfc2045
https://www.rfc-editor.org/info/rfc2046
https://www.rfc-editor.org/info/rfc2109
https://www.rfc-editor.org/info/rfc2236
https://www.rfc-editor.org/info/rfc2246
https://www.rfc-editor.org/info/rfc2253
https://www.rfc-editor.org/info/rfc2268
https://www.rfc-editor.org/info/rfc2278
https://www.rfc-editor.org/info/rfc2279
https://www.rfc-editor.org/info/rfc2296
https://www.rfc-editor.org/info/rfc2306
https://www.rfc-editor.org/info/rfc2365
https://www.rfc-editor.org/info/rfc2368
https://www.rfc-editor.org/info/rfc2373
https://www.rfc-editor.org/info/rfc2396
https://www.rfc-editor.org/info/rfc2474
https://www.rfc-editor.org/info/rfc2560
https://www.rfc-editor.org/info/rfc2616
https://www.rfc-editor.org/info/rfc2710
https://www.rfc-editor.org/info/rfc2732
https://www.rfc-editor.org/info/rfc2781
https://www.rfc-editor.org/info/rfc2898
https://www.rfc-editor.org/info/rfc2911
https://www.rfc-editor.org/info/rfc2965
https://www.rfc-editor.org/info/rfc3279
https://www.rfc-editor.org/info/rfc3330
https://www.rfc-editor.org/info/rfc3376
https://www.rfc-editor.org/info/rfc3454
https://www.rfc-editor.org/info/rfc3490
https://www.rfc-editor.org/info/rfc3491
https://www.rfc-editor.org/info/rfc3492
https://www.rfc-editor.org/info/rfc3530
https://www.rfc-editor.org/info/rfc3720
https://www.rfc-editor.org/info/rfc3810
https://www.rfc-editor.org/info/rfc3986
https://www.rfc-editor.org/info/rfc4007
https://www.rfc-editor.org/info/rfc4086
https://www.rfc-editor.org/info/rfc4122
https://www.rfc-editor.org/info/rfc4234
https://www.rfc-editor.org/info/rfc4366
https://www.rfc-editor.org/info/rfc4512
https://www.rfc-editor.org/info/rfc4647
https://www.rfc-editor.org/info/rfc4648
https://www.rfc-editor.org/info/rfc5116
https://www.rfc-editor.org/info/rfc5280
https://www.rfc-editor.org/info/rfc5646
https://www.rfc-editor.org/info/rfc5869
https://www.rfc-editor.org/info/rfc5890
https://www.rfc-editor.org/info/rfc6066
https://www.rfc-editor.org/info/rfc7301
https://www.rfc-editor.org/info/rfc7539
https://www.rfc-editor.org/info/rfc790
https://www.rfc-editor.org/info/rfc793
https://www.rfc-editor.org/info/rfc8017
https://www.rfc-editor.org/info/rfc8032
https://www.rfc-editor.org/info/rfc822
https://www.rfc-editor.org/info/rfc919
https://www.rfc-editor.org/info/rfc9231
https://www.rfc-editor.org/rfc/rfc2315.txt
https://www.rfc-editor.org/rfc/rfc5208.html
https://www.rfc-editor.org/rfc/rfc5280.html
https://www.rfc-editor.org/rfc/rfc5646
https://www.rfc-editor.org/rfc/rfc5646.html
https://www.rfc-editor.org/rfc/rfc5869
https://www.rfc-editor.org/rfc/rfc6943.html
https://www.rfc-editor.org/rfc/rfc8017
https://www.rfc-editor.org/rfc/rfc8017.html
https://www.rfc-editor.org/rfc/rfc9180
https://www.schneier.com/blowfish.html
https://www.secg.org/sec2-v2.pdf
https://www.unicode.org/reports/tr15
https://www.unicode.org/reports/tr15/
https://www.unicode.org/reports/tr18
https://www.unicode.org/reports/tr24
https://www.unicode.org/reports/tr27
https://www.unicode.org/reports/tr29/
https://www.unicode.org/reports/tr31
https://www.unicode.org/reports/tr35
https://www.unicode.org/reports/tr35/
https://www.unicode.org/reports/tr35/tr35-collation.html
https://www.unicode.org/reports/tr35/tr35-dates.html
https://www.unicode.org/reports/tr35/tr35-general.html
https://www.unicode.org/reports/tr35/tr35.html
https://www.unicode.org/reports/tr36
https://www.unicode.org/reports/tr44
https://www.unicode.org/reports/tr44/
https://www.usno.navy.mil/USNO
https://www.usno.navy.mil/USNO/time/master-clock/systems-of-time
https://www.w3.org
https://www.w3.org/Daemon/User/Config/Logging.html
https://www.w3.org/TR/1998/REC-html40-19980424/
https://www.w3.org/TR/1999/REC-xpath-19991116/
https://www.w3.org/TR/2001/REC-xml-c14n-20010315
https://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/
https://www.w3.org/TR/2002/REC-xmldsig-filter2-20021108/
https://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html
https://www.w3.org/TR/CSS22
https://www.w3.org/TR/CSS22/syndata.html
https://www.w3.org/TR/DOM-Level-3-XPath/
https://www.w3.org/TR/NOTE-datetime
https://www.w3.org/TR/REC-CSS1
https://www.w3.org/TR/REC-html32.html
https://www.w3.org/TR/REC-xml-names/
https://www.w3.org/TR/html4
https://www.w3.org/TR/html52
https://www.w3.org/TR/html52/dom.html
https://www.w3.org/TR/html52/syntax.html
https://www.w3.org/TR/xml
https://www.w3.org/TR/xml-c14n11/
https://www.w3.org/TR/xml/
https://www.w3.org/TR/xmldsig-core/
https://www.w3.org/TR/xmlschema-2
https://www.w3.org/WAI/standards-guidelines/wcag/
https://www.w3.org/XML/Schema
https://www.w3.org/XML/xml-names-19990114-errata.html
https://www.wapforum.org/what/technical/SPEC-WAESpec-19990524.pdf

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, 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.
*
* 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 8337109
* @summary Check external links in the generated documentation
* @library /test/langtools/tools/lib ../../doccheck /test/lib ../../../../tools/tester
* @build DocTester toolbox.TestRunner
* @run main/othervm -Ddoccheck.checks=extlinks DocCheck
*/

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, 2025, 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.
*
* 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 8337109
* @summary Check the html in the generated documentation
* @library /test/langtools/tools/lib ../../doccheck /test/lib ../../../../tools/tester
* @build DocTester toolbox.TestRunner jtreg.SkippedException
* @run main/othervm -Ddoccheck.checks=html DocCheck
*/

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, 2025, 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.
*
* 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 8337109 8349369
* @summary Check Links in the generated documentation
* @library /test/langtools/tools/lib ../../doccheck /test/lib ../../../../tools/tester
* @build DocTester toolbox.TestRunner
* @run main/othervm -Ddoccheck.checks=links DocCheck
*/

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, 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.
*
* 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 8337109
* @summary Check doctype and character encoding in the generated documentation
* @library /test/langtools/tools/lib ../../doccheck /test/lib ../../../../tools/tester
* @build DocTester toolbox.TestRunner
* @run main/othervm -Ddoccheck.checks=doctype,badchars DocCheck
*/

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2024, 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.
*
* 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.
*/
package doccheckutils;
import java.io.Closeable;
/**
* Base class for {@link FileChecker file checkers} and
*/
public interface Checker extends Closeable {
/**
* Writes a report at the end of a run, to summarize the results of the
* checking done by this checker.
*/
void report();
boolean isOK();
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2024, 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.
*
* 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.
*/
package doccheckutils;
import java.nio.file.Path;
import java.util.List;
public interface FileChecker extends Checker {
void checkFiles(List<Path> files);
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2024, 2025, 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.
*
* 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.
*/
package doccheckutils;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
public class FileProcessor {
private final List<Path> files;
public FileProcessor() {
files = new ArrayList<>();
}
public List<Path> getFiles() {
return files;
}
public void processFiles(Path directory) {
try {
Files.walkFileTree(directory, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.toString().endsWith(".html"))
files.add(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new RuntimeException();
}
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2024, 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.
*
* 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.
*/
package doccheckutils;
import java.nio.file.Path;
import java.util.Map;
/**
* Base class for HTML checkers.
* <p>
* For details on HTML syntax and the terms used in this API, see
* W3C <a href="https://html.spec.whatwg.org/multipage/syntax.html#syntax">The HTML syntax</a>.
*/
public interface HtmlChecker extends Checker {
/**
* Starts checking a new file,
* <p>
* The file becomes the <em>current</em> file until {@link #endFile endFile}
* is called.
*
* @param path the file.
*/
void startFile(Path path);
/**
* Ends checking the current file.
*/
void endFile();
/**
* Checks the content of a {@code <?xml ... ?>} declaration in the
* current file.
*
* @param line the line number on which the declaration was found
* @param attrs the content of the declaration
*/
void xml(int line, Map<String, String> attrs);
/**
* Checks the content of a {@code <!doctype ... >} declaration in the
* current file.
*
* @param line the line number on which the declaration was found
* @param docType the content of the declaration
*/
void docType(int line, String docType);
/**
* Checks the start of an HTML tag in the current file.
*
* @param line the line number on which the start tag for an element was found
* @param name the name of the tag
* @param attrs the attributes of the tag
* @param selfClosing whether the tag is self-closing
*/
void startElement(int line, String name, Map<String, String> attrs, boolean selfClosing);
/**
* Checks the end of an HTML tag in the current file.
*
* @param line the line number on which the end tag for an element was found
* @param name the name of the tag
*/
void endElement(int line, String name);
/**
* Checks the content appearing in between HTML tags.
*
* @param line the line number on which the content was found
* @param content the content
*/
default void content(int line, String content) {
}
}

View file

@ -0,0 +1,389 @@
/*
* Copyright (c) 2024, 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.
*
* 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.
*/
package doccheckutils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Reads an HTML file, and calls a series of{@link HtmlChecker HTML checkers}
* for the HTML constructs found therein.
*/
public class HtmlFileChecker implements FileChecker {
private final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.IGNORE)
.onUnmappableCharacter(CodingErrorAction.IGNORE);
private final Log log;
private final HtmlChecker htmlChecker;
private Path path;
private BufferedReader in;
private int ch;
private int lineNumber;
private boolean inScript;
private boolean xml;
public HtmlFileChecker(HtmlChecker htmlChecker, Path BaseDir) {
this.log = new Log();
log.setBaseDirectory(BaseDir);
this.htmlChecker = htmlChecker;
}
@Override
public void checkFiles(List<Path> files) {
for (Path file : files) {
read(file);
}
}
@Override
public void report() {
System.err.println(log);
}
@Override
public void close() throws IOException {
// report();
htmlChecker.close();
}
private void read(Path path) {
try (BufferedReader r = new BufferedReader(
new InputStreamReader(Files.newInputStream(path), decoder))) {
this.path = path;
this.in = r;
StringBuilder content = new StringBuilder();
startFile(path);
try {
lineNumber = 1;
xml = false;
nextChar();
while (ch != -1) {
if (ch == '<') {
content(content.toString());
content.setLength(0);
html();
} else {
content.append((char) ch);
if (ch == '\n') {
content(content.toString());
content.setLength(0);
}
nextChar();
}
}
} finally {
endFile();
}
} catch (IOException e) {
log.log(path, lineNumber, e);
} catch (Throwable t) {
log.log(path, lineNumber, t);
log.log(String.valueOf(t));
}
}
private void startFile(Path path) {
htmlChecker.startFile(path);
}
private void endFile() {
htmlChecker.endFile();
}
private void docType(String s) {
htmlChecker.docType(lineNumber, s);
}
private void startElement(String name, Map<String, String> attrs, boolean selfClosing) {
htmlChecker.startElement(lineNumber, name, attrs, selfClosing);
}
private void endElement(String name) {
htmlChecker.endElement(lineNumber, name);
}
private void content(String s) {
htmlChecker.content(lineNumber, s);
}
private void nextChar() throws IOException {
ch = in.read();
if (ch == '\n')
lineNumber++;
}
/**
* Read the start or end of an HTML tag, or an HTML comment
* {@literal <identifier attrs> } or {@literal </identifier> }
*
* @throws IOException if there is a problem reading the file
*/
protected void html() throws IOException {
nextChar();
if (isIdentifierStart((char) ch)) {
String name = readIdentifier().toLowerCase(Locale.US);
Map<String, String> attrs = htmlAttrs();
if (attrs != null) {
boolean selfClosing = false;
if (ch == '/') {
nextChar();
selfClosing = true;
}
if (ch == '>') {
nextChar();
startElement(name, attrs, selfClosing);
if (name.equals("script")) {
inScript = true;
}
return;
}
}
} else if (ch == '/') {
nextChar();
if (isIdentifierStart((char) ch)) {
String name = readIdentifier().toLowerCase(Locale.US);
skipWhitespace();
if (ch == '>') {
nextChar();
endElement(name);
if (name.equals("script")) {
inScript = false;
}
return;
}
}
} else if (ch == '!') {
nextChar();
if (ch == '-') {
nextChar();
if (ch == '-') {
nextChar();
while (ch != -1) {
int dash = 0;
while (ch == '-') {
dash++;
nextChar();
}
// Strictly speaking, a comment should not contain "--"
// so dash > 2 is an error, dash == 2 implies ch == '>'
// See http://www.w3.org/TR/html-markup/syntax.html#syntax-comments
// for more details.
if (dash >= 2 && ch == '>') {
nextChar();
return;
}
nextChar();
}
}
} else if (ch == '[') {
nextChar();
if (ch == 'C') {
nextChar();
if (ch == 'D') {
nextChar();
if (ch == 'A') {
nextChar();
if (ch == 'T') {
nextChar();
if (ch == 'A') {
nextChar();
if (ch == '[') {
while (true) {
nextChar();
if (ch == ']') {
nextChar();
if (ch == ']') {
nextChar();
if (ch == '>') {
nextChar();
return;
}
}
}
}
}
}
}
}
}
}
} else {
StringBuilder sb = new StringBuilder();
while (ch != -1 && ch != '>') {
sb.append((char) ch);
nextChar();
}
Pattern p = Pattern.compile("(?is)doctype\\s+html\\s?.*");
String s = sb.toString();
if (p.matcher(s).matches()) {
xml = s.contains("XHTML");
docType(s);
return;
}
}
} else if (ch == '?') {
nextChar();
if (ch == 'x') {
nextChar();
if (ch == 'm') {
nextChar();
if (ch == 'l') {
nextChar();
if (ch == '?') {
nextChar();
if (ch == '>') {
nextChar();
xml = true;
return;
}
}
}
}
}
}
if (!inScript) {
log.log(path, lineNumber, "bad html");
}
}
/**
* Read a series of HTML attributes, terminated by {@literal > }.
* Each attribute is of the form {@literal identifier[=value] }.
* "value" may be unquoted, single-quoted, or double-quoted.
*/
protected Map<String, String> htmlAttrs() throws IOException {
Map<String, String> map = new LinkedHashMap<>();
skipWhitespace();
while (isIdentifierStart((char) ch)) {
String name = readAttributeName().toLowerCase(Locale.US);
skipWhitespace();
String value = null;
if (ch == '=') {
nextChar();
skipWhitespace();
if (ch == '\'' || ch == '"') {
char quote = (char) ch;
nextChar();
StringBuilder sb = new StringBuilder();
while (ch != -1 && ch != quote) {
// if (ch == '\n') {
// error(path, lineNumber, "unterminated string");
// // No point trying to read more.
// // In fact, all attrs get discarded by the caller
// // and superseded by a malformed.html node because
// // the html tag itself is not terminated correctly.
// break loop;
// }
sb.append((char) ch);
nextChar();
}
value = sb.toString() // hack to replace common entities
.replace("&lt;", "<")
.replace("&gt;", ">")
.replace("&amp;", "&");
nextChar();
} else {
StringBuilder sb = new StringBuilder();
while (ch != -1 && !isUnquotedAttrValueTerminator((char) ch)) {
sb.append((char) ch);
nextChar();
}
value = sb.toString();
}
skipWhitespace();
}
map.put(name, value);
}
return map;
}
protected boolean isIdentifierStart(char ch) {
return Character.isUnicodeIdentifierStart(ch);
}
protected String readIdentifier() throws IOException {
StringBuilder sb = new StringBuilder();
sb.append((char) ch);
nextChar();
while (ch != -1 && Character.isUnicodeIdentifierPart(ch)) {
sb.append((char) ch);
nextChar();
}
return sb.toString();
}
protected String readAttributeName() throws IOException {
StringBuilder sb = new StringBuilder();
sb.append((char) ch);
nextChar();
while ((ch != -1 && Character.isUnicodeIdentifierPart(ch))
|| ch == '-'
|| (xml && ch == ':')) {
sb.append((char) ch);
nextChar();
}
return sb.toString();
}
protected boolean isWhitespace(char ch) {
return Character.isWhitespace(ch);
}
protected void skipWhitespace() throws IOException {
while (isWhitespace((char) ch)) {
nextChar();
}
}
protected boolean isUnquotedAttrValueTerminator(char ch) {
return switch (ch) {
case '\f', '\n', '\r', '\t', ' ', '"', '\'', '`', '=', '<', '>' -> true;
default -> false;
};
}
@Override
public boolean isOK() {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2024, 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.
*
* 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.
*/
package doccheckutils;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class Log {
private final ArrayList<String> errors;
private Path baseDir;
public Log() {
errors = new ArrayList<>();
}
public List<String> getErrors() {
return errors;
}
public void log(Path path, int line, String message, Object... args) {
errors.add(formatErrorMessage(path, line, message, args));
}
public String formatErrorMessage(Path path, int line, String message, Object... args) {
return path + ":" + line + ": " + formatErrorMessage(message, args);
}
public String formatErrorMessage(Path path, int line, Throwable t) {
return path + ":" + line + ": " + t;
}
public String formatErrorMessage(Path path, Throwable t) {
return path + ": " + t;
}
public String formatErrorMessage(String message, Object... args) {
return String.format(message, args);
}
public void log(String message) {
errors.add(message);
}
public void log(Path path, int lineNumber, String s, int errorsOnLine) {
log(formatErrorMessage(path, lineNumber, s, errorsOnLine));
}
public void log(Path path, int line, Throwable t) {
log(formatErrorMessage(path, line, t));
}
public void log(Path path, Throwable t) {
log(formatErrorMessage(path, t));
}
public void log(String message, Object... args) {
log(formatErrorMessage(message, args));
}
public void setBaseDirectory(Path baseDir) {
this.baseDir = baseDir.toAbsolutePath();
}
public Path relativize(Path path) {
return baseDir != null && path.startsWith(baseDir) ? baseDir.relativize(path) : path;
}
public boolean noErrors() {
return errors.isEmpty();
}
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 2024, 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.
*
* 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.
*/
package doccheckutils.checkers;
import doccheckutils.FileChecker;
import doccheckutils.Log;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Checks the contents of an HTML file for bad/unmappable characters.
* <p>
* The file encoding is determined from the file contents.
*/
public class BadCharacterChecker implements FileChecker, AutoCloseable {
private static final Pattern doctype = Pattern.compile("(?i)<!doctype html>");
private static final Pattern metaCharset = Pattern.compile("(?i)<meta\\s+charset=\"([^\"]+)\">");
private static final Pattern metaContentType = Pattern.compile("(?i)<meta\\s+http-equiv=\"Content-Type\"\\s+content=\"text/html;charset=([^\"]+)\">");
private final Log errors;
private int files = 0;
private int badFiles = 0;
public BadCharacterChecker() {
errors = new Log();
}
public void checkFile(Path path) {
files++;
boolean ok = true;
try (InputStream in = new BufferedInputStream(Files.newInputStream(path))) {
CharsetDecoder d = getCharset(in).newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
BufferedReader r = new BufferedReader(new InputStreamReader(in, d));
int lineNumber = 0;
String line;
try {
while ((line = r.readLine()) != null) {
lineNumber++;
int errorsOnLine = 0;
for (int i = 0; i < line.length(); i++) {
char ch = line.charAt(i);
if (ch == 0xFFFD) {
errorsOnLine++;
}
}
if (errorsOnLine > 0) {
errors.log(path, lineNumber, "found %d invalid characters", errorsOnLine);
ok = false;
}
}
} catch (IOException e) {
errors.log(path, lineNumber, e);
ok = false;
}
} catch (IOException e) {
errors.log(path, e);
ok = false;
}
if (!ok)
badFiles++;
}
@Override
public void checkFiles(List<Path> files) {
for (Path file : files) {
checkFile(file);
}
}
private Charset getCharset(InputStream in) throws IOException {
CharsetDecoder initial = StandardCharsets.US_ASCII.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
in.mark(1024);
try {
BufferedReader r = new BufferedReader(new InputStreamReader(in, initial));
char[] buf = new char[1024];
int n = r.read(buf, 0, buf.length);
String head = new String(buf, 0, n);
boolean html5 = doctype.matcher(head).find();
Matcher m1 = metaCharset.matcher(head);
if (m1.find()) {
return Charset.forName(m1.group(1));
}
Matcher m2 = metaContentType.matcher(head);
if (m2.find()) {
return Charset.forName(m2.group(1));
}
return html5 ? StandardCharsets.UTF_8 : StandardCharsets.ISO_8859_1;
} finally {
in.reset();
}
}
@Override
public void report() {
if (!errors.noErrors() && files > 0) {
System.err.println("Bad characters found in the generated HTML");
System.err.println(MessageFormat.format(
"""
Bad Characters Report
{0} files read
{1} files contained bad characters"
{2} bad characters or other errors found
""",
files, badFiles, files));
for (String s : errors.getErrors()) {
System.err.println(s);
}
throw new RuntimeException("Bad character found in the generated HTML");
}
}
@Override
public boolean isOK() {
return errors.noErrors();
}
@Override
public void close() {
report();
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2024, 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.
*
* 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.
*/
package doccheckutils.checkers;
import doccheckutils.HtmlChecker;
import doccheckutils.Log;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Checks the DocType declared at the head of an HTML file.
*
* @see <a href="https://www.w3.org/TR/html5/syntax.html#syntax-doctype">
* W3C HTML5 8.1.1 The DOCTYPE</a>
*/
public class DocTypeChecker implements HtmlChecker {
private final Log log;
private final Map<String, Integer> counts = new HashMap<>();
private int html5;
private int html5_legacy;
private int xml;
private int other;
private Path path;
public DocTypeChecker() {
log = new Log();
}
@Override
public void startFile(Path path) {
this.path = path;
}
@Override
public void endFile() {
}
@Override
public void xml(int line, Map<String, String> attrs) {
xml++;
}
@Override
public void docType(int line, String docType) {
if (docType.equalsIgnoreCase("doctype html")) {
html5++;
} else {
Pattern p = Pattern.compile("(?i)doctype"
+ "\\s+html"
+ "\\s+([a-z]+)"
+ "\\s+(?:\"([^\"]+)\"|'([^']+)')"
+ "(?:\\s+(?:\"([^\"]+)\"|'([^']+)'))?"
+ "\\s*");
Matcher m = p.matcher(docType);
if (m.matches()) {
// See http://www.w3.org/tr/html52/syntax.html#the-doctype
if (m.group(1).equalsIgnoreCase("system")
&& m.group(2).equals("about:legacy-compat")) {
html5_legacy++;
} else {
String version = m.group(2);
List<String> allowedVersions = List.of(
"-//W3C//DTD XHTML 1.0 Strict//EN"
);
if (allowedVersions.stream().noneMatch(v -> v.equals(version))) {
log.log(path, line, "unexpected doctype: " + version);
}
counts.put(version, counts.getOrDefault(version, 0) + 1);
}
} else {
log.log(path, line, "doctype not recognized: " + docType);
other++;
}
}
}
@Override
public void startElement(int line, String name, Map<String, String> attrs, boolean selfClosing) {
}
@Override
public void endElement(int line, String name) {
}
@Override
public void report() {
log.log("DocType Report");
if (xml > 0) {
log.log("%6d: XHTML%n", xml);
}
if (html5 > 0) {
log.log("%6d: HTML5%n", html5);
}
if (html5_legacy > 0) {
log.log("%6d: HTML5 (legacy)%n", html5_legacy);
}
Map<Integer, Set<String>> sortedCounts = new TreeMap<>(Comparator.reverseOrder());
for (Map.Entry<String, Integer> e : counts.entrySet()) {
String s = e.getKey();
Integer n = e.getValue();
Set<String> set = sortedCounts.computeIfAbsent(n, k -> new TreeSet<>());
set.add(s);
}
for (Map.Entry<Integer, Set<String>> e : sortedCounts.entrySet()) {
for (String p : e.getValue()) {
log.log("%6d: %s%n", e.getKey(), p);
}
}
if (other > 0) {
log.log("%6d: other/unrecognized%n", other);
}
for (var line : log.getErrors()) {
System.err.println(line);
}
}
@Override
public boolean isOK() {
return counts.isEmpty() && (other == 0);
}
@Override
public void close() {
if (!isOK()) {
report();
throw new RuntimeException("Found HTML files with missing doctype declaration");
}
}
}

View file

@ -0,0 +1,220 @@
/*
* Copyright (c) 2024, 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.
*
* 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.
*/
package doccheckutils.checkers;
import doccheckutils.HtmlChecker;
import doccheckutils.Log;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* Checks the external links referenced in HTML files.
*/
public class ExtLinkChecker implements HtmlChecker, AutoCloseable {
private static final Path testBasePath = Path.of(System.getProperty("test.src"));
private static final Set<String> extLinks = new HashSet<>();
private static final String currentVersion = String.valueOf(Runtime.version().feature());
static {
String input = null;
try {
input = Files.readString(testBasePath.getParent().resolve("ExtLinksJdk.txt"));
} catch (IOException e) {
throw new RuntimeException(e);
}
extLinks.addAll(input.lines()
.filter(line -> !line.startsWith("#"))
.map(line -> line.replaceAll("\\@\\@JAVASE_VERSION\\@\\@", currentVersion))
.collect(Collectors.toUnmodifiableSet()));
}
private final Log log;
private final Map<URI, Set<Path>> allURIs;
private int badURIs;
private Path currFile;
public ExtLinkChecker() {
this.log = new Log();
allURIs = new TreeMap<>();
}
@Override
public void startFile(Path path) {
currFile = path.toAbsolutePath().normalize();
}
@Override
public void endFile() {
}
@Override
public void xml(int line, Map<String, String> attrs) {
}
@Override
public void docType(int line, String doctype) {
}
@Override
@SuppressWarnings("fallthrough")
public void startElement(int line, String name, Map<String, String> attrs, boolean selfClosing) {
switch (name) {
case "a":
case "link":
String href = attrs.get("href");
if (href != null) {
foundReference(line, href);
}
break;
}
}
@Override
public void endElement(int line, String name) {
}
private void foundReference(int line, String ref) {
try {
String uriPath = ref;
String fragment = null;
// The checker runs into a problem with links that have more than one hash character.
// You cannot create a URI unless the second hash is escaped.
int firstHashIndex = ref.indexOf('#');
int lastHashIndex = ref.lastIndexOf('#');
if (firstHashIndex != -1 && firstHashIndex != lastHashIndex) {
uriPath = ref.substring(0, firstHashIndex);
fragment = ref.substring(firstHashIndex + 1).replace("#", "%23");
} else if (firstHashIndex != -1) {
uriPath = ref.substring(0, firstHashIndex);
fragment = ref.substring(firstHashIndex + 1);
}
URI uri = new URI(uriPath);
if (fragment != null) {
uri = new URI(uri + "#" + fragment);
}
if (uri.isAbsolute()) {
if (Objects.equals(uri.getScheme(), "javascript")) {
// ignore JavaScript URIs
return;
}
String rawFragment = uri.getRawFragment();
URI noFrag = new URI(uri.toString().replaceAll("#\\Q" + rawFragment + "\\E$", ""));
allURIs.computeIfAbsent(noFrag, _ -> new LinkedHashSet<>()).add(currFile);
}
} catch (URISyntaxException e) {
log.log(currFile, line, "invalid URI: " + e);
}
}
@Override
public void report() {
checkURIs();
}
@Override
public boolean isOK() {
return badURIs == 0;
}
@Override
public void close() {
report();
}
private void checkURIs() {
System.err.println("ExtLinkChecker: checking external links");
allURIs.forEach(this::checkURI);
System.err.println("ExtLinkChecker: finished checking external links");
}
private void checkURI(URI uri, Set<Path> files) {
try {
switch (uri.getScheme()) {
case "ftp":
case "http":
case "https":
isVettedLink(uri, files);
break;
default:
warning(files, uri);
}
} catch (Throwable t) {
badURIs++;
error(files, uri, t);
}
}
private void isVettedLink(URI uri, Set<Path> files) {
if (!extLinks.contains(uri.toString())) {
System.err.println(MessageFormat.format("""
The external link {0} needs to be added to the whitelist test/docs/jdk/javadoc/doccheck/ExtLinksJdk.txt in order to be checked regularly\s
The link is present in:
{1}\n
""", uri, files.stream().map(Path::toString).collect(Collectors.joining("\n "))));
}
}
private void warning(Set<Path> files, Object... args) {
Iterator<Path> iter = files.iterator();
Path first = iter.next();
log.log(String.valueOf(first), "URI not supported: %s", args);
reportAlsoFoundIn(iter);
}
private void error(Set<Path> files, Object... args) {
Iterator<Path> iter = files.iterator();
Path first = iter.next();
log.log(String.valueOf(first), "Exception accessing uri: %s%n [%s]", args);
reportAlsoFoundIn(iter);
}
private void reportAlsoFoundIn(Iterator<Path> iter) {
int MAX_EXTRA = 10;
int n = 0;
while (iter.hasNext()) {
log.log(" Also found in %s", log.relativize(iter.next()));
if (n++ == MAX_EXTRA) {
int rest = 0;
while (iter.hasNext()) {
iter.next();
rest++;
}
log.log(" ... and %d more", rest);
}
}
}
}

View file

@ -0,0 +1,442 @@
/*
* Copyright (c) 2024, 2025, 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.
*
* 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.
*/
package doccheckutils.checkers;
import doccheckutils.HtmlChecker;
import doccheckutils.Log;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
/**
* Checks the links defined by and referenced in HTML files.
*/
public class LinkChecker implements HtmlChecker {
private final Log log;
private final Map<Path, IDTable> allFiles;
private final Map<URI, IDTable> allURIs;
// left for debugging
private final boolean checkInwardReferencesOnly = false;
private int files;
private int links;
private int duplicateIds;
private int missingFiles;
private int missingIds;
private int badSchemes;
private Path currFile;
private IDTable currTable;
private boolean html5;
public LinkChecker() {
this.log = new Log();
allFiles = new HashMap<>();
allURIs = new HashMap<>();
}
public void setBaseDir(Path dir) {
log.setBaseDirectory(dir);
}
@Override
public void startFile(Path path) {
currFile = path.toAbsolutePath().normalize();
currTable = allFiles.computeIfAbsent(currFile, p -> new IDTable(log.relativize(p)));
html5 = false;
files++;
}
@Override
public void endFile() {
currTable.check();
}
//unused
public List<Path> getUncheckedFiles() {
return allFiles.entrySet().stream()
.filter(e -> !e.getValue().checked
&& e.getKey().toString().endsWith(".html")
&& Files.exists(e.getKey()))
.map(Map.Entry::getKey)
.toList();
}
public List<Path> getMissingFiles() {
return allFiles.keySet().stream()
.filter(idTable -> !Files.exists(idTable)).toList();
}
@Override
public void xml(int line, Map<String, String> attrs) {
}
@Override
public void docType(int line, String doctype) {
html5 = doctype.matches("(?i)<\\?doctype\\s+html>");
}
@Override
@SuppressWarnings("fallthrough")
public void startElement(int line, String name, Map<String, String> attrs, boolean selfClosing) {
switch (name) {
case "a":
String nameAttr = html5 ? null : attrs.get("name");
if (nameAttr != null) {
foundAnchor(line, nameAttr);
}
// fallthrough
case "link":
String href = attrs.get("href");
if (href != null && !checkInwardReferencesOnly) {
foundReference(line, href);
}
break;
}
String idAttr = attrs.get("id");
if (idAttr != null) {
foundAnchor(line, idAttr);
}
}
@Override
public void endElement(int line, String name) {
}
@Override
public void content(int line, String content) {
HtmlChecker.super.content(line, content);
}
@Override
public void report() {
List<Path> pathList = getMissingFiles();
log.log("");
log.log("Link Checker Report");
if (!pathList.isEmpty()) {
log.log("");
log.log("Missing files: (" + pathList.size() + ")");
pathList.stream()
.sorted()
.forEach(this::reportMissingFile);
}
int anchors = 0;
for (IDTable t : allFiles.values()) {
anchors += (int) t.map.values().stream()
.filter(e -> !e.getReferences().isEmpty())
.count();
}
for (IDTable t : allURIs.values()) {
anchors += (int) t.map.values().stream()
.filter(e -> !e.references.isEmpty())
.count();
}
log.log("Checked " + files + " files.");
log.log("Found " + links + " references to " + anchors + " anchors "
+ "in " + allFiles.size() + " files and " + allURIs.size() + " other URIs.");
if (!pathList.isEmpty()) {
log.log("%6d missing files", pathList.size());
}
if (duplicateIds > 0) {
log.log("%6d duplicate ids", duplicateIds);
}
if (missingIds > 0) {
log.log("%6d missing ids", missingIds);
}
Map<String, Integer> hostCounts = new TreeMap<>(new HostComparator());
for (URI uri : allURIs.keySet()) {
String host = uri.getHost();
if (host != null) {
hostCounts.put(host, hostCounts.computeIfAbsent(host, h -> 0) + 1);
}
}
// if (hostCounts.size() > 0) {
// log.log("");
// log.log("Hosts");
// hostCounts.forEach((h, n) -> log.log("%6d %s", n, h));
// }
for (String message : log.getErrors()) {
System.err.println(message);
}
}
private void reportMissingFile(Path file) {
log.log(log.relativize(file).toString());
IDTable table = allFiles.get(file);
Set<Path> refs = new TreeSet<>();
for (IDInfo id : table.map.values()) {
if (id.references != null) {
for (Position ref : id.references) {
refs.add(ref.path);
}
}
}
int n = 0;
int MAX_REFS = 10;
for (Path ref : refs) {
log.log(" in " + log.relativize(ref));
if (++n == MAX_REFS) {
log.log(" ... and %d more", refs.size() - n);
break;
}
}
missingFiles++;
}
@Override
public boolean isOK() {
return log.noErrors() && (missingFiles == 0);
}
@Override
public void close() {
if (!log.noErrors()) {
report();
throw new RuntimeException("LinkChecker encountered errors; see log above.");
}
}
private void foundAnchor(int line, String name) {
currTable.addID(line, name);
}
private void foundReference(int line, String ref) {
links++;
try {
String uriPath = ref;
String fragment = null;
// The checker runs into a problem with links that have more than one hash character.
// You cannot create a URI unless the second hash is escaped.
int firstHashIndex = ref.indexOf('#');
int lastHashIndex = ref.lastIndexOf('#');
if (firstHashIndex != -1 && firstHashIndex != lastHashIndex) {
uriPath = ref.substring(0, firstHashIndex);
fragment = ref.substring(firstHashIndex + 1).replace("#", "%23");
} else if (firstHashIndex != -1) {
uriPath = ref.substring(0, firstHashIndex);
fragment = ref.substring(firstHashIndex + 1);
}
URI uri = new URI(uriPath);
if (fragment != null) {
uri = new URI(uri + "#" + fragment);
}
if (uri.isAbsolute()) {
foundReference(line, uri);
} else {
Path p;
String resolvedUriPath = uri.getPath();
if (resolvedUriPath == null || resolvedUriPath.isEmpty()) {
p = currFile;
} else {
p = currFile.getParent().resolve(resolvedUriPath).normalize();
}
if (!Files.exists(p)) {
log.log(currFile, line, "missing file reference: " + log.relativize(p));
return;
}
if (fragment != null && !fragment.isEmpty()) {
foundReference(line, p, fragment);
}
}
} catch (URISyntaxException e) {
System.err.println("Failed to create URI: " + ref);
log.log(currFile, line, "invalid URI: " + e);
}
}
private void foundReference(int line, Path p, String fragment) {
IDTable t = allFiles.computeIfAbsent(p, key -> new IDTable(log.relativize(key)));
t.addReference(fragment, currFile, line);
}
private void foundReference(int line, URI uri) {
if (!isSchemeOK(uri.getScheme()) && !checkInwardReferencesOnly) {
log.log(currFile, line, "bad scheme in URI");
badSchemes++;
}
String fragment = uri.getRawFragment();
if (fragment != null && !fragment.isEmpty()) {
try {
URI noFrag = new URI(uri.toString().replaceAll("#\\Q" + fragment + "\\E$", ""));
IDTable t = allURIs.computeIfAbsent(noFrag, IDTable::new);
t.addReference(fragment, currFile, line);
} catch (URISyntaxException e) {
throw new Error(e);
}
}
}
private boolean isSchemeOK(String uriScheme) {
if (uriScheme == null) {
return true;
}
return switch (uriScheme) {
case "ftp", "http", "https", "javascript" -> true;
default -> false;
};
}
static class Position implements Comparable<Position> {
Path path;
int line;
Position(Path path, int line) {
this.path = path;
this.line = line;
}
@Override
public int compareTo(Position o) {
int v = path.compareTo(o.path);
return v != 0 ? v : Integer.compare(line, o.line);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null || getClass() != obj.getClass()) {
return false;
} else {
final Position other = (Position) obj;
return Objects.equals(this.path, other.path)
&& this.line == other.line;
}
}
@Override
public int hashCode() {
return Objects.hashCode(path) * 37 + line;
}
}
static class IDInfo {
boolean declared;
Set<Position> references;
Set<Position> getReferences() {
return references == null ? Collections.emptySet() : references;
}
}
static class HostComparator implements Comparator<String> {
@Override
public int compare(String h1, String h2) {
List<String> l1 = new ArrayList<>(Arrays.asList(h1.split("\\.")));
Collections.reverse(l1);
String r1 = String.join(".", l1);
List<String> l2 = new ArrayList<>(Arrays.asList(h2.split("\\.")));
Collections.reverse(l2);
String r2 = String.join(".", l2);
return r1.compareTo(r2);
}
}
class IDTable {
private final Map<String, IDInfo> map = new HashMap<>();
private final String pathOrURI;
private boolean checked;
IDTable(Path path) {
this.pathOrURI = path.toString();
}
IDTable(URI uri) {
this.pathOrURI = uri.toString();
}
void addID(int line, String name) {
if (checked) {
throw new IllegalStateException("Adding ID after file has been checked");
}
Objects.requireNonNull(name);
IDInfo info = map.computeIfAbsent(name, _ -> new IDInfo());
if (info.declared) {
if (info.references != null || !checkInwardReferencesOnly) {
// don't report error if we're only checking inbound references
// and there are no references to this ID.
log.log(log.relativize(currFile), line, "name already declared: " + name);
duplicateIds++;
}
} else {
info.declared = true;
}
}
void addReference(String name, Path from, int line) {
if (checked) {
if (name != null) {
IDInfo id = map.get(name);
if (id == null || !id.declared) {
log.log(log.relativize(from), line,
"id not found: " + this.pathOrURI + "#" + name);
LinkChecker.this.missingIds++;
}
}
} else {
IDInfo id = map.computeIfAbsent(name, x -> new IDInfo());
if (id.references == null) {
id.references = new TreeSet<>();
}
id.references.add(new Position(from, line));
}
}
void check() {
map.forEach((name, id) -> {
if (name != null && !id.declared) {
for (Position ref : id.references) {
log.log(log.relativize(ref.path), ref.line,
"id not found: " + this.pathOrURI + "#" + name);
}
missingIds++;
}
});
checked = true;
}
}
}

View file

@ -0,0 +1,259 @@
/*
* Copyright (c) 2024, 2025, 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.
*
* 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.
*/
package doccheckutils.checkers;
import doccheckutils.FileChecker;
import doccheckutils.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jtreg.SkippedException;
public class TidyChecker implements FileChecker, AutoCloseable {
private final Path TIDY;
final Map<Pattern, Integer> counts = new HashMap<>();
final Pattern okPattern = Pattern.compile("No warnings or errors were found.");
final Pattern countPattern = Pattern.compile("([0-9]+) warnings, ([0-9]+) errors were found!.*?(Not all warnings/errors were shown.)?");
final Pattern countPattern2 = Pattern.compile("Tidy found ([0-9]+) warning[s]? and ([0-9]+) error[s]?!.*?(Not all warnings/errors were shown.)?");
final Pattern cssPattern = Pattern.compile("You are recommended to use CSS.*");
final Pattern guardPattern = Pattern.compile("(line [0-9]+ column [0-9]+ - |[^:]+:[0-9]+:[0-9]+: )(Error|Warning):.*");
final Pattern[] patterns = {
Pattern.compile(".*Error: <.*> is not recognized!"),
Pattern.compile(".*Error: missing quote mark for attribute value"),
Pattern.compile(".*Warning: '<' \\+ '/' \\+ letter not allowed here"),
Pattern.compile(".*Warning: <.*> anchor \".*\" already defined"),
Pattern.compile(".*Warning: <.*> attribute \".*\" has invalid value \".*\""),
Pattern.compile(".*Warning: <.*> attribute \".*\" lacks value"),
Pattern.compile(".*Warning: <.*> attribute \".*\" lacks value"),
Pattern.compile(".*Warning: <.*> attribute with missing trailing quote mark"),
Pattern.compile(".*Warning: <.*> dropping value \".*\" for repeated attribute \".*\""),
Pattern.compile(".*Warning: <.*> inserting \".*\" attribute"),
Pattern.compile(".*Warning: <.*> is probably intended as </.*>"),
Pattern.compile(".*Warning: <.*> isn't allowed in <.*> elements"),
Pattern.compile(".*Warning: <.*> lacks \".*\" attribute"),
Pattern.compile(".*Warning: <.*> missing '>' for end of tag"),
Pattern.compile(".*Warning: <.*> proprietary attribute \".*\""),
Pattern.compile(".*Warning: <.*> unexpected or duplicate quote mark"),
Pattern.compile(".*Warning: <a> id and name attribute value mismatch"),
Pattern.compile(".*Warning: <a> cannot copy name attribute to id"),
Pattern.compile(".*Warning: <a> escaping malformed URI reference"),
Pattern.compile(".*Warning: <blockquote> proprietary attribute \"pre\""),
Pattern.compile(".*Warning: discarding unexpected <.*>"),
Pattern.compile(".*Warning: discarding unexpected </.*>"),
Pattern.compile(".*Warning: entity \".*\" doesn't end in ';'"),
Pattern.compile(".*Warning: inserting implicit <.*>"),
Pattern.compile(".*Warning: inserting missing 'title' element"),
Pattern.compile(".*Warning: missing <!DOCTYPE> declaration"),
Pattern.compile(".*Warning: missing <.*>"),
Pattern.compile(".*Warning: missing </.*> before <.*>"),
Pattern.compile(".*Warning: nested emphasis <.*>"),
Pattern.compile(".*Warning: plain text isn't allowed in <.*> elements"),
Pattern.compile(".*Warning: removing whitespace preceding XML Declaration"),
Pattern.compile(".*Warning: replacing <p> (by|with) <br>"),
Pattern.compile(".*Warning: replacing invalid numeric character reference .*"),
Pattern.compile(".*Warning: replacing obsolete element <xmp> with <pre>"),
Pattern.compile(".*Warning: replacing unexpected .* (by|with) </.*>"),
Pattern.compile(".*Warning: trimming empty <.*>"),
Pattern.compile(".*Warning: unescaped & or unknown entity \".*\""),
Pattern.compile(".*Warning: unescaped & which should be written as &amp;"),
Pattern.compile(".*Warning: using <br> in place of <p>"),
Pattern.compile(".*Warning: <.*> element removed from HTML5"),
Pattern.compile(".*Warning: <.*> attribute \".*\" not allowed for HTML5"),
Pattern.compile(".*Warning: The summary attribute on the <table> element is obsolete in HTML5"),
Pattern.compile(".*Warning: replacing invalid UTF-8 bytes \\(char. code U\\+.*\\)")
};
private final Log errors;
private int files = 0;
private int ok;
private int warns;
private int errs;
private int css;
private int overflow;
public TidyChecker() {
TIDY = initTidy();
errors = new Log();
}
@Override
public void checkFiles(List<Path> sb) {
files += sb.size();
try {
for (int i = 0; i < sb.size(); i += 1024) {
List<String> command = new ArrayList<>();
command.add(TIDY.toString());
command.add("-q");
command.add("-e");
command.add("--gnu-emacs");
command.add("true");
List<Path> sublist = sb.subList(i, Math.min(i + 1024, sb.size()));
for (Path p : sublist) {
command.add(p.toString());
}
Process p = new ProcessBuilder()
.command(command)
.redirectErrorStream(true)
.start();
try (BufferedReader r =
new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = r.readLine()) != null) {
checkLine(line);
}
}
}
} catch (IOException e) {
throw new RuntimeException();
}
}
private Path initTidy() {
Path tidyExePath;
String tidyProperty = System.getProperty("tidy");
if (tidyProperty != null) {
tidyExePath = Path.of(tidyProperty);
if (!Files.exists(tidyExePath)) {
System.err.println("tidy not found: " + tidyExePath);
}
if (!Files.isExecutable(tidyExePath)) {
System.err.println("tidy not executable: " + tidyExePath);
}
} else {
boolean isWindows = System.getProperty("os.name")
.toLowerCase(Locale.US)
.startsWith("windows");
String tidyExe = isWindows ? "tidy.exe" : "tidy";
Optional<Path> p = Stream.of(System.getenv("PATH")
.split(File.pathSeparator))
.map(Path::of)
.map(d -> d.resolve(tidyExe))
.filter(Files::exists)
.filter(Files::isExecutable)
.findFirst();
if (p.isPresent()) {
tidyExePath = p.get();
} else {
throw new jtreg.SkippedException("tidy not found on PATH");
}
}
try {
Process p = new ProcessBuilder()
.command(tidyExePath.toString(), "-version")
.redirectErrorStream(true)
.start();
try (BufferedReader r =
new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
List<String> lines = r.lines().collect(Collectors.toList());
// Look for a line containing "version" and a dotted identifier beginning 5.
// If not found, look for known old/bad versions, to report in error message
Pattern version = Pattern.compile("version.* [5678]\\.\\d+(\\.\\d+)");
if (lines.stream().noneMatch(line -> version.matcher(line).find())) {
Pattern oldVersion = Pattern.compile("2006"); // 2006 implies old macOS version
String lineSep = System.lineSeparator();
String message = lines.stream().anyMatch(line -> oldVersion.matcher(line).find())
? "old version of 'tidy' found on the PATH\n"
: "could not determine the version of 'tidy' on the PATH\n";
System.err.println(message + String.join(lineSep, lines));
}
}
} catch (IOException e) {
System.err.println("Could not execute 'tidy -version': " + e);
}
return tidyExePath;
}
@Override
public void report() {
if (files > 0) {
System.err.println("Tidy found errors in the generated HTML");
if (!errors.noErrors()) {
for (String s : errors.getErrors()) {
System.err.println(s);
}
System.err.println("Tidy output end.");
System.err.println();
System.err.println();
throw new RuntimeException("Tidy found errors in the generated HTML");
}
}
}
@Override
public boolean isOK() {
return (ok == files)
&& (overflow == 0)
&& (errs == 0)
&& (warns == 0)
&& (css == 0);
}
void checkLine(String line) {
Matcher m;
if (okPattern.matcher(line).matches()) {
ok++;
} else if ((m = countPattern.matcher(line)).matches() || (m = countPattern2.matcher(line)).matches()) {
warns += Integer.parseInt(m.group(1));
errs += Integer.parseInt(m.group(2));
if (m.group(3) != null)
overflow++;
} else if (guardPattern.matcher(line).matches()) {
boolean found = false;
for (Pattern p : patterns) {
if (p.matcher(line).matches()) {
errors.log("%s", line);
found = true;
count(p);
break;
}
}
if (!found)
errors.log("unrecognized line: " + line);
} else if (cssPattern.matcher(line).matches()) {
css++;
}
}
void count(Pattern p) {
Integer i = counts.get(p);
counts.put(p, (i == null) ? 1 : i + 1);
}
@Override
public void close() {
report();
}
}