8162353: javadoc should provide a way to disable use of frames

Reviewed-by: bpatel, ksrini
This commit is contained in:
Jonathan Gibbons 2016-08-15 18:00:36 -07:00
parent 66ff418a4b
commit 9208c82732
14 changed files with 667 additions and 55 deletions

View File

@ -25,7 +25,7 @@
package jdk.javadoc.internal.doclets.formats.html;
import java.io.*;
import java.io.IOException;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
@ -89,25 +89,32 @@ public class AllClassesFrameWriter extends HtmlDocletWriter {
* "allclasses-frame.html" file. Generate the file in the current or the
* destination directory.
*
* @param indexbuilder IndexBuilder object for all classes index.
* @param indexBuilder IndexBuilder object for all classes index.
* @throws DocletAbortException
*/
public static void generate(ConfigurationImpl configuration,
IndexBuilder indexbuilder) {
AllClassesFrameWriter allclassgen;
DocPath filename = DocPaths.ALLCLASSES_FRAME;
IndexBuilder indexBuilder) {
if (configuration.frames) {
generate(configuration, indexBuilder, DocPaths.ALLCLASSES_FRAME, true);
generate(configuration, indexBuilder, DocPaths.ALLCLASSES_NOFRAME, false);
} else {
generate(configuration, indexBuilder, DocPaths.ALLCLASSES, false);
}
}
private static void generate(ConfigurationImpl configuration, IndexBuilder indexBuilder,
DocPath fileName, boolean wantFrames) {
try {
AllClassesFrameWriter allclassgen = new AllClassesFrameWriter(configuration,
fileName, indexBuilder);
allclassgen.buildAllClassesFile(wantFrames);
allclassgen = new AllClassesFrameWriter(configuration,
filename, indexbuilder);
allclassgen.buildAllClassesFile(true);
filename = DocPaths.ALLCLASSES_NOFRAME;
allclassgen = new AllClassesFrameWriter(configuration,
filename, indexbuilder);
fileName, indexBuilder);
allclassgen.buildAllClassesFile(false);
} catch (IOException exc) {
Messages messages = configuration.getMessages();
messages.error("doclet.exception_encountered",
exc.toString(), filename);
exc.toString(), fileName);
throw new DocletAbortException(exc);
}
}

View File

@ -197,6 +197,12 @@ public class ConfigurationImpl extends Configuration {
*/
public boolean createoverview = false;
/**
* Specifies whether or not frames should be generated.
* Defaults to true; can be set by --frames; can be set to false by --no-frames; last one wins.
*/
public boolean frames = true;
/**
* This is the HTML version of the generated pages. HTML 4.01 is the default output version.
*/
@ -414,18 +420,18 @@ public class ConfigurationImpl extends Configuration {
* package to document. It will be a class page(first in the sorted order),
* if only classes are provided on the command line.
*
* @param root Root of the program structure.
* @param docEnv Root of the program structure.
*/
protected void setTopFile(DocletEnvironment root) {
if (!checkForDeprecation(root)) {
protected void setTopFile(DocletEnvironment docEnv) {
if (!checkForDeprecation(docEnv)) {
return;
}
if (createoverview) {
topFile = DocPaths.OVERVIEW_SUMMARY;
topFile = DocPaths.overviewSummary(frames);
} else {
if (packages.size() == 1 && packages.first().isUnnamed()) {
if (!root.getIncludedClasses().isEmpty()) {
List<TypeElement> classes = new ArrayList<>(root.getIncludedClasses());
if (!docEnv.getIncludedClasses().isEmpty()) {
List<TypeElement> classes = new ArrayList<>(docEnv.getIncludedClasses());
TypeElement te = getValidClass(classes);
topFile = DocPath.forClass(utils, te);
}
@ -727,6 +733,22 @@ public class ConfigurationImpl extends Configuration {
return true;
}
},
new Option(this, "--frames") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
frames = true;
return true;
}
},
new Option(this, "--no-frames") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
frames = false;
return true;
}
},
new Hidden(this, "-packagesheader", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {

View File

@ -141,7 +141,7 @@ public class HelpWriter extends HtmlDocletWriter {
? HtmlTree.SECTION(overviewHeading)
: HtmlTree.LI(HtmlStyle.blockList, overviewHeading);
Content line3 = contents.getContent("doclet.Help_line_3",
getHyperLink(DocPaths.OVERVIEW_SUMMARY,
getHyperLink(DocPaths.overviewSummary(configuration.frames),
configuration.getText("doclet.Overview")));
Content overviewPara = HtmlTree.P(line3);
htmlTree.addContent(overviewPara);
@ -362,26 +362,31 @@ public class HelpWriter extends HtmlDocletWriter {
} else {
ul.addContent(htmlTree);
}
Content frameHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING,
contents.getContent("doclet.Help_line_25"));
htmlTree = (configuration.allowTag(HtmlTag.SECTION))
? HtmlTree.SECTION(frameHead)
: HtmlTree.LI(HtmlStyle.blockList, frameHead);
Content line26 = contents.getContent("doclet.Help_line_26");
Content framePara = HtmlTree.P(line26);
htmlTree.addContent(framePara);
if (configuration.frames) {
Content frameHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING,
contents.getContent("doclet.Help_line_25"));
htmlTree = (configuration.allowTag(HtmlTag.SECTION))
? HtmlTree.SECTION(frameHead)
: HtmlTree.LI(HtmlStyle.blockList, frameHead);
Content line26 = contents.getContent("doclet.Help_line_26");
Content framePara = HtmlTree.P(line26);
htmlTree.addContent(framePara);
}
if (configuration.allowTag(HtmlTag.SECTION)) {
ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree));
} else {
ul.addContent(htmlTree);
}
Content allclassesHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING,
contents.allClassesLabel);
htmlTree = (configuration.allowTag(HtmlTag.SECTION))
? HtmlTree.SECTION(allclassesHead)
: HtmlTree.LI(HtmlStyle.blockList, allclassesHead);
Content line27 = contents.getContent("doclet.Help_line_27",
getHyperLink(DocPaths.ALLCLASSES_NOFRAME,
getHyperLink(DocPaths.AllClasses(configuration.frames),
resources.getText("doclet.All_Classes")));
Content allclassesPara = HtmlTree.P(line27);
htmlTree.addContent(allclassesPara);

View File

@ -106,7 +106,7 @@ public class HtmlDoclet extends AbstractDoclet {
*
* For new format.
*
* @see jdk.doclet.RootDoc
* @see jdk.doclet.DocletEnvironment
*/
@Override // defined by AbstractDoclet
protected void generateOtherFiles(DocletEnvironment docEnv, ClassTree classtree)
@ -149,7 +149,9 @@ public class HtmlDoclet extends AbstractDoclet {
AllClassesFrameWriter.generate(configuration,
new IndexBuilder(configuration, nodeprecated, true));
FrameOutputWriter.generate(configuration);
if (configuration.frames) {
FrameOutputWriter.generate(configuration);
}
if (configuration.createoverview) {
if (configuration.showModules) {
@ -158,6 +160,11 @@ public class HtmlDoclet extends AbstractDoclet {
PackageIndexWriter.generate(configuration);
}
}
if (!configuration.frames && !configuration.createoverview) {
IndexRedirectWriter.generate(configuration);
}
if (configuration.helpfile.length() == 0 &&
!configuration.nohelp) {
HelpWriter.generate(configuration);
@ -270,13 +277,17 @@ public class HtmlDoclet extends AbstractDoclet {
@Override // defined by AbstractDoclet
protected void generateModuleFiles() throws Exception {
if (configuration.showModules) {
ModuleIndexFrameWriter.generate(configuration);
if (configuration.frames) {
ModuleIndexFrameWriter.generate(configuration);
}
ModuleElement prevModule = null, nextModule;
List<ModuleElement> mdles = new ArrayList<>(configuration.modulePackages.keySet());
int i = 0;
for (ModuleElement mdle : mdles) {
ModulePackageIndexFrameWriter.generate(configuration, mdle);
ModuleFrameWriter.generate(configuration, mdle);
if (configuration.frames) {
ModulePackageIndexFrameWriter.generate(configuration, mdle);
ModuleFrameWriter.generate(configuration, mdle);
}
nextModule = (i + 1 < mdles.size()) ? mdles.get(i + 1) : null;
AbstractBuilder moduleSummaryBuilder =
configuration.getBuilderFactory().getModuleSummaryBuilder(
@ -304,7 +315,7 @@ public class HtmlDoclet extends AbstractDoclet {
@Override // defined by AbstractDoclet
protected void generatePackageFiles(ClassTree classtree) throws Exception {
Set<PackageElement> packages = configuration.packages;
if (packages.size() > 1) {
if (packages.size() > 1 && configuration.frames) {
PackageIndexFrameWriter.generate(configuration);
}
List<PackageElement> pList = new ArrayList<>(packages);
@ -315,7 +326,9 @@ public class HtmlDoclet extends AbstractDoclet {
// and package-tree.html pages for that package.
PackageElement pkg = pList.get(i);
if (!(configuration.nodeprecated && utils.isDeprecated(pkg))) {
PackageFrameWriter.generate(configuration, pkg);
if (configuration.frames) {
PackageFrameWriter.generate(configuration, pkg);
}
int nexti = i + 1;
PackageElement next = null;
if (nexti < pList.size()) {

View File

@ -615,12 +615,13 @@ public class HtmlDocletWriter extends HtmlDocWriter {
} else {
tree.addContent(navDiv);
}
Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious());
ulNav.addContent(getNavLinkNext());
Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious(), getNavLinkNext());
Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav);
Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists());
ulFrames.addContent(getNavHideLists(filename));
subDiv.addContent(ulFrames);
if (configuration.frames) {
Content ulFrames = HtmlTree.UL(HtmlStyle.navList,
getNavShowLists(), getNavHideLists(filename));
subDiv.addContent(ulFrames);
}
HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex());
ulAllClasses.addAttr(HtmlAttr.ID, allClassesId);
subDiv.addContent(ulAllClasses);
@ -688,7 +689,7 @@ public class HtmlDocletWriter extends HtmlDocWriter {
* @return a content tree for the link
*/
protected Content getNavLinkContents() {
Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY),
Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.overviewSummary(configuration.frames)),
contents.overviewLabel, "", "");
Content li = HtmlTree.LI(linkContent);
return li;
@ -875,7 +876,7 @@ public class HtmlDocletWriter extends HtmlDocWriter {
*/
protected Content getNavLinkClassIndex() {
Content allClassesContent = getHyperLink(pathToRoot.resolve(
DocPaths.ALLCLASSES_NOFRAME),
DocPaths.AllClasses(configuration.frames)),
contents.allClassesLabel, "", "");
Content li = HtmlTree.LI(allClassesContent);
return li;

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 jdk.javadoc.internal.doclets.formats.html;
import java.io.IOException;
import jdk.javadoc.internal.doclets.formats.html.markup.Comment;
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
import jdk.javadoc.internal.doclets.formats.html.markup.DocType;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.Messages;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException;
import static jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocWriter.CONTENT_TYPE;
/**
* Writes an index.html file that tries to redirect to an alternate page.
* The redirect uses JavaSCript, if enabled, falling back on
* {@code <meta http-eqiv=refresh content="0,<uri>">}.
* If neither are supported/enabled in a browser, the page displays the
* standard "JavaScipt not enabled" message, and a link to the alternate page.
*/
public class IndexRedirectWriter extends HtmlDocletWriter {
public static void generate(ConfigurationImpl configuration) {
IndexRedirectWriter indexRedirect;
DocPath filename = DocPath.empty;
try {
filename = DocPaths.INDEX;
indexRedirect = new IndexRedirectWriter(configuration, filename);
indexRedirect.generateIndexFile();
} catch (IOException exc) {
Messages messages = configuration.getMessages();
messages.error(
"doclet.exception_encountered",
exc.toString(), filename);
throw new DocletAbortException(exc);
}
}
IndexRedirectWriter(ConfigurationImpl configuration, DocPath filename)
throws IOException {
super(configuration, filename);
}
void generateIndexFile() throws IOException {
Content htmlDocType = configuration.isOutputHtml5()
? DocType.HTML5
: DocType.TRANSITIONAL;
Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
Content head = new HtmlTree(HtmlTag.HEAD);
head.addContent(getGeneratedBy(!configuration.notimestamp));
String title = (configuration.windowtitle.length() > 0)
? configuration.windowtitle
: configuration.getText("doclet.Generated_Docs_Untitled");
Content windowTitle = HtmlTree.TITLE(new StringContent(title));
head.addContent(windowTitle);
Content metaContentType = HtmlTree.META("Content", CONTENT_TYPE,
(configuration.charset.length() > 0) ?
configuration.charset : HtmlConstants.HTML_DEFAULT_CHARSET);
head.addContent(metaContentType);
String topFilePath = configuration.topFile.getPath();
String javaScriptRefresh = "window.location.replace('" + topFilePath + "')";
HtmlTree scriptTree = HtmlTree.SCRIPT();
scriptTree.addContent(javaScriptRefresh);
head.addContent(scriptTree);
HtmlTree metaRefresh = new HtmlTree(HtmlTag.META);
metaRefresh.addAttr(HtmlAttr.HTTP_EQUIV, "Refresh");
metaRefresh.addAttr(HtmlAttr.CONTENT, "0;" + topFilePath);
if (configuration.isOutputHtml5()) {
head.addContent(HtmlTree.NOSCRIPT(metaRefresh));
} else {
head.addContent(metaRefresh);
}
head.addContent(getStyleSheetProperties(configuration));
ContentBuilder bodyContent = new ContentBuilder();
bodyContent.addContent(HtmlTree.NOSCRIPT(
HtmlTree.P(configuration.getContent("doclet.No_Script_Message"))));
bodyContent.addContent(HtmlTree.P(HtmlTree.A(topFilePath, new StringContent(topFilePath))));
Content body = new HtmlTree(HtmlTag.BODY);
if (configuration.allowTag(HtmlTag.MAIN)) {
HtmlTree main = HtmlTree.MAIN(bodyContent);
body.addContent(main);
} else {
body.addContent(bodyContent);
}
Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
head, body);
Content htmlDocument = new HtmlDocument(htmlDocType,
htmlComment, htmlTree);
write(htmlDocument);
}
}

View File

@ -88,7 +88,7 @@ public class ModuleIndexWriter extends AbstractModuleIndexWriter {
*/
public static void generate(ConfigurationImpl configuration) {
ModuleIndexWriter mdlgen;
DocPath filename = DocPaths.OVERVIEW_SUMMARY;
DocPath filename = DocPaths.overviewSummary(configuration.frames);
try {
mdlgen = new ModuleIndexWriter(configuration, filename);
mdlgen.buildModuleIndexFile("doclet.Window_Overview_Summary", true);

View File

@ -101,7 +101,7 @@ public class PackageIndexWriter extends AbstractPackageIndexWriter {
*/
public static void generate(ConfigurationImpl configuration) {
PackageIndexWriter packgen;
DocPath filename = DocPaths.OVERVIEW_SUMMARY;
DocPath filename = DocPaths.overviewSummary(configuration.frames);
try {
packgen = new PackageIndexWriter(configuration, filename);
packgen.buildPackageIndexFile("doclet.Window_Overview_Summary", true);

View File

@ -833,13 +833,18 @@ public class HtmlTree extends Content {
* Generates a UL tag with the style class attribute and some content.
*
* @param styleClass style for the tag
* @param body content for the tag
* @param first initial content to be added
* @param more a series of additional content nodes to be added
* @return an HtmlTree object for the UL tag
*/
public static HtmlTree UL(HtmlStyle styleClass, Content body) {
HtmlTree htmltree = new HtmlTree(HtmlTag.UL, nullCheck(body));
htmltree.addStyle(nullCheck(styleClass));
return htmltree;
public static HtmlTree UL(HtmlStyle styleClass, Content first, Content... more) {
HtmlTree htmlTree = new HtmlTree(HtmlTag.UL);
htmlTree.addContent(nullCheck(first));
for (Content c : more) {
htmlTree.addContent(nullCheck(c));
}
htmlTree.addStyle(nullCheck(styleClass));
return htmlTree;
}
/**

View File

@ -299,6 +299,10 @@ doclet.usage.stylesheetfile.description=File to change style of the generated\n\
doclet.usage.docencoding.parameters=<name>
doclet.usage.docencoding.description=Specify the character encoding for the output
doclet.usage.frames.description=Enable the use of frames in the generated output (default)
doclet.usage.no-frames.description=Disable the use of frames in the generated output
doclet.xusage.xdocrootparent.parameters=<url>
doclet.xusage.xdocrootparent.description=Replaces all @docRoot followed by /..\n\
\ in doc comments with <url>

View File

@ -37,6 +37,8 @@ import javax.lang.model.element.ModuleElement;
*
*/
public class DocPaths {
/** The name of the file for all classes, without using frames, when --no-frames is specified. */
public static final DocPath ALLCLASSES = DocPath.create("allclasses.html");
/** The name of the file for all classes, using frames. */
public static final DocPath ALLCLASSES_FRAME = DocPath.create("allclasses-frame.html");
@ -44,6 +46,10 @@ public class DocPaths {
/** The name of the file for all classes, without using frames. */
public static final DocPath ALLCLASSES_NOFRAME = DocPath.create("allclasses-noframe.html");
public static DocPath AllClasses(boolean frames) {
return frames ? ALLCLASSES_NOFRAME : ALLCLASSES;
}
/** The name of the sub-directory for storing class usage info. */
public static final DocPath CLASS_USE = DocPath.create("class-use");
@ -121,6 +127,10 @@ public class DocPaths {
/** The name of the file for the overview summary. */
public static final DocPath OVERVIEW_SUMMARY = DocPath.create("overview-summary.html");
public static DocPath overviewSummary(boolean frames) {
return frames ? OVERVIEW_SUMMARY : INDEX;
}
/** The name of the file for the overview tree. */
public static final DocPath OVERVIEW_TREE = DocPath.create("overview-tree.html");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2016, 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
@ -40,6 +40,7 @@ import java.lang.reflect.Method;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
@ -157,7 +158,7 @@ public abstract class JavadocTester {
private final Map<File,SoftReference<String>> fileContentCache = new HashMap<>();
/** Stream used for logging messages. */
private final PrintStream out = System.out;
protected final PrintStream out = System.out;
/** The directory containing the source code for the test. */
public static final String testSrc = System.getProperty("test.src");
@ -416,6 +417,17 @@ public abstract class JavadocTester {
* @param paths the files to check, within the most recent output directory.
* */
public void checkFiles(boolean expectedFound, String... paths) {
checkFiles(expectedFound, Arrays.asList(paths));
}
/**
* Check for files in (or not in) the generated output.
* @param expectedFound true if all of the files are expected
* to be found, or false if all of the files are expected to be
* not found
* @param paths the files to check, within the most recent output directory.
* */
public void checkFiles(boolean expectedFound, Collection<String> paths) {
for (String path: paths) {
// log.logCheckFile(path, expectedFound);
checking("checkFile");
@ -574,7 +586,7 @@ public abstract class JavadocTester {
return content;
content = new String(Files.readAllBytes(file.toPath()));
fileContentCache.put(file, new SoftReference(content));
fileContentCache.put(file, new SoftReference<>(content));
return content;
} catch (FileNotFoundException e) {
System.err.println(e);
@ -613,16 +625,19 @@ public abstract class JavadocTester {
* Print a summary of the test results.
*/
protected void printSummary() {
// log.write();
String javadocRuns = (javadocRunNum <= 1) ? ""
: ", in " + javadocRunNum + " runs of javadoc";
if (numTestsRun != 0 && numTestsPassed == numTestsRun) {
// Test passed
out.println();
out.println("All " + numTestsPassed + " subtests passed");
out.println("All " + numTestsPassed + " subtests passed" + javadocRuns);
} else {
// Test failed
throw new Error((numTestsRun - numTestsPassed)
+ " of " + (numTestsRun)
+ " subtests failed");
+ " subtests failed"
+ javadocRuns);
}
}

View File

@ -0,0 +1,397 @@
/*
* Copyright (c) 2016, 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 8162353
* @summary javadoc should provide a way to disable use of frames
* @library /tools/lib ../lib
* @modules
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* jdk.javadoc/jdk.javadoc.internal.tool
* @build toolbox.ModuleBuilder toolbox.ToolBox
* @build JavadocTester
* @run main TestFramesNoFrames
*/
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Collectors;
import toolbox.ModuleBuilder;
import toolbox.ToolBox;
public class TestFramesNoFrames extends JavadocTester {
public static void main(String... args) throws Exception {
TestFramesNoFrames tester = new TestFramesNoFrames();
tester.generateSource();
tester.runTests();
}
ToolBox tb = new ToolBox();
Path gensrcModules = Paths.get("gensrc/modules");
Path gensrcPackages = Paths.get("gensrc/packages");
void generateSource() throws IOException {
String[] modules = { "", "m1", "m2", "m3" };
String[] packages = { "p1", "p2", "p3" };
String[] classes = { "C1", "C2", "C3" };
for (String m: modules) {
ModuleBuilder mb = m.equals("") ? null : new ModuleBuilder(tb, m);
for (String p: packages) {
Path pkgRoot;
if (m.equals("")) {
pkgRoot = gensrcPackages;
} else {
pkgRoot = gensrcModules.resolve(m);
mb.exports(m + p);
}
for (String c: classes) {
tb.writeJavaFiles(pkgRoot,
"package " + (m + p) + ";\n"
+ "/** class " + (m + p + c).toUpperCase() + ". */\n"
+ "public class " + (m + p + c).toUpperCase() + " { }"
);
}
}
if (!m.equals("")) {
mb.write(gensrcModules);
}
}
tb.writeFile("overview.html",
"<html><body>This is the overview file</body></html>");
}
enum FrameKind {
DEFAULT(),
FRAMES("--frames"),
NO_FRAMES("--no-frames");
FrameKind(String... opts) {
this.opts = Arrays.asList(opts);
}
final List<String> opts;
}
enum OverviewKind {
DEFAULT(),
OVERVIEW("-overview", "overview.html"),
NO_OVERVIEW("-nooverview");
OverviewKind(String... opts) {
this.opts = Arrays.asList(opts);
}
final List<String> opts;
}
enum HtmlKind {
HTML4("-html4"),
HTML5("-html5");
HtmlKind(String... opts) {
this.opts = Arrays.asList(opts);
}
final List<String> opts;
}
@Override
public void runTests() throws Exception {
for (Method m : getClass().getDeclaredMethods()) {
Annotation a = m.getAnnotation(Test.class);
if (a != null) {
for (FrameKind fk : FrameKind.values()) {
for (OverviewKind ok : OverviewKind.values()) {
for (HtmlKind hk : HtmlKind.values()) {
try {
out.println("Running test " + m.getName() + " " + fk + " " + ok + " " + hk);
Path base = Paths.get(m.getName() + "_" + fk + "_" + ok + "_" + hk);
Files.createDirectories(base);
m.invoke(this, new Object[]{base, fk, ok, hk});
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
throw (cause instanceof Exception) ? ((Exception) cause) : e;
}
out.println();
}
}
}
}
}
printSummary();
}
void javadoc(Path outDir, FrameKind fKind, OverviewKind oKind, HtmlKind hKind, String... rest) {
List<String> args = new ArrayList<>();
args.add("-d");
args.add(outDir.toString());
args.addAll(fKind.opts);
args.addAll(oKind.opts);
args.addAll(hKind.opts);
args.addAll(Arrays.asList(rest));
javadoc(args.toArray(new String[0]));
checkExit(Exit.OK);
}
@Test
void testClass(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws Exception {
javadoc(base, fKind, oKind, hKind,
gensrcPackages.resolve("p1/P1C1.java").toString());
new Checker(fKind, oKind, hKind)
.classes("p1.P1C1")
.check();
}
@Test
void testClasses(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
javadoc(base, fKind, oKind, hKind,
gensrcPackages.resolve("p1/P1C1.java").toString(),
gensrcPackages.resolve("p1/P1C2.java").toString(),
gensrcPackages.resolve("p1/P1C3.java").toString());
new Checker(fKind, oKind, hKind)
.classes("p1.P1C1", "p1.P1C2", "p1.P1C3")
.check();
}
@Test
void testPackage(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
javadoc(base, fKind, oKind, hKind,
"-sourcepath", gensrcPackages.toString(),
"p1");
new Checker(fKind, oKind, hKind)
.classes("p1.P1C1", "p1.P1C2", "p1.P1C3")
.check();
}
@Test
void testPackages(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
javadoc(base, fKind, oKind, hKind,
"-sourcepath", gensrcPackages.toString(),
"p1", "p2", "p3");
new Checker(fKind, oKind, hKind)
.classes("p1.P1C1", "p1.P1C2", "p1.P1C3",
"p2.P2C1", "p2.P2C2", "p2.P2C3",
"p3.P3C1", "p3.P3C2", "p3.P3C3")
.check();
}
@Test
void testModules(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
javadoc(base, fKind, oKind, hKind,
"-modulesourcepath", gensrcModules.toString(),
"-addmods", "m1,m2,m3", // TEMPORARY, SHOULD NOT BE NECESSARY
"m1p1", "m1p2", "m1p3",
"m2p1", "m2p2", "m2p3",
"m3p1", "m3p2", "m3p3");
new Checker(fKind, oKind, hKind)
.classes("m1/m1p1.M1P1C1", "m1/m1p1.M1P1C2", "m1/m1p1.M1P1C3",
"m2/m2p1.M2P1C1", "m2/m2p1.M2P1C2", "m2/m2p1.M2P1C3",
"m3/m3p1.M3P1C1", "m3/m3p1.M3P1C2", "m3/m3p1.M3P1C3")
.check();
}
/**
* Check the contents of the generated output, according to the
* specified options.
*/
class Checker {
private final FrameKind fKind;
private final OverviewKind oKind;
private final HtmlKind hKind;
List<String> classes;
private boolean frames;
private boolean overview;
Checker(FrameKind fKind, OverviewKind oKind, HtmlKind hKind) {
this.fKind = fKind;
this.oKind = oKind;
this.hKind = hKind;
}
Checker classes(String... classes) {
this.classes = Arrays.asList(classes);
return this;
}
void check() throws IOException {
switch (fKind) {
case DEFAULT:
case FRAMES:
frames = true;
break;
case NO_FRAMES:
frames = false;
break;
}
switch (oKind) {
case DEFAULT:
overview = (getPackageCount() > 1);
break;
case OVERVIEW:
overview = true;
break;
case NO_OVERVIEW:
overview = false;
break;
}
checkAllClassesFiles();
checkFrameFiles();
checkOverviewSummary();
checkIndex();
checkNavBar();
checkHelpDoc();
}
private void checkAllClassesFiles() {
// these files are only generated in frames mode
checkFiles(frames,
"allclasses-frame.html",
"allclasses-noframe.html");
// this file is only generated when not in frames mode
checkFiles(!frames,
"allclasses.html");
}
private void checkFrameFiles() {
// these files are all only generated in frames mode
// <module>-frame.html and <module>-type-frame.html files
checkFiles(frames, classes.stream()
.filter(c -> isInModule(c))
.map(c -> modulePart(c))
.flatMap(m -> Arrays.asList(
m + "-frame.html",
m + "-type-frame.html").stream())
.collect(Collectors.toSet()));
// <package>/package-frame.html files
checkFiles(frames, classes.stream()
.map(c -> packagePart(c) + "/package-frame.html")
.collect(Collectors.toSet()));
}
private void checkHelpDoc() {
// the Help page only describes Frame/NoFrames in frames mode
checkOutput("help-doc.html", frames,
"<h2>Frames/No Frames</h2>");
}
private void checkIndex() {
// the index.html page only contains frames in frames mode
checkOutput("index.html", frames,
"<iframe ",
"</iframe>");
// the index.html contains the overview if one
// has been given, and not in frames mode
checkOutput("index.html", !frames && oKind == OverviewKind.OVERVIEW,
"This is the overview file");
// the index.html file contains a summary table
// if an overview was generated and not in frames mode
checkOutput("index.html", !frames && overview,
"<table class=\"overviewSummary\"");
// the index.html file contains a redirect if
// no frames and no overview
checkOutput("index.html", !frames && !overview,
"<meta http-equiv=\"Refresh\" content=\"0;",
"<script type=\"text/javascript\">window.location.replace(");
// the index.html file <meta> refresh should only use <noscript> in HTML 5
if (!frames && !overview) {
checkOutput("index.html", hKind == HtmlKind.HTML5,
"<noscript>\n<meta http-equiv=\"Refresh\" content=\"0;");
}
}
private void checkNavBar() {
// the files containing a navigation bar should only
// contain FRAMES/NO-FRAMES links in frames mode
List<String> navbarFiles = new ArrayList<>();
navbarFiles.addAll(classes.stream()
.map(c -> toHtml(packageClassPart(c)))
.collect(Collectors.toSet()));
for (String f : navbarFiles) {
checkOutput(f, frames,
"target=\"_top\">Frames</a>",
"target=\"_top\">No&nbsp;Frames</a>");
}
}
private void checkOverviewSummary() {
// the overview-summary.html file only appears if
// in frames mode and (overview requested or multiple packages)
checkFiles(frames && overview,
"overview-summary.html");
}
private long getPackageCount() {
return this.classes.stream()
.filter(name -> name.contains("."))
.map(name -> name.substring(0, name.lastIndexOf(".")))
.distinct()
.count();
}
private String packagePart(String className) {
int slash = className.indexOf("/");
int lastDot = className.lastIndexOf(".");
return className.substring(slash + 1, lastDot);
}
private String packageClassPart(String className) {
int slash = className.indexOf("/");
return className.substring(slash + 1);
}
private boolean isInModule(String className) {
return className.contains("/");
}
private String modulePart(String className) {
int slash = className.indexOf("/");
return className.substring(0, slash);
}
private String toHtml(String className) {
return className.replace(".", "/") + ".html";
}
}
}

View File

@ -165,7 +165,7 @@ public class ModuleBuilder {
if (!comment.isEmpty()) {
sb.append("/**\n").append(comment.replace("\n", " *")).append(" */\n");
}
sb.append("module ").append(name).append(" {");
sb.append("module ").append(name).append(" {\n");
requires.forEach(r -> sb.append(" " + r + "\n"));
exports.forEach(e -> sb.append(" " + e + "\n"));
uses.forEach(u -> sb.append(" " + u + "\n"));