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 }