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