/* * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * 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 java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Optional; import java.util.Spliterator; import java.util.Spliterators; import java.util.regex.Pattern; import java.util.stream.Stream; import java.util.stream.StreamSupport; import javadoc.tester.JavadocTester; import toolbox.ToolBox; /* * @test * @bug 8318082 * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool * @build toolbox.ToolBox javadoc.tester.* * @run main TestSelfIndexing */ public class TestSelfIndexing extends JavadocTester { public static void main(String... args) throws Exception { new TestSelfIndexing().runTests(); } private final ToolBox tb = new ToolBox(); /* * Pages derived from other pages must not be indexed and may not * cross-reference each other except for navigation ergonomics. * * For example, it's okay for all-index.html to reference deprecated-list.html; * but it is not okay, for all-index.html to reference an anchor, such as * deprecated-list.html#java.lang.Object.finalize() */ @Test public void test(Path base) throws Exception { Path src = base.resolve("src"); int i = 0; // try to start a search tag (i) with the same letter, H, // as the class, Hello, and (ii) with some other letter, P for (var l : List.of("H", "P")) { // try all markup constructs that cause indexing for (var t : List.of("

%s

", "{@index %s}", "{@systemProperty %s}")) { tb.writeJavaFiles(src, """ package pkg; /** @deprecated %s */ public class Hello { } """.formatted(t.formatted(l))); Path out = base.resolve("out-" + i); checking(t.formatted(l) + "; results in: " + out); setAutomaticCheckNoStacktrace(true); // no exceptions javadoc("-d", out.toString(), "--source-path", src.toString(), "pkg"); // check that index pages do not refer to derived pages try (var s = findIndexFiles(out)) { record PathAndString(Path path, String str) { } Optional r = s.map(p -> { try { return new PathAndString(p, Files.readString(p)); } catch (IOException e) { throw new UncheckedIOException(e); } }) .flatMap(pac -> findLinksToDerivedPages(pac.str) .map(link -> new PathAndString(pac.path, link))) .findAny(); r.ifPresentOrElse(p -> failed(p.toString()), () -> passed(t.formatted(l))); } i++; } } } // ----------- support and infrastructure ----------- private static Stream findIndexFiles(Path start) throws IOException { return Files.find(start, Integer.MAX_VALUE, (path, attr) -> { if (attr.isDirectory()) return false; var fileName = path.getFileName().toString(); if (!fileName.endsWith(".html") && !fileName.endsWith(".js")) return false; if (!fileName.contains("-index") && !fileName.contains("index-")) return false; var underDocFiles = StreamSupport.stream(Spliterators.spliterator(path.iterator(), Integer.MAX_VALUE, Spliterator.ORDERED), false) .anyMatch(p -> p.equals(DOC_FILES)); return !underDocFiles; }); } private static final Path DOC_FILES = Path.of("doc-files"); // good enough to capture relevant parts of URLs that javadoc uses, // from html and js files alike private static final Pattern URL = Pattern.compile( "(?([a-zA-Z.%0-9-]+/)*+)(?[a-zA-Z.%0-9-]+\\.html)#[a-zA-Z.%0-9-]+"); static { assert findLinksToDerivedPages("module-summary.html#a").findAny().isEmpty(); assert findLinksToDerivedPages("package-summary.html#a").findAny().isEmpty(); assert findLinksToDerivedPages("Exception.html#a").findAny().isEmpty(); assert findLinksToDerivedPages("util/doc-files/coll-index.html#a").findAny().isEmpty(); assert findLinksToDerivedPages("util/doc-files/index-all.html#a").findAny().isEmpty(); // tricky assert findLinksToDerivedPages("index-all.html#a").findAny().isPresent(); assert findLinksToDerivedPages("index-17.html#a").findAny().isPresent(); } // NOTE: this will not find self-links that are allowed on some index pages. // For example, the quick-jump first-character links, such as #I:A, // #I:B, etc., on the top and at the bottom of index-all.html private static Stream findLinksToDerivedPages(String content) { return URL.matcher(content).results() .filter(r -> { String f = r.group("file"); if (!f.contains("-")) return false; return switch (f) { case "package-summary.html", "module-summary.html", "overview-summary.html", "help-doc.html" -> false; default -> { String p = r.group("path"); yield !p.contains("/doc-files/") && !p.startsWith("doc-files/"); } }; }) .map(r -> r.group(0)); } }