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