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 }