/* * Copyright (c) 2017, 2019, 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 com.sun.source.doctree.DocTree; import com.sun.source.doctree.EndElementTree; import com.sun.source.doctree.StartElementTree; import com.sun.source.doctree.TextTree; import com.sun.source.util.DocTreeFactory; import com.sun.tools.doclint.HtmlTag; import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.markup.Navigation; import jdk.javadoc.internal.doclets.toolkit.Content; import jdk.javadoc.internal.doclets.toolkit.DocFileElement; import jdk.javadoc.internal.doclets.toolkit.DocFilesHandler; import jdk.javadoc.internal.doclets.toolkit.util.DocFile; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; import jdk.javadoc.internal.doclets.toolkit.util.Utils; import javax.lang.model.element.Element; import javax.lang.model.element.ModuleElement; import javax.lang.model.element.PackageElement; import javax.tools.FileObject; import javax.tools.JavaFileManager.Location; import java.util.ArrayList; import java.util.Collections; import java.util.List; import jdk.javadoc.internal.doclets.formats.html.markup.Navigation.PageMode; public class DocFilesHandlerImpl implements DocFilesHandler { public final Element element; public final Location location; public final DocPath source; public final HtmlConfiguration configuration; private final HtmlOptions options; private Navigation navBar; /** * Constructor to construct the DocFilesWriter object. * * @param configuration the configuration of this doclet. * @param element the containing element of the doc-files. * */ public DocFilesHandlerImpl(HtmlConfiguration configuration, Element element) { this.configuration = configuration; this.options = configuration.getOptions(); this.element = element; switch (element.getKind()) { case MODULE: ModuleElement mdle = (ModuleElement)element; location = configuration.utils.getLocationForModule(mdle); source = DocPaths.DOC_FILES; break; case PACKAGE: PackageElement pkg = (PackageElement)element; location = configuration.utils.getLocationForPackage(pkg); // Note, given that we have a module-specific location, // we want a module-relative path for the source, and not the // standard path that may include the module directory source = DocPath.create(pkg.getQualifiedName().toString().replace('.', '/')) .resolve(DocPaths.DOC_FILES); break; default: throw new AssertionError("unsupported element " + element); } } /** * Copy doc-files directory and its contents from the source * elements directory to the generated documentation directory. * * @throws DocFileIOException if there is a problem while copying * the documentation files */ public void copyDocFiles() throws DocFileIOException { boolean first = true; for (DocFile srcdir : DocFile.list(configuration, location, source)) { if (!srcdir.isDirectory()) { continue; } DocPath path = null; switch (this.element.getKind()) { case MODULE: path = DocPaths.forModule((ModuleElement)this.element); break; case PACKAGE: path = configuration.docPaths.forPackage((PackageElement)this.element); break; default: throw new AssertionError("unknown kind:" + this.element.getKind()); } copyDirectory(srcdir, path.resolve(DocPaths.DOC_FILES), first); first = false; } } public List getStylesheets() throws DocFileIOException { List stylesheets = new ArrayList(); for (DocFile srcdir : DocFile.list(configuration, location, source)) { for (DocFile srcFile : srcdir.list()) { if (srcFile.getName().endsWith(".css")) stylesheets.add(DocPaths.DOC_FILES.resolve(srcFile.getName())); } } return stylesheets; } private void copyDirectory(DocFile srcdir, final DocPath dstDocPath, boolean first) throws DocFileIOException { DocFile dstdir = DocFile.createFileForOutput(configuration, dstDocPath); if (srcdir.isSameFile(dstdir)) { return; } for (DocFile srcfile: srcdir.list()) { DocFile destfile = dstdir.resolve(srcfile.getName()); if (srcfile.isFile()) { if (destfile.exists() && !first) { configuration.messages.warning("doclet.Copy_Overwrite_warning", srcfile.getPath(), dstdir.getPath()); } else { if (Utils.toLowerCase(srcfile.getPath()).endsWith(".html")) { handleHtmlFile(srcfile, dstDocPath); } else { configuration.messages.notice("doclet.Copying_File_0_To_Dir_1", srcfile.getPath(), dstdir.getPath()); destfile.copyFile(srcfile); } } } else if (srcfile.isDirectory()) { if (options.copyDocfileSubdirs && !configuration.shouldExcludeDocFileDir(srcfile.getName())) { DocPath dirDocPath = dstDocPath.resolve(srcfile.getName()); copyDirectory(srcfile, dirDocPath, first); } } } } private void handleHtmlFile(DocFile srcfile, DocPath dstPath) throws DocFileIOException { Utils utils = configuration.utils; FileObject fileObject = srcfile.getFileObject(); DocFileElement dfElement = new DocFileElement(utils, element, fileObject); DocPath dfilePath = dstPath.resolve(srcfile.getName()); HtmlDocletWriter docletWriter = new DocFileWriter(configuration, dfilePath, element); configuration.messages.notice("doclet.Generating_0", docletWriter.filename.getPath()); List localTags = getLocalHeaderTags(utils.getPreamble(dfElement)); Content localTagsContent = docletWriter.commentTagsToContent(null, dfElement, localTags, false); String title = getWindowTitle(docletWriter, dfElement).trim(); HtmlTree htmlContent = docletWriter.getBody(title); PackageElement pkg = dfElement.getPackageElement(); this.navBar = new Navigation(element, configuration, PageMode.DOCFILE, docletWriter.path); Content headerContent = new ContentBuilder(); docletWriter.addTop(headerContent); Content mdleLinkContent = docletWriter.getModuleLink(utils.elementUtils.getModuleOf(pkg), docletWriter.contents.moduleLabel); navBar.setNavLinkModule(mdleLinkContent); Content pkgLinkContent = docletWriter.getPackageLink(pkg, docletWriter.contents.packageLabel); navBar.setNavLinkPackage(pkgLinkContent); navBar.setUserHeader(docletWriter.getUserHeaderFooter(true)); headerContent.add(navBar.getContent(true)); List fullBody = utils.getFullBody(dfElement); Content pageContent = docletWriter.commentTagsToContent(null, dfElement, fullBody, false); docletWriter.addTagsInfo(dfElement, pageContent); navBar.setUserFooter(docletWriter.getUserHeaderFooter(false)); Content footer = HtmlTree.FOOTER(); footer.add(navBar.getContent(false)); docletWriter.addBottom(footer); htmlContent.add(new BodyContents() .setHeader(headerContent) .addMainContent(HtmlTree.DIV(HtmlStyle.contentContainer, pageContent)) .setFooter(footer) .toContent()); docletWriter.printHtmlDocument(Collections.emptyList(), null, localTagsContent, Collections.emptyList(), htmlContent); } private List getLocalHeaderTags(List dtrees) { List localTags = new ArrayList<>(); DocTreeFactory docTreeFactory = configuration.docEnv.getDocTrees().getDocTreeFactory(); boolean inHead = false; boolean inTitle = false; loop: for (DocTree dt : dtrees) { switch (dt.getKind()) { case START_ELEMENT: StartElementTree startElem = (StartElementTree)dt; switch (HtmlTag.get(startElem.getName())) { case HEAD: inHead = true; break; case META: break; case TITLE: inTitle = true; break; default: if (inHead) { localTags.add(startElem); localTags.add(docTreeFactory.newTextTree(DocletConstants.NL)); } } break; case END_ELEMENT: EndElementTree endElem = (EndElementTree)dt; switch (HtmlTag.get(endElem.getName())) { case HEAD: inHead = false; break loop; case TITLE: inTitle = false; break; default: if (inHead) { localTags.add(endElem); localTags.add(docTreeFactory.newTextTree(DocletConstants.NL)); } } break; case ENTITY: case TEXT: if (inHead && !inTitle) { localTags.add(dt); } break; } } return localTags; } private String getWindowTitle(HtmlDocletWriter docletWriter, Element element) { List preamble = configuration.utils.getPreamble(element); StringBuilder sb = new StringBuilder(); boolean titleFound = false; loop: for (DocTree dt : preamble) { switch (dt.getKind()) { case START_ELEMENT: StartElementTree nodeStart = (StartElementTree)dt; if (Utils.toLowerCase(nodeStart.getName().toString()).equals("title")) { titleFound = true; } break; case END_ELEMENT: EndElementTree nodeEnd = (EndElementTree)dt; if (Utils.toLowerCase(nodeEnd.getName().toString()).equals("title")) { break loop; } break; case TEXT: TextTree nodeText = (TextTree)dt; if (titleFound) sb.append(nodeText.getBody()); break; default: // do nothing } } return docletWriter.getWindowTitle(sb.toString().trim()); } private static class DocFileWriter extends HtmlDocletWriter { /** * Constructor to construct the HtmlDocletWriter object. * * @param configuration the configuration of this doclet. * @param path the file to be generated. * @param e the anchoring element. */ public DocFileWriter(HtmlConfiguration configuration, DocPath path, Element e) { super(configuration, path); switch (e.getKind()) { case PACKAGE: case MODULE: break; default: throw new AssertionError("unsupported element: " + e.getKind()); } } } }