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 }