1 /*
   2  * Copyright (c) 1998, 2019, 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 java.io.IOException;
  29 import java.io.OutputStream;
  30 import java.io.Writer;
  31 import java.util.Collection;
  32 import java.util.List;
  33 import java.util.zip.ZipEntry;
  34 import java.util.zip.ZipOutputStream;
  35 
  36 import javax.lang.model.element.Element;
  37 import javax.lang.model.element.ExecutableElement;
  38 import javax.lang.model.element.ModuleElement;
  39 import javax.lang.model.element.PackageElement;
  40 import javax.lang.model.element.TypeElement;
  41 import javax.lang.model.util.SimpleElementVisitor14;
  42 
  43 import com.sun.source.doctree.DocTree;
  44 import jdk.javadoc.internal.doclets.formats.html.markup.Entity;
  45 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
  46 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
  47 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
  48 import jdk.javadoc.internal.doclets.formats.html.markup.Navigation;
  49 import jdk.javadoc.internal.doclets.formats.html.markup.Navigation.PageMode;
  50 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
  51 import jdk.javadoc.internal.doclets.toolkit.Content;
  52 import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
  53 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
  54 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
  55 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
  56 import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
  57 
  58 /**
  59  * Generate Index for all the Member Names with Indexing in
  60  * Unicode Order. This class is a base class for {@link SingleIndexWriter} and
  61  * {@link SplitIndexWriter}. It uses the functionality from
  62  * {@link HtmlDocletWriter} to generate the Index Contents.
  63  *
  64  *  <p><b>This is NOT part of any supported API.
  65  *  If you write code that depends on this, you do so at your own risk.
  66  *  This code and its internal interfaces are subject to change or
  67  *  deletion without notice.</b>
  68  *
  69  * @see    IndexBuilder
  70  */
  71 public class AbstractIndexWriter extends HtmlDocletWriter {
  72 
  73     /**
  74      * The index of all the members with unicode character.
  75      */
  76     protected IndexBuilder indexbuilder;
  77 
  78     protected Navigation navBar;
  79 
  80     /**
  81      * This constructor will be used by {@link SplitIndexWriter}. Initializes
  82      * path to this file and relative path from this file.
  83      *
  84      * @param configuration  The current configuration
  85      * @param path       Path to the file which is getting generated.
  86      * @param indexbuilder Unicode based Index from {@link IndexBuilder}
  87      */
  88     protected AbstractIndexWriter(HtmlConfiguration configuration,
  89                                   DocPath path,
  90                                   IndexBuilder indexbuilder) {
  91         super(configuration, path);
  92         this.indexbuilder = indexbuilder;
  93         this.navBar = new Navigation(null, configuration, PageMode.INDEX, path);
  94     }
  95 
  96     /**
  97      * Add the member information for the unicode character along with the
  98      * list of the members.
  99      *
 100      * @param uc Unicode for which member list information to be generated
 101      * @param memberlist List of members for the unicode character
 102      * @param contentTree the content tree to which the information will be added
 103      */
 104     protected void addContents(Character uc, Collection<? extends Element> memberlist,
 105             Content contentTree) {
 106         addHeading(uc, contentTree);
 107         // Display the list only if there are elements to be displayed.
 108         if (!memberlist.isEmpty()) {
 109             Content dl = new HtmlTree(HtmlTag.DL);
 110             for (Element element : memberlist) {
 111                 addDescription(dl, element);
 112             }
 113             contentTree.add(dl);
 114         }
 115     }
 116 
 117     protected void addSearchContents(Character uc, List<SearchIndexItem> searchList,
 118             Content contentTree) {
 119         addHeading(uc, contentTree);
 120         // Display the list only if there are elements to be displayed.
 121         if (!searchList.isEmpty()) {
 122             Content dl = new HtmlTree(HtmlTag.DL);
 123             for (SearchIndexItem sii : searchList) {
 124                 addDescription(sii, dl);
 125             }
 126             contentTree.add(dl);
 127         }
 128     }
 129 
 130     protected void addContents(Character uc, List<? extends Element> memberlist,
 131             List<SearchIndexItem> searchList, Content contentTree) {
 132         addHeading(uc, contentTree);
 133         int memberListSize = memberlist.size();
 134         int searchListSize = searchList.size();
 135         int i = 0;
 136         int j = 0;
 137         Content dl = new HtmlTree(HtmlTag.DL);
 138         while (i < memberListSize && j < searchListSize) {
 139             Element elem = memberlist.get(i);
 140             String name = (utils.isModule(elem))
 141                     ? utils.getFullyQualifiedName(elem) : utils.getSimpleName(elem);
 142             if (name.compareTo(searchList.get(j).getLabel()) < 0) {
 143                 addDescription(dl, memberlist.get(i));
 144                 i++;
 145             } else if (name.compareTo(searchList.get(j).getLabel()) > 0) {
 146                 addDescription(searchList.get(j), dl);
 147                 j++;
 148             } else {
 149                 addDescription(dl, memberlist.get(i));
 150                 addDescription(searchList.get(j), dl);
 151                 j++;
 152                 i++;
 153             }
 154         }
 155         if (i >= memberListSize) {
 156             while (j < searchListSize) {
 157                 addDescription(searchList.get(j), dl);
 158                 j++;
 159             }
 160         }
 161         if (j >= searchListSize) {
 162             while (i < memberListSize) {
 163                 addDescription(dl, memberlist.get(i));
 164                 i++;
 165             }
 166         }
 167         contentTree.add(dl);
 168     }
 169 
 170     protected void addHeading(Character uc, Content contentTree) {
 171         String unicode = uc.toString();
 172         Content headContent = new StringContent(unicode);
 173         HtmlTree heading = HtmlTree.HEADING(Headings.CONTENT_HEADING, false,
 174                 HtmlStyle.title, headContent);
 175         heading.setId(getNameForIndex(unicode));
 176         contentTree.add(heading);
 177     }
 178 
 179     @SuppressWarnings("preview")
 180     protected void addDescription(Content dl, Element element) {
 181         SearchIndexItem si = new SearchIndexItem();
 182         new SimpleElementVisitor14<Void, Void>() {
 183 
 184             @Override
 185             public Void visitModule(ModuleElement e, Void p) {
 186                 if (configuration.showModules) {
 187                     addDescription(e, dl, si);
 188                     configuration.moduleSearchIndex.add(si);
 189                 }
 190                 return null;
 191             }
 192 
 193             @Override
 194             public Void visitPackage(PackageElement e, Void p) {
 195                 addDescription(e, dl, si);
 196                 configuration.packageSearchIndex.add(si);
 197                 return null;
 198             }
 199 
 200             @Override
 201             public Void visitType(TypeElement e, Void p) {
 202                 addDescription(e, dl, si);
 203                 configuration.typeSearchIndex.add(si);
 204                 return null;
 205             }
 206 
 207             @Override
 208             protected Void defaultAction(Element e, Void p) {
 209                 addDescription(e, dl, si);
 210                 configuration.memberSearchIndex.add(si);
 211                 return null;
 212             }
 213 
 214         }.visit(element);
 215     }
 216 
 217     /**
 218      * Add one line summary comment for the module.
 219      *
 220      * @param mdle the module to be documented
 221      * @param dlTree the content tree to which the description will be added
 222      * @param si the search index item
 223      */
 224     protected void addDescription(ModuleElement mdle, Content dlTree, SearchIndexItem si) {
 225         String moduleName = utils.getFullyQualifiedName(mdle);
 226         Content link = getModuleLink(mdle, new StringContent(moduleName));
 227         si.setLabel(moduleName);
 228         si.setCategory(SearchIndexItem.Category.MODULES);
 229         Content dt = HtmlTree.DT(link);
 230         dt.add(" - ");
 231         dt.add(contents.module_);
 232         dt.add(" " + moduleName);
 233         dlTree.add(dt);
 234         Content dd = new HtmlTree(HtmlTag.DD);
 235         addSummaryComment(mdle, dd);
 236         dlTree.add(dd);
 237     }
 238 
 239     /**
 240      * Add one line summary comment for the package.
 241      *
 242      * @param pkg the package to be documented
 243      * @param dlTree the content tree to which the description will be added
 244      * @param si the search index item to be updated
 245      */
 246     protected void addDescription(PackageElement pkg, Content dlTree, SearchIndexItem si) {
 247         Content link = getPackageLink(pkg, new StringContent(utils.getPackageName(pkg)));
 248         if (configuration.showModules) {
 249             si.setContainingModule(utils.getFullyQualifiedName(utils.containingModule(pkg)));
 250         }
 251         si.setLabel(utils.getPackageName(pkg));
 252         si.setCategory(SearchIndexItem.Category.PACKAGES);
 253         Content dt = HtmlTree.DT(link);
 254         dt.add(" - ");
 255         dt.add(contents.package_);
 256         dt.add(" " + utils.getPackageName(pkg));
 257         dlTree.add(dt);
 258         Content dd = new HtmlTree(HtmlTag.DD);
 259         addSummaryComment(pkg, dd);
 260         dlTree.add(dd);
 261     }
 262 
 263     /**
 264      * Add one line summary comment for the class.
 265      *
 266      * @param typeElement the class being documented
 267      * @param dlTree the content tree to which the description will be added
 268      * @param si the search index item to be updated
 269      */
 270     protected void addDescription(TypeElement typeElement, Content dlTree, SearchIndexItem si) {
 271         Content link = getLink(new LinkInfoImpl(configuration,
 272                         LinkInfoImpl.Kind.INDEX, typeElement).strong(true));
 273         si.setContainingPackage(utils.getPackageName(utils.containingPackage(typeElement)));
 274         si.setLabel(utils.getSimpleName(typeElement));
 275         si.setCategory(SearchIndexItem.Category.TYPES);
 276         Content dt = HtmlTree.DT(link);
 277         dt.add(" - ");
 278         addClassInfo(typeElement, dt);
 279         dlTree.add(dt);
 280         Content dd = new HtmlTree(HtmlTag.DD);
 281         addComment(typeElement, dd);
 282         dlTree.add(dd);
 283     }
 284 
 285     /**
 286      * Add the classkind (class, interface, exception), error of the class
 287      * passed.
 288      *
 289      * @param te the class being documented
 290      * @param contentTree the content tree to which the class info will be added
 291      */
 292     protected void addClassInfo(TypeElement te, Content contentTree) {
 293         contentTree.add(contents.getContent("doclet.in",
 294                 utils.getTypeElementName(te, false),
 295                 getPackageLink(utils.containingPackage(te),
 296                     utils.getPackageName(utils.containingPackage(te)))
 297                 ));
 298     }
 299 
 300     /**
 301      * Add description for Class, Field, Method or Constructor.
 302      *
 303      * @param member the member of the Class Kind
 304      * @param dlTree the content tree to which the description will be added
 305      * @param si search index item
 306      */
 307     protected void addDescription(Element member, Content dlTree, SearchIndexItem si) {
 308 
 309         si.setContainingPackage(utils.getPackageName(utils.containingPackage(member)));
 310         si.setContainingClass(utils.getSimpleName(utils.getEnclosingTypeElement(member)));
 311         String name = utils.getSimpleName(member);
 312         if (utils.isExecutableElement(member)) {
 313             ExecutableElement ee = (ExecutableElement)member;
 314             name = name + utils.flatSignature(ee);
 315             si.setLabel(name);
 316             String url = HtmlTree.encodeURL(links.getName(getAnchor(ee)));
 317             if (!name.equals(url)) {
 318                 si.setUrl(url);
 319             }
 320         }  else {
 321             si.setLabel(name);
 322         }
 323         si.setCategory(SearchIndexItem.Category.MEMBERS);
 324         Content span = HtmlTree.SPAN(HtmlStyle.memberNameLink,
 325                 getDocLink(LinkInfoImpl.Kind.INDEX, member, name));
 326         Content dt = HtmlTree.DT(span);
 327         dt.add(" - ");
 328         addMemberDesc(member, dt);
 329         dlTree.add(dt);
 330         Content dd = new HtmlTree(HtmlTag.DD);
 331         addComment(member, dd);
 332         dlTree.add(dd);
 333     }
 334 
 335     protected void addDescription(SearchIndexItem sii, Content dlTree) {
 336         String siiPath = pathToRoot.isEmpty() ? "" : pathToRoot.getPath() + "/";
 337         siiPath += sii.getUrl();
 338         HtmlTree labelLink = HtmlTree.A(siiPath, new StringContent(sii.getLabel()));
 339         Content dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.searchTagLink, labelLink));
 340         dt.add(" - ");
 341         dt.add(contents.getContent("doclet.Search_tag_in", sii.getHolder()));
 342         dlTree.add(dt);
 343         Content dd = new HtmlTree(HtmlTag.DD);
 344         if (sii.getDescription().isEmpty()) {
 345             dd.add(Entity.NO_BREAK_SPACE);
 346         } else {
 347             dd.add(sii.getDescription());
 348         }
 349         dlTree.add(dd);
 350     }
 351 
 352     /**
 353      * Add comment for each element in the index. If the element is deprecated
 354      * and it has a @deprecated tag, use that comment. Else if the containing
 355      * class for this element is deprecated, then add the word "Deprecated." at
 356      * the start and then print the normal comment.
 357      *
 358      * @param element Index element
 359      * @param contentTree the content tree to which the comment will be added
 360      */
 361     protected void addComment(Element element, Content contentTree) {
 362         List<? extends DocTree> tags;
 363         Content span = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(element));
 364         HtmlTree div = new HtmlTree(HtmlTag.DIV);
 365         div.setStyle(HtmlStyle.deprecationBlock);
 366         if (utils.isDeprecated(element)) {
 367             div.add(span);
 368             tags = utils.getBlockTags(element, DocTree.Kind.DEPRECATED);
 369             if (!tags.isEmpty())
 370                 addInlineDeprecatedComment(element, tags.get(0), div);
 371             contentTree.add(div);
 372         } else {
 373             TypeElement encl = utils.getEnclosingTypeElement(element);
 374             while (encl != null) {
 375                 if (utils.isDeprecated(encl)) {
 376                     div.add(span);
 377                     contentTree.add(div);
 378                     break;
 379                 }
 380                 encl = utils.getEnclosingTypeElement(encl);
 381             }
 382             addSummaryComment(element, contentTree);
 383         }
 384     }
 385 
 386     /**
 387      * Add description about the Static Variable/Method/Constructor for a
 388      * member.
 389      *
 390      * @param member MemberDoc for the member within the Class Kind
 391      * @param contentTree the content tree to which the member description will be added
 392      */
 393     protected void addMemberDesc(Element member, Content contentTree) {
 394         TypeElement containing = utils.getEnclosingTypeElement(member);
 395         String classdesc = utils.getTypeElementName(containing, true) + " ";
 396         if (utils.isField(member)) {
 397             Content resource = contents.getContent(utils.isStatic(member)
 398                     ? "doclet.Static_variable_in"
 399                     : "doclet.Variable_in", classdesc);
 400             contentTree.add(resource);
 401         } else if (utils.isConstructor(member)) {
 402             contentTree.add(
 403                     contents.getContent("doclet.Constructor_for", classdesc));
 404         } else if (utils.isMethod(member)) {
 405             Content resource = contents.getContent(utils.isStatic(member)
 406                     ? "doclet.Static_method_in"
 407                     : "doclet.Method_in", classdesc);
 408             contentTree.add(resource);
 409         }
 410         addPreQualifiedClassLink(LinkInfoImpl.Kind.INDEX, containing,
 411                 false, contentTree);
 412     }
 413 
 414     /**
 415      * Generate a valid HTML name for member index page.
 416      *
 417      * @param unicode the string that needs to be converted to valid HTML name.
 418      * @return a valid HTML name string.
 419      */
 420     public String getNameForIndex(String unicode) {
 421         return "I:" + links.getName(unicode);
 422     }
 423 
 424     /**
 425      * @throws DocFileIOException if there is a problem creating any of the search index files
 426      */
 427     protected void createSearchIndexFiles() throws DocFileIOException {
 428         if (configuration.showModules) {
 429             createSearchIndexFile(DocPaths.MODULE_SEARCH_INDEX_JS,
 430                                   configuration.moduleSearchIndex,
 431                                   "moduleSearchIndex");
 432         }
 433         if (!configuration.packages.isEmpty()) {
 434             SearchIndexItem si = new SearchIndexItem();
 435             si.setCategory(SearchIndexItem.Category.PACKAGES);
 436             si.setLabel(resources.getText("doclet.All_Packages"));
 437             si.setUrl(DocPaths.ALLPACKAGES_INDEX.getPath());
 438             configuration.packageSearchIndex.add(si);
 439         }
 440         createSearchIndexFile(DocPaths.PACKAGE_SEARCH_INDEX_JS,
 441                               configuration.packageSearchIndex,
 442                               "packageSearchIndex");
 443         SearchIndexItem si = new SearchIndexItem();
 444         si.setCategory(SearchIndexItem.Category.TYPES);
 445         si.setLabel(resources.getText("doclet.All_Classes"));
 446         si.setUrl(DocPaths.ALLCLASSES_INDEX.getPath());
 447         configuration.typeSearchIndex.add(si);
 448         createSearchIndexFile(DocPaths.TYPE_SEARCH_INDEX_JS,
 449                               configuration.typeSearchIndex,
 450                               "typeSearchIndex");
 451         createSearchIndexFile(DocPaths.MEMBER_SEARCH_INDEX_JS,
 452                               configuration.memberSearchIndex,
 453                               "memberSearchIndex");
 454         createSearchIndexFile(DocPaths.TAG_SEARCH_INDEX_JS,
 455                               configuration.tagSearchIndex,
 456                               "tagSearchIndex");
 457     }
 458 
 459     /**
 460      * Creates a search index file.
 461      *
 462      * @param searchIndexJS     the file for the JavaScript to be generated
 463      * @param searchIndex       the search index items
 464      * @param varName           the variable name to write in the JavaScript file
 465      * @throws DocFileIOException if there is a problem creating the search index file
 466      */
 467     protected void createSearchIndexFile(DocPath searchIndexJS,
 468                                          Collection<SearchIndexItem> searchIndex,
 469                                          String varName)
 470             throws DocFileIOException
 471     {
 472         if (!searchIndex.isEmpty()) { // TODO: write to disk straight
 473             StringBuilder searchVar = new StringBuilder("[");
 474             boolean first = true;
 475             for (SearchIndexItem item : searchIndex) {
 476                 if (first) {
 477                     searchVar.append(item.toString());
 478                     first = false;
 479                 } else {
 480                     searchVar.append(",").append(item.toString());
 481                 }
 482             }
 483             searchVar.append("]");
 484             DocFile jsFile = DocFile.createFileForOutput(configuration, searchIndexJS);
 485             try (Writer wr = jsFile.openWriter()) {
 486                 wr.write(varName);
 487                 wr.write(" = ");
 488                 wr.write(searchVar.toString());
 489             } catch (IOException ie) {
 490                 throw new DocFileIOException(jsFile, DocFileIOException.Mode.WRITE, ie);
 491             }
 492         }
 493     }
 494 }