1 /* 2 * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.javadoc.internal.doclets.formats.html; 27 28 import com.sun.source.doctree.DocTree; 29 import com.sun.source.doctree.EndElementTree; 30 import com.sun.source.doctree.StartElementTree; 31 import com.sun.source.doctree.TextTree; 32 import com.sun.source.util.DocTreeFactory; 33 import com.sun.tools.doclint.HtmlTag; 34 import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; 35 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; 36 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; 37 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; 38 import jdk.javadoc.internal.doclets.formats.html.markup.Navigation; 39 import jdk.javadoc.internal.doclets.toolkit.Content; 40 import jdk.javadoc.internal.doclets.toolkit.DocFileElement; 41 import jdk.javadoc.internal.doclets.toolkit.DocFilesHandler; 42 import jdk.javadoc.internal.doclets.toolkit.util.DocFile; 43 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; 44 import jdk.javadoc.internal.doclets.toolkit.util.DocPath; 45 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; 46 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; 47 import jdk.javadoc.internal.doclets.toolkit.util.Utils; 48 49 import javax.lang.model.element.Element; 50 import javax.lang.model.element.ModuleElement; 51 import javax.lang.model.element.PackageElement; 52 import javax.tools.FileObject; 53 import javax.tools.JavaFileManager.Location; 54 55 import java.util.ArrayList; 56 import java.util.Collections; 57 import java.util.List; 58 59 import jdk.javadoc.internal.doclets.formats.html.markup.Navigation.PageMode; 60 61 public class DocFilesHandlerImpl implements DocFilesHandler { 62 63 public final Element element; 64 public final Location location; 65 public final DocPath source; 66 public final HtmlConfiguration configuration; 67 private Navigation navBar; 68 69 /** 70 * Constructor to construct the DocFilesWriter object. 71 * 72 * @param configuration the configuration of this doclet. 73 * @param element the containing element of the doc-files. 74 * 75 */ 76 public DocFilesHandlerImpl(HtmlConfiguration configuration, Element element) { 77 this.configuration = configuration; 78 this.element = element; 79 80 switch (element.getKind()) { 81 case MODULE: 82 ModuleElement mdle = (ModuleElement)element; 83 location = configuration.utils.getLocationForModule(mdle); 84 source = DocPaths.DOC_FILES; 85 break; 86 case PACKAGE: 87 PackageElement pkg = (PackageElement)element; 88 location = configuration.utils.getLocationForPackage(pkg); 89 // Note, given that we have a module-specific location, 90 // we want a module-relative path for the source, and not the 91 // standard path that may include the module directory 92 source = DocPath.create(pkg.getQualifiedName().toString().replace('.', '/')) 93 .resolve(DocPaths.DOC_FILES); 94 break; 95 default: 96 throw new AssertionError("unsupported element " + element); 97 } 98 } 99 100 /** 101 * Copy doc-files directory and its contents from the source 102 * elements directory to the generated documentation directory. 103 * 104 * @throws DocFileIOException if there is a problem while copying 105 * the documentation files 106 */ 107 108 public void copyDocFiles() throws DocFileIOException { 109 boolean first = true; 110 for (DocFile srcdir : DocFile.list(configuration, location, source)) { 111 if (!srcdir.isDirectory()) { 112 continue; 113 } 114 DocPath path = null; 115 switch (this.element.getKind()) { 116 case MODULE: 117 path = DocPaths.forModule((ModuleElement)this.element); 118 break; 119 case PACKAGE: 120 path = configuration.docPaths.forPackage((PackageElement)this.element); 121 break; 122 default: 123 throw new AssertionError("unknown kind:" + this.element.getKind()); 124 } 125 copyDirectory(srcdir, path.resolve(DocPaths.DOC_FILES), first); 126 first = false; 127 } 128 } 129 130 public List<DocPath> getStylesheets() throws DocFileIOException { 131 List<DocPath> stylesheets = new ArrayList<DocPath>(); 132 for (DocFile srcdir : DocFile.list(configuration, location, source)) { 133 for (DocFile srcFile : srcdir.list()) { 134 if (srcFile.getName().endsWith(".css")) 135 stylesheets.add(DocPaths.DOC_FILES.resolve(srcFile.getName())); 136 } 137 } 138 return stylesheets; 139 } 140 141 private void copyDirectory(DocFile srcdir, final DocPath dstDocPath, 142 boolean first) throws DocFileIOException { 143 DocFile dstdir = DocFile.createFileForOutput(configuration, dstDocPath); 144 if (srcdir.isSameFile(dstdir)) { 145 return; 146 } 147 for (DocFile srcfile: srcdir.list()) { 148 DocFile destfile = dstdir.resolve(srcfile.getName()); 149 if (srcfile.isFile()) { 150 if (destfile.exists() && !first) { 151 configuration.messages.warning("doclet.Copy_Overwrite_warning", 152 srcfile.getPath(), dstdir.getPath()); 153 } else { 154 if (Utils.toLowerCase(srcfile.getPath()).endsWith(".html")) { 155 handleHtmlFile(srcfile, dstDocPath); 156 } else { 157 configuration.messages.notice("doclet.Copying_File_0_To_Dir_1", 158 srcfile.getPath(), dstdir.getPath()); 159 destfile.copyFile(srcfile); 160 } 161 } 162 } else if (srcfile.isDirectory()) { 163 if (configuration.copydocfilesubdirs 164 && !configuration.shouldExcludeDocFileDir(srcfile.getName())) { 165 DocPath dirDocPath = dstDocPath.resolve(srcfile.getName()); 166 copyDirectory(srcfile, dirDocPath, first); 167 } 168 } 169 } 170 } 171 172 private void handleHtmlFile(DocFile srcfile, DocPath dstPath) throws DocFileIOException { 173 Utils utils = configuration.utils; 174 FileObject fileObject = srcfile.getFileObject(); 175 DocFileElement dfElement = new DocFileElement(utils, element, fileObject); 176 177 DocPath dfilePath = dstPath.resolve(srcfile.getName()); 178 HtmlDocletWriter docletWriter = new DocFileWriter(configuration, dfilePath, element); 179 configuration.messages.notice("doclet.Generating_0", docletWriter.filename.getPath()); 180 181 List<? extends DocTree> localTags = getLocalHeaderTags(utils.getPreamble(dfElement)); 182 Content localTagsContent = docletWriter.commentTagsToContent(null, dfElement, localTags, false); 183 184 String title = getWindowTitle(docletWriter, dfElement).trim(); 185 HtmlTree htmlContent = docletWriter.getBody(title); 186 PackageElement pkg = dfElement.getPackageElement(); 187 this.navBar = new Navigation(element, configuration, PageMode.DOCFILE, docletWriter.path); 188 Content headerContent = new ContentBuilder(); 189 docletWriter.addTop(headerContent); 190 Content mdleLinkContent = docletWriter.getModuleLink(utils.elementUtils.getModuleOf(pkg), 191 docletWriter.contents.moduleLabel); 192 navBar.setNavLinkModule(mdleLinkContent); 193 Content pkgLinkContent = docletWriter.getPackageLink(pkg, docletWriter.contents.packageLabel); 194 navBar.setNavLinkPackage(pkgLinkContent); 195 navBar.setUserHeader(docletWriter.getUserHeaderFooter(true)); 196 headerContent.add(navBar.getContent(true)); 197 198 List<? extends DocTree> fullBody = utils.getFullBody(dfElement); 199 Content pageContent = docletWriter.commentTagsToContent(null, dfElement, fullBody, false); 200 docletWriter.addTagsInfo(dfElement, pageContent); 201 202 navBar.setUserFooter(docletWriter.getUserHeaderFooter(false)); 203 Content footer = HtmlTree.FOOTER(); 204 footer.add(navBar.getContent(false)); 205 docletWriter.addBottom(footer); 206 htmlContent.add(new BodyContents() 207 .setHeader(headerContent) 208 .addMainContent(HtmlTree.DIV(HtmlStyle.contentContainer, pageContent)) 209 .setFooter(footer) 210 .toContent()); 211 docletWriter.printHtmlDocument(Collections.emptyList(), null, localTagsContent, Collections.emptyList(), htmlContent); 212 } 213 214 215 private List<? extends DocTree> getLocalHeaderTags(List<? extends DocTree> dtrees) { 216 List<DocTree> localTags = new ArrayList<>(); 217 DocTreeFactory docTreeFactory = configuration.docEnv.getDocTrees().getDocTreeFactory(); 218 boolean inHead = false; 219 boolean inTitle = false; 220 loop: 221 for (DocTree dt : dtrees) { 222 switch (dt.getKind()) { 223 case START_ELEMENT: 224 StartElementTree startElem = (StartElementTree)dt; 225 switch (HtmlTag.get(startElem.getName())) { 226 case HEAD: 227 inHead = true; 228 break; 229 case META: 230 break; 231 case TITLE: 232 inTitle = true; 233 break; 234 default: 235 if (inHead) { 236 localTags.add(startElem); 237 localTags.add(docTreeFactory.newTextTree(DocletConstants.NL)); 238 } 239 } 240 break; 241 case END_ELEMENT: 242 EndElementTree endElem = (EndElementTree)dt; 243 switch (HtmlTag.get(endElem.getName())) { 244 case HEAD: 245 inHead = false; 246 break loop; 247 case TITLE: 248 inTitle = false; 249 break; 250 default: 251 if (inHead) { 252 localTags.add(endElem); 253 localTags.add(docTreeFactory.newTextTree(DocletConstants.NL)); 254 } 255 } 256 break; 257 case ENTITY: 258 case TEXT: 259 if (inHead && !inTitle) { 260 localTags.add(dt); 261 } 262 break; 263 } 264 } 265 return localTags; 266 } 267 268 private String getWindowTitle(HtmlDocletWriter docletWriter, Element element) { 269 List<? extends DocTree> preamble = configuration.utils.getPreamble(element); 270 StringBuilder sb = new StringBuilder(); 271 boolean titleFound = false; 272 loop: 273 for (DocTree dt : preamble) { 274 switch (dt.getKind()) { 275 case START_ELEMENT: 276 StartElementTree nodeStart = (StartElementTree)dt; 277 if (Utils.toLowerCase(nodeStart.getName().toString()).equals("title")) { 278 titleFound = true; 279 } 280 break; 281 282 case END_ELEMENT: 283 EndElementTree nodeEnd = (EndElementTree)dt; 284 if (Utils.toLowerCase(nodeEnd.getName().toString()).equals("title")) { 285 break loop; 286 } 287 break; 288 289 case TEXT: 290 TextTree nodeText = (TextTree)dt; 291 if (titleFound) 292 sb.append(nodeText.getBody()); 293 break; 294 295 default: 296 // do nothing 297 } 298 } 299 return docletWriter.getWindowTitle(sb.toString().trim()); 300 } 301 302 private static class DocFileWriter extends HtmlDocletWriter { 303 304 /** 305 * Constructor to construct the HtmlDocletWriter object. 306 * 307 * @param configuration the configuration of this doclet. 308 * @param path the file to be generated. 309 * @param e the anchoring element. 310 */ 311 public DocFileWriter(HtmlConfiguration configuration, DocPath path, Element e) { 312 super(configuration, path); 313 switch (e.getKind()) { 314 case PACKAGE: 315 case MODULE: 316 break; 317 default: 318 throw new AssertionError("unsupported element: " + e.getKind()); 319 } 320 } 321 } 322 }