1 /* 2 * Copyright (c) 2001, 2020, 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 jdk.javadoc.internal.doclets.formats.html.markup.Head; 29 30 import java.io.*; 31 import java.util.List; 32 33 import javax.lang.model.element.Element; 34 import javax.lang.model.element.ModuleElement; 35 import javax.lang.model.element.PackageElement; 36 import javax.lang.model.element.TypeElement; 37 import javax.tools.FileObject; 38 39 import jdk.javadoc.doclet.DocletEnvironment; 40 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; 41 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument; 42 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; 43 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; 44 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; 45 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; 46 import jdk.javadoc.internal.doclets.toolkit.Content; 47 import jdk.javadoc.internal.doclets.toolkit.Messages; 48 import jdk.javadoc.internal.doclets.toolkit.Resources; 49 import jdk.javadoc.internal.doclets.toolkit.util.DocFile; 50 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; 51 import jdk.javadoc.internal.doclets.toolkit.util.DocPath; 52 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; 53 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; 54 import jdk.javadoc.internal.doclets.toolkit.util.SimpleDocletException; 55 import jdk.javadoc.internal.doclets.toolkit.util.Utils; 56 57 /** 58 * Converts Java Source Code to HTML. 59 * 60 * <p><b>This is NOT part of any supported API. 61 * If you write code that depends on this, you do so at your own risk. 62 * This code and its internal interfaces are subject to change or 63 * deletion without notice.</b> 64 */ 65 public class SourceToHTMLConverter { 66 67 /** 68 * The number of trailing blank lines at the end of the page. 69 * This is inserted so that anchors at the bottom of small pages 70 * can be reached. 71 */ 72 private static final int NUM_BLANK_LINES = 60; 73 74 /** 75 * New line to be added to the documentation. 76 */ 77 private static final String NEW_LINE = DocletConstants.NL; 78 79 private final HtmlConfiguration configuration; 80 private final Messages messages; 81 private final Resources resources; 82 private final Utils utils; 83 84 private final DocletEnvironment docEnv; 85 86 private final DocPath outputdir; 87 88 /** 89 * Relative path from the documentation root to the file that is being 90 * generated. 91 */ 92 private DocPath relativePath = DocPath.empty; 93 94 private SourceToHTMLConverter(HtmlConfiguration configuration, DocletEnvironment rd, 95 DocPath outputdir) { 96 this.configuration = configuration; 97 this.messages = configuration.getMessages(); 98 this.resources = configuration.resources; 99 this.utils = configuration.utils; 100 this.docEnv = rd; 101 this.outputdir = outputdir; 102 } 103 104 /** 105 * Translate the TypeElements in the given DocletEnvironment to HTML representation. 106 * 107 * @param configuration the configuration. 108 * @param docEnv the DocletEnvironment to convert. 109 * @param outputdir the name of the directory to output to. 110 * @throws DocFileIOException if there is a problem generating an output file 111 * @throws SimpleDocletException if there is a problem reading a source file 112 */ 113 public static void convertRoot(HtmlConfiguration configuration, DocletEnvironment docEnv, 114 DocPath outputdir) throws DocFileIOException, SimpleDocletException { 115 new SourceToHTMLConverter(configuration, docEnv, outputdir).generate(); 116 } 117 118 void generate() throws DocFileIOException, SimpleDocletException { 119 if (docEnv == null || outputdir == null) { 120 return; 121 } 122 for (ModuleElement mdl : configuration.getSpecifiedModuleElements()) { 123 // If -nodeprecated option is set and the module is marked as deprecated, 124 // do not convert the module files to HTML. 125 if (!(configuration.nodeprecated && utils.isDeprecated(mdl))) 126 convertModule(mdl, outputdir); 127 } 128 for (PackageElement pkg : configuration.getSpecifiedPackageElements()) { 129 // If -nodeprecated option is set and the package is marked as deprecated, 130 // do not convert the package files to HTML. 131 if (!(configuration.nodeprecated && utils.isDeprecated(pkg))) 132 convertPackage(pkg, outputdir); 133 } 134 for (TypeElement te : configuration.getSpecifiedTypeElements()) { 135 // If -nodeprecated option is set and the class is marked as deprecated 136 // or the containing package is deprecated, do not convert the 137 // package files to HTML. 138 if (!(configuration.nodeprecated && 139 (utils.isDeprecated(te) || utils.isDeprecated(utils.containingPackage(te))))) 140 convertClass(te, outputdir); 141 } 142 } 143 144 /** 145 * Convert the Classes in the given Package to an HTML file. 146 * 147 * @param pkg the Package to convert. 148 * @param outputdir the name of the directory to output to. 149 * @throws DocFileIOException if there is a problem generating an output file 150 * @throws SimpleDocletException if there is a problem reading a source file 151 */ 152 public void convertPackage(PackageElement pkg, DocPath outputdir) 153 throws DocFileIOException, SimpleDocletException { 154 if (pkg == null) { 155 return; 156 } 157 for (Element te : utils.getAllClasses(pkg)) { 158 // If -nodeprecated option is set and the class is marked as deprecated, 159 // do not convert the package files to HTML. We do not check for 160 // containing package deprecation since it is already check in 161 // the calling method above. 162 if (!(configuration.nodeprecated && utils.isDeprecated(te))) 163 convertClass((TypeElement)te, outputdir); 164 } 165 } 166 167 /** 168 * Convert the documented packages contained in the given module to an HTML representation. 169 * 170 * @param mdl the module to convert. 171 * @param outputdir the name of the directory to output to. 172 * @throws DocFileIOException if there is a problem generating an output file 173 * @throws SimpleDocletException if there is a problem reading a source file 174 */ 175 public void convertModule(ModuleElement mdl, DocPath outputdir) 176 throws DocFileIOException, SimpleDocletException { 177 if (mdl == null) { 178 return; 179 } 180 for (Element elem : mdl.getEnclosedElements()) { 181 if (elem instanceof PackageElement && configuration.docEnv.isIncluded(elem) 182 && !(configuration.nodeprecated && utils.isDeprecated(elem))) { 183 convertPackage((PackageElement) elem, outputdir); 184 } 185 } 186 } 187 188 /** 189 * Convert the given Class to an HTML. 190 * 191 * @param te the class to convert. 192 * @param outputdir the name of the directory to output to 193 * @throws DocFileIOException if there is a problem generating the output file 194 * @throws SimpleDocletException if there is a problem reading the source file 195 */ 196 public void convertClass(TypeElement te, DocPath outputdir) 197 throws DocFileIOException, SimpleDocletException { 198 if (te == null) { 199 return; 200 } 201 FileObject fo = utils.getFileObject(te); 202 if (fo == null) 203 return; 204 205 try { 206 Reader r = fo.openReader(true); 207 int lineno = 1; 208 String line; 209 relativePath = DocPaths.SOURCE_OUTPUT 210 .resolve(configuration.docPaths.forPackage(te)) 211 .invert(); 212 Content body = getHeader(); 213 Content pre = new HtmlTree(HtmlTag.PRE); 214 try (LineNumberReader reader = new LineNumberReader(r)) { 215 while ((line = reader.readLine()) != null) { 216 addLineNo(pre, lineno); 217 addLine(pre, line, lineno); 218 lineno++; 219 } 220 } 221 addBlankLines(pre); 222 Content div = HtmlTree.DIV(HtmlStyle.sourceContainer, pre); 223 body.add(HtmlTree.MAIN(div)); 224 writeToFile(body, outputdir.resolve(configuration.docPaths.forClass(te)), te); 225 } catch (IOException e) { 226 String message = resources.getText("doclet.exception.read.file", fo.getName()); 227 throw new SimpleDocletException(message, e); 228 } 229 } 230 231 /** 232 * Write the output to the file. 233 * 234 * @param body the documentation content to be written to the file. 235 * @param path the path for the file. 236 */ 237 private void writeToFile(Content body, DocPath path, TypeElement te) throws DocFileIOException { 238 Head head = new Head(path, configuration.docletVersion, configuration.startTime) 239 // .setTimestamp(!configuration.notimestamp) // temporary: compatibility! 240 .setTitle(resources.getText("doclet.Window_Source_title")) 241 // .setCharset(configuration.charset) // temporary: compatibility! 242 .setDescription(HtmlDocletWriter.getDescription("source", te)) 243 .setGenerator(HtmlDocletWriter.getGenerator(getClass())) 244 .addDefaultScript(false) 245 .setStylesheets(configuration.getMainStylesheet(), configuration.getAdditionalStylesheets()); 246 Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), 247 head.toContent(), body); 248 HtmlDocument htmlDocument = new HtmlDocument(htmlTree); 249 messages.notice("doclet.Generating_0", path.getPath()); 250 htmlDocument.write(DocFile.createFileForOutput(configuration, path)); 251 } 252 253 /** 254 * Returns a link to the stylesheet file. 255 * 256 * @param head an HtmlTree to which the stylesheet links will be added 257 */ 258 public void addStyleSheetProperties(Content head) { 259 String filename = configuration.stylesheetfile; 260 DocPath stylesheet; 261 if (filename.length() > 0) { 262 DocFile file = DocFile.createFileForInput(configuration, filename); 263 stylesheet = DocPath.create(file.getName()); 264 } else { 265 stylesheet = DocPaths.STYLESHEET; 266 } 267 DocPath p = relativePath.resolve(stylesheet); 268 HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", p.getPath(), "Style"); 269 head.add(link); 270 addStylesheets(head); 271 } 272 273 protected void addStylesheets(Content tree) { 274 List<String> stylesheets = configuration.additionalStylesheets; 275 if (!stylesheets.isEmpty()) { 276 stylesheets.forEach((ssheet) -> { 277 DocFile file = DocFile.createFileForInput(configuration, ssheet); 278 DocPath ssheetPath = DocPath.create(file.getName()); 279 HtmlTree slink = HtmlTree.LINK("stylesheet", "text/css", relativePath.resolve(ssheetPath).getPath(), 280 "Style"); 281 tree.add(slink); 282 }); 283 } 284 } 285 286 /** 287 * Get the header. 288 * 289 * @return the header content for the HTML file 290 */ 291 private static Content getHeader() { 292 return new HtmlTree(HtmlTag.BODY).put(HtmlAttr.CLASS, "source"); 293 } 294 295 /** 296 * Add the line numbers for the source code. 297 * 298 * @param pre the content tree to which the line number will be added 299 * @param lineno The line number 300 */ 301 private static void addLineNo(Content pre, int lineno) { 302 HtmlTree span = new HtmlTree(HtmlTag.SPAN); 303 span.setStyle(HtmlStyle.sourceLineNo); 304 if (lineno < 10) { 305 span.add("00" + Integer.toString(lineno)); 306 } else if (lineno < 100) { 307 span.add("0" + Integer.toString(lineno)); 308 } else { 309 span.add(Integer.toString(lineno)); 310 } 311 pre.add(span); 312 } 313 314 /** 315 * Add a line from source to the HTML file that is generated. 316 * 317 * @param pre the content tree to which the line will be added. 318 * @param line the string to format. 319 * @param currentLineNo the current number. 320 */ 321 private void addLine(Content pre, String line, int currentLineNo) { 322 if (line != null) { 323 Content anchor = HtmlTree.SPAN_ID( 324 "line." + Integer.toString(currentLineNo), 325 new StringContent(utils.replaceTabs(line))); 326 pre.add(anchor); 327 pre.add(NEW_LINE); 328 } 329 } 330 331 /** 332 * Add trailing blank lines at the end of the page. 333 * 334 * @param pre the content tree to which the blank lines will be added. 335 */ 336 private static void addBlankLines(Content pre) { 337 for (int i = 0; i < NUM_BLANK_LINES; i++) { 338 pre.add(NEW_LINE); 339 } 340 } 341 342 /** 343 * Given an element, return an anchor name for it. 344 * 345 * @param utils the utility class, used to get the line number of the element 346 * @param e the element to check. 347 * @return the name of the anchor. 348 */ 349 public static String getAnchorName(Utils utils, Element e) { 350 return "line." + utils.getLineNumber(e); 351 } 352 }