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 }