1 /* 2 * Copyright (c) 2017, 2018, 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.AttributeTree; 29 import com.sun.source.doctree.AttributeTree.ValueKind; 30 import com.sun.source.doctree.DocRootTree; 31 import com.sun.source.doctree.DocTree; 32 import com.sun.source.doctree.EndElementTree; 33 import com.sun.source.doctree.LinkTree; 34 import com.sun.source.doctree.StartElementTree; 35 import com.sun.source.doctree.TextTree; 36 import com.sun.source.util.SimpleDocTreeVisitor; 37 import com.sun.tools.doclint.HtmlTag; 38 import com.sun.tools.doclint.HtmlTag.Attr; 39 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; 40 import jdk.javadoc.internal.doclets.toolkit.Content; 41 import jdk.javadoc.internal.doclets.toolkit.DocFileElement; 42 import jdk.javadoc.internal.doclets.toolkit.DocFilesHandler; 43 import jdk.javadoc.internal.doclets.toolkit.util.DocFile; 44 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; 45 import jdk.javadoc.internal.doclets.toolkit.util.DocPath; 46 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; 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 import java.util.Collections; 55 import java.util.List; 56 57 public class DocFilesHandlerImpl implements DocFilesHandler { 58 59 public final Element element; 60 public final Location location; 61 public final DocPath source; 62 public final HtmlConfiguration configuration; 63 64 /** 65 * Constructor to construct the DocFilesWriter object. 66 * 67 * @param configuration the configuration of this doclet. 68 * @param element the containing element of the doc-files. 69 * 70 */ 71 public DocFilesHandlerImpl(HtmlConfiguration configuration, Element element) { 72 this.configuration = configuration; 73 this.element = element; 74 75 switch (element.getKind()) { 76 case MODULE: 77 location = configuration.utils.getLocationForModule((ModuleElement)element); 78 source = DocPaths.DOC_FILES; 79 break; 80 case PACKAGE: 81 location = configuration.utils.getLocationForPackage((PackageElement)element); 82 source = DocPath.forPackage((PackageElement)element).resolve(DocPaths.DOC_FILES); 83 break; 84 default: 85 throw new AssertionError("unsupported element " + element); 86 } 87 } 88 89 /** 90 * Copy doc-files directory and its contents from the source 91 * elements directory to the generated documentation directory. 92 * 93 * @throws DocFileIOException if there is a problem while copying 94 * the documentation files 95 */ 96 97 public void copyDocFiles() throws DocFileIOException { 98 boolean first = true; 99 for (DocFile srcdir : DocFile.list(configuration, location, source)) { 100 if (!srcdir.isDirectory()) { 101 continue; 102 } 103 DocPath path = null; 104 switch (this.element.getKind()) { 105 case MODULE: 106 path = DocPath.forModule((ModuleElement)this.element); 107 break; 108 case PACKAGE: 109 path = DocPath.forPackage((PackageElement)this.element); 110 break; 111 default: 112 throw new AssertionError("unknown kind:" + this.element.getKind()); 113 } 114 copyDirectory(srcdir, path.resolve(DocPaths.DOC_FILES), first); 115 first = false; 116 } 117 } 118 119 120 private void copyDirectory(DocFile srcdir, final DocPath dstDocPath, 121 boolean first) throws DocFileIOException { 122 DocFile dstdir = DocFile.createFileForOutput(configuration, dstDocPath); 123 if (srcdir.isSameFile(dstdir)) { 124 return; 125 } 126 for (DocFile srcfile: srcdir.list()) { 127 DocFile destfile = dstdir.resolve(srcfile.getName()); 128 if (srcfile.isFile()) { 129 if (destfile.exists() && !first) { 130 configuration.messages.warning("doclet.Copy_Overwrite_warning", 131 srcfile.getPath(), dstdir.getPath()); 132 } else { 133 if (Utils.toLowerCase(srcfile.getPath()).endsWith(".html")) { 134 if (handleHtmlFile(srcfile, dstDocPath)) { 135 continue; 136 } 137 } 138 configuration.messages.notice("doclet.Copying_File_0_To_Dir_1", 139 srcfile.getPath(), dstdir.getPath()); 140 destfile.copyFile(srcfile); 141 } 142 } else if (srcfile.isDirectory()) { 143 if (configuration.copydocfilesubdirs 144 && !configuration.shouldExcludeDocFileDir(srcfile.getName())) { 145 DocPath dirDocPath = dstDocPath.resolve(srcfile.getName()); 146 copyDirectory(srcfile, dirDocPath, first); 147 } 148 } 149 } 150 } 151 152 private boolean handleHtmlFile(DocFile srcfile, DocPath dstPath) throws DocFileIOException { 153 Utils utils = configuration.utils; 154 FileObject fileObject = srcfile.getFileObject(); 155 DocFileElement dfElement = new DocFileElement(element, fileObject); 156 157 if (shouldPassThrough(utils.getPreamble(dfElement))) { 158 return false; 159 } 160 161 DocPath dfilePath = dstPath.resolve(srcfile.getName()); 162 HtmlDocletWriter docletWriter = new DocFileWriter(configuration, dfilePath, element); 163 configuration.messages.notice("doclet.Generating_0", docletWriter.filename); 164 165 String title = getWindowTitle(docletWriter, dfElement).trim(); 166 HtmlTree htmlContent = docletWriter.getBody(true, title); 167 docletWriter.addTop(htmlContent); 168 docletWriter.addNavLinks(true, htmlContent); 169 List<? extends DocTree> fullBody = utils.getFullBody(dfElement); 170 Content bodyContent = docletWriter.commentTagsToContent(null, dfElement, fullBody, false); 171 172 docletWriter.addTagsInfo(dfElement, bodyContent); 173 htmlContent.addContent(bodyContent); 174 175 docletWriter.addNavLinks(false, htmlContent); 176 docletWriter.addBottom(htmlContent); 177 docletWriter.printHtmlDocument(Collections.emptyList(), false, htmlContent); 178 return true; 179 } 180 181 182 private boolean shouldPassThrough(List<? extends DocTree> dtrees) { 183 SimpleDocTreeVisitor<Boolean, Boolean> check = new SimpleDocTreeVisitor<Boolean, Boolean>() { 184 @Override 185 public Boolean visitStartElement(StartElementTree node, Boolean p) { 186 if (Utils.toLowerCase(node.getName().toString()).equals((Attr.STYLE.getText()))) { 187 return true; 188 } 189 if (Utils.toLowerCase(node.getName().toString()).equals(HtmlTag.LINK.getText())) { 190 for (DocTree dt : node.getAttributes()) { 191 if (this.visit(dt, true)) 192 return true; 193 } 194 } 195 return false; 196 } 197 198 @Override 199 public Boolean visitAttribute(AttributeTree node, Boolean p) { 200 if (p == null || p == false) { 201 return false; 202 } 203 if (Utils.toLowerCase(node.getName().toString()).equals("rel")) { 204 for (DocTree dt : node.getValue()) { 205 Boolean found = new SimpleDocTreeVisitor<Boolean, ValueKind>() { 206 207 @Override 208 public Boolean visitText(TextTree node, ValueKind valueKind) { 209 switch (valueKind) { 210 case EMPTY: 211 return false; 212 default: 213 return Utils.toLowerCase(node.getBody()).equals("stylesheet"); 214 } 215 } 216 217 @Override 218 protected Boolean defaultAction(DocTree node, ValueKind valueKind) { 219 return false; 220 } 221 222 }.visit(dt, node.getValueKind()); 223 224 if (found) 225 return true; 226 } 227 } 228 return false; 229 } 230 231 @Override 232 protected Boolean defaultAction(DocTree node, Boolean p) { 233 return false; 234 } 235 }; 236 for (DocTree dt : dtrees) { 237 if (check.visit(dt, false)) 238 return true; 239 } 240 return false; 241 } 242 243 private String getWindowTitle(HtmlDocletWriter docletWriter, Element element) { 244 List<? extends DocTree> preamble = configuration.utils.getPreamble(element); 245 StringBuilder sb = new StringBuilder(); 246 boolean titleFound = false; 247 loop: 248 for (DocTree dt : preamble) { 249 switch (dt.getKind()) { 250 case START_ELEMENT: 251 StartElementTree nodeStart = (StartElementTree)dt; 252 if (Utils.toLowerCase(nodeStart.getName().toString()).equals("title")) { 253 titleFound = true; 254 } 255 break; 256 257 case END_ELEMENT: 258 EndElementTree nodeEnd = (EndElementTree)dt; 259 if (Utils.toLowerCase(nodeEnd.getName().toString()).equals("title")) { 260 break loop; 261 } 262 break; 263 264 case TEXT: 265 TextTree nodeText = (TextTree)dt; 266 if (titleFound) 267 sb.append(nodeText.getBody()); 268 break; 269 270 default: 271 // do nothing 272 } 273 } 274 return docletWriter.getWindowTitle(sb.toString().trim()); 275 } 276 277 private static class DocFileWriter extends HtmlDocletWriter { 278 279 final PackageElement pkg; 280 281 /** 282 * Constructor to construct the HtmlDocletWriter object. 283 * 284 * @param configuration the configuruation of this doclet. 285 * @param path the file to be generated. 286 * @param e the anchoring element. 287 */ 288 public DocFileWriter(HtmlConfiguration configuration, DocPath path, Element e) { 289 super(configuration, path); 290 switch (e.getKind()) { 291 case PACKAGE: 292 pkg = (PackageElement)e; 293 break; 294 default: 295 throw new AssertionError("unsupported element: " + e.getKind()); 296 } 297 } 298 299 /** 300 * Get the module link. 301 * 302 * @return a content tree for the module link 303 */ 304 @Override 305 protected Content getNavLinkModule() { 306 Content linkContent = getModuleLink(utils.elementUtils.getModuleOf(pkg), 307 contents.moduleLabel); 308 Content li = HtmlTree.LI(linkContent); 309 return li; 310 } 311 312 /** 313 * Get this package link. 314 * 315 * @return a content tree for the package link 316 */ 317 @Override 318 protected Content getNavLinkPackage() { 319 Content linkContent = getPackageLink(pkg, 320 contents.packageLabel); 321 Content li = HtmlTree.LI(linkContent); 322 return li; 323 } 324 } 325 }