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