1 /* 2 * Copyright (c) 1997, 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.markup; 27 28 import java.io.*; 29 import java.util.*; 30 31 import jdk.javadoc.doclet.DocletEnvironment.ModuleMode; 32 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; 33 import jdk.javadoc.internal.doclets.toolkit.Content; 34 import jdk.javadoc.internal.doclets.toolkit.Resources; 35 import jdk.javadoc.internal.doclets.toolkit.util.DocFile; 36 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; 37 import jdk.javadoc.internal.doclets.toolkit.util.DocPath; 38 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; 39 import jdk.javadoc.internal.doclets.toolkit.util.TableTabTypes; 40 41 42 /** 43 * Class for the Html format code generation. 44 * Initializes PrintWriter with FileWriter, to enable print 45 * related methods to generate the code to the named File through FileWriter. 46 * 47 * <p><b>This is NOT part of any supported API. 48 * If you write code that depends on this, you do so at your own risk. 49 * This code and its internal interfaces are subject to change or 50 * deletion without notice.</b> 51 * 52 * @author Atul M Dambalkar 53 * @author Bhavesh Patel (Modified) 54 */ 55 public class HtmlWriter { 56 57 /** 58 * The window title of this file 59 */ 60 protected String winTitle; 61 62 /** 63 * The configuration 64 */ 65 protected BaseConfiguration configuration; 66 67 /** 68 * Header for table displaying modules and description. 69 */ 70 protected final List<String> moduleTableHeader; 71 72 /** 73 * Header for tables displaying packages and description. 74 */ 75 protected final List<String> packageTableHeader; 76 77 /** 78 * Header for tables displaying modules and description. 79 */ 80 protected final List<String> requiresTableHeader; 81 82 /** 83 * Header for tables displaying packages and description. 84 */ 85 protected final List<String> exportedPackagesTableHeader; 86 87 /** 88 * Header for tables displaying modules and exported packages. 89 */ 90 protected final List<String> indirectPackagesTableHeader; 91 92 /** 93 * Header for tables displaying types and description. 94 */ 95 protected final List<String> usesTableHeader; 96 97 /** 98 * Header for tables displaying types and description. 99 */ 100 protected final List<String> providesTableHeader; 101 102 /** 103 * Summary for use tables displaying class and package use. 104 */ 105 protected final String useTableSummary; 106 107 /** 108 * Column header for class docs displaying Modifier and Type header. 109 */ 110 protected final String modifierTypeHeader; 111 112 private final DocFile docFile; 113 114 protected Content script; 115 116 117 /** 118 * Constructor. 119 * 120 * @param path The directory path to be created for this file 121 * or null if none to be created. 122 */ 123 public HtmlWriter(BaseConfiguration configuration, DocPath path) { 124 docFile = DocFile.createFileForOutput(configuration, path); 125 this.configuration = configuration; 126 127 // The following should be converted to shared Content objects 128 // and moved to Contents, but that will require additional 129 // changes at the use sites. 130 Resources resources = configuration.getResources(); 131 moduleTableHeader = Arrays.asList( 132 resources.getText("doclet.Module"), 133 resources.getText("doclet.Description")); 134 packageTableHeader = new ArrayList<>(); 135 packageTableHeader.add(resources.getText("doclet.Package")); 136 packageTableHeader.add(resources.getText("doclet.Description")); 137 requiresTableHeader = new ArrayList<>(); 138 requiresTableHeader.add(resources.getText("doclet.Modifier")); 139 requiresTableHeader.add(resources.getText("doclet.Module")); 140 requiresTableHeader.add(resources.getText("doclet.Description")); 141 exportedPackagesTableHeader = new ArrayList<>(); 142 exportedPackagesTableHeader.add(resources.getText("doclet.Package")); 143 if (configuration.docEnv.getModuleMode() == ModuleMode.ALL) { 144 exportedPackagesTableHeader.add(resources.getText("doclet.Module")); 145 } 146 exportedPackagesTableHeader.add(resources.getText("doclet.Description")); 147 indirectPackagesTableHeader = new ArrayList<>(); 148 indirectPackagesTableHeader.add(resources.getText("doclet.From")); 149 indirectPackagesTableHeader.add(resources.getText("doclet.Packages")); 150 usesTableHeader = new ArrayList<>(); 151 usesTableHeader.add(resources.getText("doclet.Type")); 152 usesTableHeader.add(resources.getText("doclet.Description")); 153 providesTableHeader = new ArrayList<>(); 154 providesTableHeader.add(resources.getText("doclet.Type")); 155 providesTableHeader.add(resources.getText("doclet.Description")); 156 useTableSummary = resources.getText("doclet.Use_Table_Summary", 157 resources.getText("doclet.packages")); 158 modifierTypeHeader = resources.getText("doclet.0_and_1", 159 resources.getText("doclet.Modifier"), 160 resources.getText("doclet.Type")); 161 } 162 163 public void write(Content c) throws DocFileIOException { 164 try (Writer writer = docFile.openWriter()) { 165 c.write(writer, true); 166 } catch (IOException e) { 167 throw new DocFileIOException(docFile, DocFileIOException.Mode.WRITE, e); 168 } 169 } 170 171 /** 172 * Returns an HtmlTree for the SCRIPT tag. 173 * 174 * @return an HtmlTree for the SCRIPT tag 175 */ 176 protected HtmlTree getWinTitleScript(){ 177 HtmlTree scriptTree = HtmlTree.SCRIPT(); 178 if(winTitle != null && winTitle.length() > 0) { 179 String scriptCode = "<!--\n" + 180 " try {\n" + 181 " if (location.href.indexOf('is-external=true') == -1) {\n" + 182 " parent.document.title=\"" + escapeJavaScriptChars(winTitle) + "\";\n" + 183 " }\n" + 184 " }\n" + 185 " catch(err) {\n" + 186 " }\n" + 187 "//-->\n"; 188 RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL)); 189 scriptTree.addContent(scriptContent); 190 } 191 return scriptTree; 192 } 193 194 /** 195 * Returns a String with escaped special JavaScript characters. 196 * 197 * @param s String that needs to be escaped 198 * @return a valid escaped JavaScript string 199 */ 200 private static String escapeJavaScriptChars(String s) { 201 StringBuilder sb = new StringBuilder(); 202 for (int i = 0; i < s.length(); i++) { 203 char ch = s.charAt(i); 204 switch (ch) { 205 case '\b': 206 sb.append("\\b"); 207 break; 208 case '\t': 209 sb.append("\\t"); 210 break; 211 case '\n': 212 sb.append("\\n"); 213 break; 214 case '\f': 215 sb.append("\\f"); 216 break; 217 case '\r': 218 sb.append("\\r"); 219 break; 220 case '"': 221 sb.append("\\\""); 222 break; 223 case '\'': 224 sb.append("\\\'"); 225 break; 226 case '\\': 227 sb.append("\\\\"); 228 break; 229 default: 230 if (ch < 32 || ch >= 127) { 231 sb.append(String.format("\\u%04X", (int)ch)); 232 } else { 233 sb.append(ch); 234 } 235 break; 236 } 237 } 238 return sb.toString(); 239 } 240 241 /** 242 * Returns a content tree for the SCRIPT tag for the main page(index.html). 243 * 244 * @return a content for the SCRIPT tag 245 */ 246 protected Content getFramesJavaScript() { 247 HtmlTree scriptTree = HtmlTree.SCRIPT(); 248 String scriptCode = "\n" + 249 " tmpTargetPage = \"\" + window.location.search;\n" + 250 " if (tmpTargetPage != \"\" && tmpTargetPage != \"undefined\")\n" + 251 " tmpTargetPage = tmpTargetPage.substring(1);\n" + 252 " if (tmpTargetPage.indexOf(\":\") != -1 || (tmpTargetPage != \"\" && !validURL(tmpTargetPage)))\n" + 253 " tmpTargetPage = \"undefined\";\n" + 254 " targetPage = tmpTargetPage;\n" + 255 " function validURL(url) {\n" + 256 " try {\n" + 257 " url = decodeURIComponent(url);\n" + 258 " }\n" + 259 " catch (error) {\n" + 260 " return false;\n" + 261 " }\n" + 262 " var pos = url.indexOf(\".html\");\n" + 263 " if (pos == -1 || pos != url.length - 5)\n" + 264 " return false;\n" + 265 " var allowNumber = false;\n" + 266 " var allowSep = false;\n" + 267 " var seenDot = false;\n" + 268 " for (var i = 0; i < url.length - 5; i++) {\n" + 269 " var ch = url.charAt(i);\n" + 270 " if ('a' <= ch && ch <= 'z' ||\n" + 271 " 'A' <= ch && ch <= 'Z' ||\n" + 272 " ch == '$' ||\n" + 273 " ch == '_' ||\n" + 274 " ch.charCodeAt(0) > 127) {\n" + 275 " allowNumber = true;\n" + 276 " allowSep = true;\n" + 277 " } else if ('0' <= ch && ch <= '9'\n" + 278 " || ch == '-') {\n" + 279 " if (!allowNumber)\n" + 280 " return false;\n" + 281 " } else if (ch == '/' || ch == '.') {\n" + 282 " if (!allowSep)\n" + 283 " return false;\n" + 284 " allowNumber = false;\n" + 285 " allowSep = false;\n" + 286 " if (ch == '.')\n" + 287 " seenDot = true;\n" + 288 " if (ch == '/' && seenDot)\n" + 289 " return false;\n" + 290 " } else {\n" + 291 " return false;\n" + 292 " }\n" + 293 " }\n" + 294 " return true;\n" + 295 " }\n" + 296 " function loadFrames() {\n" + 297 " if (targetPage != \"\" && targetPage != \"undefined\")\n" + 298 " top.classFrame.location = top.targetPage;\n" + 299 " }\n"; 300 RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL)); 301 scriptTree.addContent(scriptContent); 302 return scriptTree; 303 } 304 305 /** 306 * Returns an HtmlTree for the BODY tag. 307 * 308 * @param includeScript set true if printing windowtitle script 309 * @param title title for the window 310 * @return an HtmlTree for the BODY tag 311 */ 312 public HtmlTree getBody(boolean includeScript, String title) { 313 HtmlTree body = new HtmlTree(HtmlTag.BODY); 314 // Set window title string which is later printed 315 this.winTitle = title; 316 // Don't print windowtitle script for overview-frame, allclasses-frame 317 // and package-frame 318 if (includeScript) { 319 this.script = getWinTitleScript(); 320 body.addContent(script); 321 Content noScript = HtmlTree.NOSCRIPT( 322 HtmlTree.DIV(configuration.getContent("doclet.No_Script_Message"))); 323 body.addContent(noScript); 324 } 325 return body; 326 } 327 328 /** 329 * Generated javascript variables for the document. 330 * 331 * @param typeMap map comprising of method and type relationship 332 * @param tabTypes set comprising of all table tab types for this class 333 * @param elementName packages or methods table for which tabs need to be displayed 334 */ 335 public void generateTableTabTypesScript(Map<String,Integer> typeMap, 336 Set<? extends TableTabTypes> tabTypes, String elementName) { 337 String sep = ""; 338 StringBuilder vars = new StringBuilder("var "); 339 vars.append(elementName) 340 .append(" = {"); 341 for (Map.Entry<String,Integer> entry : typeMap.entrySet()) { 342 vars.append(sep); 343 sep = ","; 344 vars.append("\"") 345 .append(entry.getKey()) 346 .append("\":") 347 .append(entry.getValue()); 348 } 349 vars.append("};").append(DocletConstants.NL); 350 sep = ""; 351 vars.append("var tabs = {"); 352 for (TableTabTypes entry : tabTypes) { 353 vars.append(sep); 354 sep = ","; 355 vars.append(entry.tableTabs().value()) 356 .append(":") 357 .append("[") 358 .append("\"") 359 .append(entry.tableTabs().tabId()) 360 .append("\"") 361 .append(sep) 362 .append("\"") 363 .append(configuration.getText(entry.tableTabs().resourceKey())) 364 .append("\"]"); 365 } 366 vars.append("};") 367 .append(DocletConstants.NL); 368 addStyles(HtmlStyle.altColor, vars); 369 addStyles(HtmlStyle.rowColor, vars); 370 addStyles(HtmlStyle.tableTab, vars); 371 addStyles(HtmlStyle.activeTableTab, vars); 372 script.addContent(new RawHtml(vars)); 373 } 374 375 /** 376 * Adds javascript style variables to the document. 377 * 378 * @param style style to be added as a javascript variable 379 * @param vars variable string to which the style variable will be added 380 */ 381 public void addStyles(HtmlStyle style, StringBuilder vars) { 382 vars.append("var ").append(style).append(" = \"").append(style) 383 .append("\";").append(DocletConstants.NL); 384 } 385 386 /** 387 * Returns an HtmlTree for the TITLE tag. 388 * 389 * @return an HtmlTree for the TITLE tag 390 */ 391 public HtmlTree getTitle() { 392 HtmlTree title = HtmlTree.TITLE(new StringContent(winTitle)); 393 return title; 394 } 395 396 /* 397 * Returns a header for Modifier and Type column of a table. 398 */ 399 public String getModifierTypeHeader() { 400 return modifierTypeHeader; 401 } 402 }