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  * @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     @SuppressWarnings("preview")
 182     protected void addDescription(Content dl, Element element) {
 183         SearchIndexItem si = new SearchIndexItem();
 184         new SimpleElementVisitor14<Void, Void>() {
 185 
 186             @Override
 187             public Void visitModule(ModuleElement e, Void p) {
 188                 if (configuration.showModules) {
 189                     addDescription(e, dl, si);
 190                     configuration.moduleSearchIndex.add(si);
 191                 }
 192                 return null;
 193             }
 194 
 195             @Override
 196             public Void visitPackage(PackageElement e, Void p) {
 197                 addDescription(e, dl, si);
 198                 configuration.packageSearchIndex.add(si);
 199                 return null;
 200             }
 201 
 202             @Override
 203             public Void visitType(TypeElement e, Void p) {
 204                 addDescription(e, dl, si);
 205                 configuration.typeSearchIndex.add(si);
 206                 return null;
 207             }
 208 
 209             @Override
 210             protected Void defaultAction(Element e, Void p) {
 211                 addDescription(e, dl, si);
 212                 configuration.memberSearchIndex.add(si);
 213                 return null;
 214             }
 215 
 216         }.visit(element);
 217     }
 218 
 219     /**
 220      * Add one line summary comment for the module.
 221      *
 222      * @param mdle the module to be documented
 223      * @param dlTree the content tree to which the description will be added
 224      * @param si the search index item
 225      */
 226     protected void addDescription(ModuleElement mdle, Content dlTree, SearchIndexItem si) {
 227         String moduleName = utils.getFullyQualifiedName(mdle);
 228         Content link = getModuleLink(mdle, new StringContent(moduleName));
 229         si.setLabel(moduleName);
 230         si.setCategory(SearchIndexItem.Category.MODULES);
 231         Content dt = HtmlTree.DT(link);
 232         dt.add(" - ");
 233         dt.add(contents.module_);
 234         dt.add(" " + moduleName);
 235         dlTree.add(dt);
 236         Content dd = new HtmlTree(HtmlTag.DD);
 237         addSummaryComment(mdle, dd);
 238         dlTree.add(dd);
 239     }
 240 
 241     /**
 242      * Add one line summary comment for the package.
 243      *
 244      * @param pkg the package to be documented
 245      * @param dlTree the content tree to which the description will be added
 246      * @param si the search index item to be updated
 247      */
 248     protected void addDescription(PackageElement pkg, Content dlTree, SearchIndexItem si) {
 249         Content link = getPackageLink(pkg, new StringContent(utils.getPackageName(pkg)));
 250         if (configuration.showModules) {
 251             si.setContainingModule(utils.getFullyQualifiedName(utils.containingModule(pkg)));
 252         }
 253         si.setLabel(utils.getPackageName(pkg));
 254         si.setCategory(SearchIndexItem.Category.PACKAGES);
 255         Content dt = HtmlTree.DT(link);
 256         dt.add(" - ");
 257         dt.add(contents.package_);
 258         dt.add(" " + utils.getPackageName(pkg));
 259         dlTree.add(dt);
 260         Content dd = new HtmlTree(HtmlTag.DD);
 261         addSummaryComment(pkg, dd);
 262         dlTree.add(dd);
 263     }
 264 
 265     /**
 266      * Add one line summary comment for the class.
 267      *
 268      * @param typeElement the class being documented
 269      * @param dlTree the content tree to which the description will be added
 270      * @param si the search index item to be updated
 271      */
 272     protected void addDescription(TypeElement typeElement, Content dlTree, SearchIndexItem si) {
 273         Content link = getLink(new LinkInfoImpl(configuration,
 274                         LinkInfoImpl.Kind.INDEX, typeElement).strong(true));
 275         si.setContainingPackage(utils.getPackageName(utils.containingPackage(typeElement)));
 276         si.setLabel(utils.getSimpleName(typeElement));
 277         si.setCategory(SearchIndexItem.Category.TYPES);
 278         Content dt = HtmlTree.DT(link);
 279         dt.add(" - ");
 280         addClassInfo(typeElement, dt);
 281         dlTree.add(dt);
 282         Content dd = new HtmlTree(HtmlTag.DD);
 283         addComment(typeElement, dd);
 284         dlTree.add(dd);
 285     }
 286 
 287     /**
 288      * Add the classkind (class, interface, exception), error of the class
 289      * passed.
 290      *
 291      * @param te the class being documented
 292      * @param contentTree the content tree to which the class info will be added
 293      */
 294     protected void addClassInfo(TypeElement te, Content contentTree) {
 295         contentTree.add(contents.getContent("doclet.in",
 296                 utils.getTypeElementName(te, false),
 297                 getPackageLink(utils.containingPackage(te),
 298                     utils.getPackageName(utils.containingPackage(te)))
 299                 ));
 300     }
 301 
 302     /**
 303      * Add description for Class, Field, Method or Constructor.
 304      *
 305      * @param member the member of the Class Kind
 306      * @param dlTree the content tree to which the description will be added
 307      * @param si search index item
 308      */
 309     protected void addDescription(Element member, Content dlTree, SearchIndexItem si) {
 310 
 311         si.setContainingPackage(utils.getPackageName(utils.containingPackage(member)));
 312         si.setContainingClass(utils.getSimpleName(utils.getEnclosingTypeElement(member)));
 313         String name = utils.getSimpleName(member);
 314         if (utils.isExecutableElement(member)) {
 315             ExecutableElement ee = (ExecutableElement)member;
 316             name = name + utils.flatSignature(ee);
 317             si.setLabel(name);
 318             String url = HtmlTree.encodeURL(links.getName(getAnchor(ee)));
 319             if (!name.equals(url)) {
 320                 si.setUrl(url);
 321             }
 322         }  else {
 323             si.setLabel(name);
 324         }
 325         si.setCategory(SearchIndexItem.Category.MEMBERS);
 326         Content span = HtmlTree.SPAN(HtmlStyle.memberNameLink,
 327                 getDocLink(LinkInfoImpl.Kind.INDEX, member, name));
 328         Content dt = HtmlTree.DT(span);
 329         dt.add(" - ");
 330         addMemberDesc(member, dt);
 331         dlTree.add(dt);
 332         Content dd = new HtmlTree(HtmlTag.DD);
 333         addComment(member, dd);
 334         dlTree.add(dd);
 335     }
 336 
 337     protected void addDescription(SearchIndexItem sii, Content dlTree) {
 338         String siiPath = pathToRoot.isEmpty() ? "" : pathToRoot.getPath() + "/";
 339         siiPath += sii.getUrl();
 340         HtmlTree labelLink = HtmlTree.A(siiPath, new StringContent(sii.getLabel()));
 341         Content dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.searchTagLink, labelLink));
 342         dt.add(" - ");
 343         dt.add(contents.getContent("doclet.Search_tag_in", sii.getHolder()));
 344         dlTree.add(dt);
 345         Content dd = new HtmlTree(HtmlTag.DD);
 346         if (sii.getDescription().isEmpty()) {
 347             dd.add(Entity.NO_BREAK_SPACE);
 348         } else {
 349             dd.add(sii.getDescription());
 350         }
 351         dlTree.add(dd);
 352     }
 353 
 354     /**
 355      * Add comment for each element in the index. If the element is deprecated
 356      * and it has a @deprecated tag, use that comment. Else if the containing
 357      * class for this element is deprecated, then add the word "Deprecated." at
 358      * the start and then print the normal comment.
 359      *
 360      * @param element Index element
 361      * @param contentTree the content tree to which the comment will be added
 362      */
 363     protected void addComment(Element element, Content contentTree) {
 364         List<? extends DocTree> tags;
 365         Content span = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(element));
 366         HtmlTree div = new HtmlTree(HtmlTag.DIV);
 367         div.setStyle(HtmlStyle.deprecationBlock);
 368         if (utils.isDeprecated(element)) {
 369             div.add(span);
 370             tags = utils.getBlockTags(element, DocTree.Kind.DEPRECATED);
 371             if (!tags.isEmpty())
 372                 addInlineDeprecatedComment(element, tags.get(0), div);
 373             contentTree.add(div);
 374         } else {
 375             TypeElement encl = utils.getEnclosingTypeElement(element);
 376             while (encl != null) {
 377                 if (utils.isDeprecated(encl)) {
 378                     div.add(span);
 379                     contentTree.add(div);
 380                     break;
 381                 }
 382                 encl = utils.getEnclosingTypeElement(encl);
 383             }
 384             addSummaryComment(element, contentTree);
 385         }
 386     }
 387 
 388     /**
 389      * Add description about the Static Variable/Method/Constructor for a
 390      * member.
 391      *
 392      * @param member MemberDoc for the member within the Class Kind
 393      * @param contentTree the content tree to which the member description will be added
 394      */
 395     protected void addMemberDesc(Element member, Content contentTree) {
 396         TypeElement containing = utils.getEnclosingTypeElement(member);
 397         String classdesc = utils.getTypeElementName(containing, true) + " ";
 398         if (utils.isField(member)) {
 399             Content resource = contents.getContent(utils.isStatic(member)
 400                     ? "doclet.Static_variable_in"
 401                     : "doclet.Variable_in", classdesc);
 402             contentTree.add(resource);
 403         } else if (utils.isConstructor(member)) {
 404             contentTree.add(
 405                     contents.getContent("doclet.Constructor_for", classdesc));
 406         } else if (utils.isMethod(member)) {
 407             Content resource = contents.getContent(utils.isStatic(member)
 408                     ? "doclet.Static_method_in"
 409                     : "doclet.Method_in", classdesc);
 410             contentTree.add(resource);
 411         }
 412         addPreQualifiedClassLink(LinkInfoImpl.Kind.INDEX, containing,
 413                 false, contentTree);
 414     }
 415 
 416     /**
 417      * Get the marker anchor which will be added to the index documentation tree.
 418      *
 419      * @param anchorNameForIndex the anchor name attribute for index page
 420      * @return a content tree for the marker anchor
 421      */
 422     public Content getMarkerAnchorForIndex(String anchorNameForIndex) {
 423         return links.createAnchor(getNameForIndex(anchorNameForIndex), null);
 424     }
 425 
 426     /**
 427      * Generate a valid HTML name for member index page.
 428      *
 429      * @param unicode the string that needs to be converted to valid HTML name.
 430      * @return a valid HTML name string.
 431      */
 432     public String getNameForIndex(String unicode) {
 433         return "I:" + links.getName(unicode);
 434     }
 435 
 436     /**
 437      * @throws DocFileIOException if there is a problem creating any of the search index files
 438      */
 439     protected void createSearchIndexFiles() throws DocFileIOException {
 440         if (configuration.showModules) {
 441             createSearchIndexFile(DocPaths.MODULE_SEARCH_INDEX_JSON, DocPaths.MODULE_SEARCH_INDEX_ZIP,
 442                     DocPaths.MODULE_SEARCH_INDEX_JS, configuration.moduleSearchIndex, "moduleSearchIndex");
 443         }
 444         if (!configuration.packages.isEmpty()) {
 445             SearchIndexItem si = new SearchIndexItem();
 446             si.setCategory(SearchIndexItem.Category.PACKAGES);
 447             si.setLabel(resources.getText("doclet.All_Packages"));
 448             si.setUrl(DocPaths.ALLPACKAGES_INDEX.getPath());
 449             configuration.packageSearchIndex.add(si);
 450         }
 451         createSearchIndexFile(DocPaths.PACKAGE_SEARCH_INDEX_JSON, DocPaths.PACKAGE_SEARCH_INDEX_ZIP,
 452                 DocPaths.PACKAGE_SEARCH_INDEX_JS, configuration.packageSearchIndex, "packageSearchIndex");
 453         SearchIndexItem si = new SearchIndexItem();
 454         si.setCategory(SearchIndexItem.Category.TYPES);
 455         si.setLabel(resources.getText("doclet.All_Classes"));
 456         si.setUrl(DocPaths.ALLCLASSES_INDEX.getPath());
 457         configuration.typeSearchIndex.add(si);
 458         createSearchIndexFile(DocPaths.TYPE_SEARCH_INDEX_JSON, DocPaths.TYPE_SEARCH_INDEX_ZIP,
 459                 DocPaths.TYPE_SEARCH_INDEX_JS, configuration.typeSearchIndex, "typeSearchIndex");
 460         createSearchIndexFile(DocPaths.MEMBER_SEARCH_INDEX_JSON, DocPaths.MEMBER_SEARCH_INDEX_ZIP,
 461                 DocPaths.MEMBER_SEARCH_INDEX_JS, configuration.memberSearchIndex, "memberSearchIndex");
 462         createSearchIndexFile(DocPaths.TAG_SEARCH_INDEX_JSON, DocPaths.TAG_SEARCH_INDEX_ZIP,
 463                 DocPaths.TAG_SEARCH_INDEX_JS, configuration.tagSearchIndex, "tagSearchIndex");
 464     }
 465 
 466     /**
 467      * Creates a search index file.
 468      *
 469      * @param searchIndexFile   the file to be generated
 470      * @param searchIndexZip    the zip file to be generated
 471      * @param searchIndexJS     the file for the JavaScript to be generated
 472      * @param searchIndex       the search index items
 473      * @param varName           the variable name to write in the JavaScript file
 474      * @throws DocFileIOException if there is a problem creating the search index file
 475      */
 476     protected void createSearchIndexFile(DocPath searchIndexFile, DocPath searchIndexZip,
 477             DocPath searchIndexJS, Collection<SearchIndexItem> searchIndex, String varName) throws DocFileIOException {
 478         if (!searchIndex.isEmpty()) {
 479             StringBuilder searchVar = new StringBuilder("[");
 480             boolean first = true;
 481             for (SearchIndexItem item : searchIndex) {
 482                 if (first) {
 483                     searchVar.append(item.toString());
 484                     first = false;
 485                 } else {
 486                     searchVar.append(",").append(item.toString());
 487                 }
 488             }
 489             searchVar.append("]");
 490             DocFile jsFile = DocFile.createFileForOutput(configuration, searchIndexJS);
 491             try (Writer wr = jsFile.openWriter()) {
 492                 wr.write(varName);
 493                 wr.write(" = ");
 494                 wr.write(searchVar.toString());
 495             } catch (IOException ie) {
 496                 throw new DocFileIOException(jsFile, DocFileIOException.Mode.WRITE, ie);
 497             }
 498 
 499             DocFile zipFile = DocFile.createFileForOutput(configuration, searchIndexZip);
 500             try (OutputStream fos = zipFile.openOutputStream();
 501                     ZipOutputStream zos = new ZipOutputStream(fos)) {
 502                 try {
 503                     ZipEntry ze = new ZipEntry(searchIndexFile.getPath());
 504                     zos.putNextEntry(ze);
 505                     zos.write(searchVar.toString().getBytes());
 506                 } finally {
 507                     zos.closeEntry();
 508                 }
 509             } catch (IOException ie) {
 510                 throw new DocFileIOException(zipFile, DocFileIOException.Mode.WRITE, ie);
 511             }
 512         }
 513     }
 514 }