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