1 /* 2 * Copyright (c) 1997, 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.util.Collection; 29 import java.util.List; 30 import java.util.Set; 31 import java.util.SortedSet; 32 import java.util.TreeSet; 33 34 import javax.lang.model.element.AnnotationMirror; 35 import javax.lang.model.element.Element; 36 import javax.lang.model.element.ModuleElement; 37 import javax.lang.model.element.PackageElement; 38 import javax.lang.model.element.RecordComponentElement; 39 import javax.lang.model.element.TypeElement; 40 import javax.lang.model.type.TypeMirror; 41 import javax.lang.model.util.SimpleElementVisitor8; 42 43 import com.sun.source.doctree.DocTree; 44 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; 45 import jdk.javadoc.internal.doclets.formats.html.markup.Entity; 46 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; 47 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; 48 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; 49 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; 50 import jdk.javadoc.internal.doclets.formats.html.markup.Navigation; 51 import jdk.javadoc.internal.doclets.formats.html.markup.Navigation.PageMode; 52 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; 53 import jdk.javadoc.internal.doclets.toolkit.ClassWriter; 54 import jdk.javadoc.internal.doclets.toolkit.Content; 55 import jdk.javadoc.internal.doclets.toolkit.taglets.ParamTaglet; 56 import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; 57 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; 58 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; 59 import jdk.javadoc.internal.doclets.toolkit.util.DocPath; 60 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; 61 62 /** 63 * Generate the Class Information Page. 64 * 65 * <p><b>This is NOT part of any supported API. 66 * If you write code that depends on this, you do so at your own risk. 67 * This code and its internal interfaces are subject to change or 68 * deletion without notice.</b> 69 * 70 * @see javax.lang.model.element.TypeElement 71 * @see java.util.Collections 72 * @see java.util.List 73 * @see java.util.ArrayList 74 * @see java.util.HashMap 75 */ 76 public class ClassWriterImpl extends SubWriterHolderWriter implements ClassWriter { 77 78 private static final Set<String> suppressSubtypesSet 79 = Set.of("java.lang.Object", 80 "org.omg.CORBA.Object"); 81 82 private static final Set<String> suppressImplementingSet 83 = Set.of( "java.lang.Cloneable", 84 "java.lang.constant.Constable", 85 "java.lang.constant.ConstantDesc", 86 "java.io.Serializable"); 87 88 protected final TypeElement typeElement; 89 90 protected final ClassTree classtree; 91 92 private final Navigation navBar; 93 94 /** 95 * @param configuration the configuration data for the doclet 96 * @param typeElement the class being documented. 97 * @param classTree the class tree for the given class. 98 */ 99 public ClassWriterImpl(HtmlConfiguration configuration, TypeElement typeElement, 100 ClassTree classTree) { 101 super(configuration, configuration.docPaths.forClass(typeElement)); 102 this.typeElement = typeElement; 103 configuration.currentTypeElement = typeElement; 104 this.classtree = classTree; 105 this.navBar = new Navigation(typeElement, configuration, PageMode.CLASS, path); 106 } 107 108 /** 109 * {@inheritDoc} 110 */ 111 @Override 112 public Content getHeader(String header) { 113 HtmlTree bodyTree = getBody(getWindowTitle(utils.getSimpleName(typeElement))); 114 Content headerContent = new ContentBuilder(); 115 addTop(headerContent); 116 Content linkContent = getModuleLink(utils.elementUtils.getModuleOf(typeElement), 117 contents.moduleLabel); 118 navBar.setNavLinkModule(linkContent); 119 navBar.setMemberSummaryBuilder(configuration.getBuilderFactory().getMemberSummaryBuilder(this)); 120 navBar.setUserHeader(getUserHeaderFooter(true)); 121 headerContent.add(navBar.getContent(true)); 122 HtmlTree div = new HtmlTree(HtmlTag.DIV); 123 div.setStyle(HtmlStyle.header); 124 if (configuration.showModules) { 125 ModuleElement mdle = configuration.docEnv.getElementUtils().getModuleOf(typeElement); 126 Content classModuleLabel = HtmlTree.SPAN(HtmlStyle.moduleLabelInType, contents.moduleLabel); 127 Content moduleNameDiv = HtmlTree.DIV(HtmlStyle.subTitle, classModuleLabel); 128 moduleNameDiv.add(Entity.NO_BREAK_SPACE); 129 moduleNameDiv.add(getModuleLink(mdle, 130 new StringContent(mdle.getQualifiedName()))); 131 div.add(moduleNameDiv); 132 } 133 PackageElement pkg = utils.containingPackage(typeElement); 134 if (!pkg.isUnnamed()) { 135 Content classPackageLabel = HtmlTree.SPAN(HtmlStyle.packageLabelInType, contents.packageLabel); 136 Content pkgNameDiv = HtmlTree.DIV(HtmlStyle.subTitle, classPackageLabel); 137 pkgNameDiv.add(Entity.NO_BREAK_SPACE); 138 Content pkgNameContent = getPackageLink(pkg, 139 new StringContent(utils.getPackageName(pkg))); 140 pkgNameDiv.add(pkgNameContent); 141 div.add(pkgNameDiv); 142 } 143 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 144 LinkInfoImpl.Kind.CLASS_HEADER, typeElement); 145 //Let's not link to ourselves in the header. 146 linkInfo.linkToSelf = false; 147 Content heading = HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, true, 148 HtmlStyle.title, new StringContent(header)); 149 heading.add(getTypeParameterLinks(linkInfo)); 150 div.add(heading); 151 bodyContents.setHeader(headerContent) 152 .addMainContent(MarkerComments.START_OF_CLASS_DATA) 153 .addMainContent(div); 154 return bodyTree; 155 } 156 157 /** 158 * {@inheritDoc} 159 */ 160 @Override 161 public Content getClassContentHeader() { 162 return getContentHeader(); 163 } 164 165 /** 166 * {@inheritDoc} 167 */ 168 @Override 169 public void addFooter() { 170 bodyContents.addMainContent(MarkerComments.END_OF_CLASS_DATA); 171 Content htmlTree = HtmlTree.FOOTER(); 172 navBar.setUserFooter(getUserHeaderFooter(false)); 173 htmlTree.add(navBar.getContent(false)); 174 addBottom(htmlTree); 175 bodyContents.setFooter(htmlTree); 176 } 177 178 /** 179 * {@inheritDoc} 180 */ 181 @Override 182 public void printDocument(Content contentTree) throws DocFileIOException { 183 String description = getDescription("declaration", typeElement); 184 PackageElement pkg = utils.containingPackage(typeElement); 185 List<DocPath> localStylesheets = getLocalStylesheets(pkg); 186 contentTree.add(bodyContents.toContent()); 187 printHtmlDocument(configuration.metakeywords.getMetaKeywords(typeElement), 188 description, localStylesheets, contentTree); 189 } 190 191 /** 192 * {@inheritDoc} 193 */ 194 @Override 195 public Content getClassInfoTreeHeader() { 196 return getMemberTreeHeader(); 197 } 198 199 /** 200 * {@inheritDoc} 201 */ 202 @Override 203 public Content getClassInfo(Content classInfoTree) { 204 return getMemberTree(HtmlStyle.description, classInfoTree); 205 } 206 207 /** 208 * {@inheritDoc} 209 */ 210 @Override 211 protected TypeElement getCurrentPageElement() { 212 return typeElement; 213 } 214 215 /** 216 * {@inheritDoc} 217 */ 218 @Override @SuppressWarnings("preview") 219 public void addClassSignature(String modifiers, Content classInfoTree) { 220 Content hr = new HtmlTree(HtmlTag.HR); 221 classInfoTree.add(hr); 222 Content pre = new HtmlTree(HtmlTag.PRE); 223 addAnnotationInfo(typeElement, pre); 224 pre.add(modifiers); 225 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 226 LinkInfoImpl.Kind.CLASS_SIGNATURE, typeElement); 227 //Let's not link to ourselves in the signature. 228 linkInfo.linkToSelf = false; 229 Content className = new StringContent(utils.getSimpleName(typeElement)); 230 Content parameterLinks = getTypeParameterLinks(linkInfo); 231 if (configuration.getOptions().linkSource) { 232 addSrcLink(typeElement, className, pre); 233 pre.add(parameterLinks); 234 } else { 235 Content span = HtmlTree.SPAN(HtmlStyle.typeNameLabel, className); 236 span.add(parameterLinks); 237 pre.add(span); 238 } 239 if (utils.isRecord(typeElement)) { 240 pre.add(getRecordComponents(typeElement)); 241 } 242 if (!utils.isInterface(typeElement)) { 243 TypeMirror superclass = utils.getFirstVisibleSuperClass(typeElement); 244 if (superclass != null) { 245 pre.add(DocletConstants.NL); 246 pre.add("extends "); 247 Content link = getLink(new LinkInfoImpl(configuration, 248 LinkInfoImpl.Kind.CLASS_SIGNATURE_PARENT_NAME, 249 superclass)); 250 pre.add(link); 251 } 252 } 253 List<? extends TypeMirror> interfaces = typeElement.getInterfaces(); 254 if (!interfaces.isEmpty()) { 255 boolean isFirst = true; 256 for (TypeMirror type : interfaces) { 257 TypeElement tDoc = utils.asTypeElement(type); 258 if (!(utils.isPublic(tDoc) || utils.isLinkable(tDoc))) { 259 continue; 260 } 261 if (isFirst) { 262 pre.add(DocletConstants.NL); 263 pre.add(utils.isInterface(typeElement) ? "extends " : "implements "); 264 isFirst = false; 265 } else { 266 pre.add(", "); 267 } 268 Content link = getLink(new LinkInfoImpl(configuration, 269 LinkInfoImpl.Kind.CLASS_SIGNATURE_PARENT_NAME, 270 type)); 271 pre.add(link); 272 } 273 } 274 classInfoTree.add(pre); 275 } 276 277 @SuppressWarnings("preview") 278 private Content getRecordComponents(TypeElement typeElem) { 279 Content content = new ContentBuilder(); 280 content.add("("); 281 String sep = ""; 282 for (RecordComponentElement e : typeElement.getRecordComponents()) { 283 content.add(sep); 284 getAnnotations(e.getAnnotationMirrors(), false).stream() 285 .forEach(a -> { content.add(a); content.add(" "); }); 286 Content link = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.RECORD_COMPONENT, 287 e.asType())); 288 content.add(link); 289 content.add(Entity.NO_BREAK_SPACE); 290 content.add(e.getSimpleName()); 291 sep = ", "; 292 } 293 content.add(")"); 294 return content; 295 } 296 297 /** 298 * {@inheritDoc} 299 */ 300 @Override 301 public void addClassDescription(Content classInfoTree) { 302 if(!configuration.getOptions().noComment) { 303 // generate documentation for the class. 304 if (!utils.getFullBody(typeElement).isEmpty()) { 305 addInlineComment(typeElement, classInfoTree); 306 } 307 } 308 } 309 310 /** 311 * {@inheritDoc} 312 */ 313 @Override 314 public void addClassTagInfo(Content classInfoTree) { 315 if(!configuration.getOptions().noComment) { 316 // Print Information about all the tags here 317 addTagsInfo(typeElement, classInfoTree); 318 } 319 } 320 321 /** 322 * Get the class hierarchy tree for the given class. 323 * 324 * @param type the class to print the hierarchy for 325 * @return a content tree for class inheritance 326 */ 327 private Content getClassInheritanceTree(TypeMirror type) { 328 TypeMirror sup; 329 HtmlTree classTree = null; 330 do { 331 sup = utils.getFirstVisibleSuperClass(type); 332 HtmlTree htmlElement = HtmlTree.DIV(HtmlStyle.inheritance, getTreeForClassHelper(type)); 333 if (classTree != null) 334 htmlElement.add(classTree); 335 classTree = htmlElement; 336 type = sup; 337 } while (sup != null); 338 classTree.put(HtmlAttr.TITLE, contents.getContent("doclet.Inheritance_Tree").toString()); 339 return classTree; 340 } 341 342 /** 343 * Get the class helper tree for the given class. 344 * 345 * @param type the class to print the helper for 346 * @return a content tree for class helper 347 */ 348 private Content getTreeForClassHelper(TypeMirror type) { 349 Content content = new ContentBuilder(); 350 if (type.equals(typeElement.asType())) { 351 Content typeParameters = getTypeParameterLinks( 352 new LinkInfoImpl(configuration, LinkInfoImpl.Kind.TREE, 353 typeElement)); 354 if (configuration.shouldExcludeQualifier(utils.containingPackage(typeElement).toString())) { 355 content.add(utils.asTypeElement(type).getSimpleName()); 356 content.add(typeParameters); 357 } else { 358 content.add(utils.asTypeElement(type).getQualifiedName()); 359 content.add(typeParameters); 360 } 361 } else { 362 Content link = getLink(new LinkInfoImpl(configuration, 363 LinkInfoImpl.Kind.CLASS_TREE_PARENT, type) 364 .label(configuration.getClassName(utils.asTypeElement(type)))); 365 content.add(link); 366 } 367 return content; 368 } 369 370 /** 371 * {@inheritDoc} 372 */ 373 @Override 374 public void addClassTree(Content classContentTree) { 375 if (!utils.isClass(typeElement)) { 376 return; 377 } 378 classContentTree.add(getClassInheritanceTree(typeElement.asType())); 379 } 380 381 /** 382 * {@inheritDoc} 383 */ 384 @Override 385 public void addParamInfo(Content classInfoTree) { 386 if (utils.hasBlockTag(typeElement, DocTree.Kind.PARAM)) { 387 Content paramInfo = (new ParamTaglet()).getTagletOutput(typeElement, 388 getTagletWriterInstance(false)); 389 if (!paramInfo.isEmpty()) { 390 classInfoTree.add(HtmlTree.DL(paramInfo)); 391 } 392 } 393 } 394 395 /** 396 * {@inheritDoc} 397 */ 398 @Override 399 public void addSubClassInfo(Content classInfoTree) { 400 if (utils.isClass(typeElement)) { 401 for (String s : suppressSubtypesSet) { 402 if (typeElement.getQualifiedName().contentEquals(s)) { 403 return; // Don't generate the list, too huge 404 } 405 } 406 Set<TypeElement> subclasses = classtree.directSubClasses(typeElement, false); 407 if (!subclasses.isEmpty()) { 408 Content label = contents.subclassesLabel; 409 Content dt = HtmlTree.DT(label); 410 Content dl = HtmlTree.DL(dt); 411 dl.add(getClassLinks(LinkInfoImpl.Kind.SUBCLASSES, 412 subclasses)); 413 classInfoTree.add(dl); 414 } 415 } 416 } 417 418 /** 419 * {@inheritDoc} 420 */ 421 @Override 422 public void addSubInterfacesInfo(Content classInfoTree) { 423 if (utils.isInterface(typeElement)) { 424 Set<TypeElement> subInterfaces = classtree.allSubClasses(typeElement, false); 425 if (!subInterfaces.isEmpty()) { 426 Content label = contents.subinterfacesLabel; 427 Content dt = HtmlTree.DT(label); 428 Content dl = HtmlTree.DL(dt); 429 dl.add(getClassLinks(LinkInfoImpl.Kind.SUBINTERFACES, 430 subInterfaces)); 431 classInfoTree.add(dl); 432 } 433 } 434 } 435 436 /** 437 * {@inheritDoc} 438 */ 439 @Override 440 public void addInterfaceUsageInfo (Content classInfoTree) { 441 if (!utils.isInterface(typeElement)) { 442 return; 443 } 444 for (String s : suppressImplementingSet) { 445 if (typeElement.getQualifiedName().contentEquals(s)) { 446 return; // Don't generate the list, too huge 447 } 448 } 449 Set<TypeElement> implcl = classtree.implementingClasses(typeElement); 450 if (!implcl.isEmpty()) { 451 Content label = contents.implementingClassesLabel; 452 Content dt = HtmlTree.DT(label); 453 Content dl = HtmlTree.DL(dt); 454 dl.add(getClassLinks(LinkInfoImpl.Kind.IMPLEMENTED_CLASSES, 455 implcl)); 456 classInfoTree.add(dl); 457 } 458 } 459 460 /** 461 * {@inheritDoc} 462 */ 463 @Override 464 public void addImplementedInterfacesInfo(Content classInfoTree) { 465 SortedSet<TypeMirror> interfaces = new TreeSet<>(utils.makeTypeMirrorClassUseComparator()); 466 interfaces.addAll(utils.getAllInterfaces(typeElement)); 467 if (utils.isClass(typeElement) && !interfaces.isEmpty()) { 468 Content label = contents.allImplementedInterfacesLabel; 469 Content dt = HtmlTree.DT(label); 470 Content dl = HtmlTree.DL(dt); 471 dl.add(getClassLinks(LinkInfoImpl.Kind.IMPLEMENTED_INTERFACES, interfaces)); 472 classInfoTree.add(dl); 473 } 474 } 475 476 /** 477 * {@inheritDoc} 478 */ 479 @Override 480 public void addSuperInterfacesInfo(Content classInfoTree) { 481 SortedSet<TypeMirror> interfaces = 482 new TreeSet<>(utils.makeTypeMirrorIndexUseComparator()); 483 interfaces.addAll(utils.getAllInterfaces(typeElement)); 484 485 if (utils.isInterface(typeElement) && !interfaces.isEmpty()) { 486 Content label = contents.allSuperinterfacesLabel; 487 Content dt = HtmlTree.DT(label); 488 Content dl = HtmlTree.DL(dt); 489 dl.add(getClassLinks(LinkInfoImpl.Kind.SUPER_INTERFACES, interfaces)); 490 classInfoTree.add(dl); 491 } 492 } 493 494 /** 495 * {@inheritDoc} 496 */ 497 @Override 498 public void addNestedClassInfo(final Content classInfoTree) { 499 Element outerClass = typeElement.getEnclosingElement(); 500 if (outerClass == null) 501 return; 502 new SimpleElementVisitor8<Void, Void>() { 503 @Override 504 public Void visitType(TypeElement e, Void p) { 505 Content label = utils.isInterface(e) 506 ? contents.enclosingInterfaceLabel 507 : contents.enclosingClassLabel; 508 Content dt = HtmlTree.DT(label); 509 Content dl = HtmlTree.DL(dt); 510 Content dd = new HtmlTree(HtmlTag.DD); 511 dd.add(getLink(new LinkInfoImpl(configuration, 512 LinkInfoImpl.Kind.CLASS, e))); 513 dl.add(dd); 514 classInfoTree.add(dl); 515 return null; 516 } 517 }.visit(outerClass); 518 } 519 520 /** 521 * {@inheritDoc} 522 */ 523 @Override 524 public void addFunctionalInterfaceInfo (Content classInfoTree) { 525 if (isFunctionalInterface()) { 526 Content dt = HtmlTree.DT(contents.functionalInterface); 527 Content dl = HtmlTree.DL(dt); 528 Content dd = new HtmlTree(HtmlTag.DD); 529 dd.add(contents.functionalInterfaceMessage); 530 dl.add(dd); 531 classInfoTree.add(dl); 532 } 533 } 534 535 public boolean isFunctionalInterface() { 536 List<? extends AnnotationMirror> annotationMirrors = ((Element) typeElement).getAnnotationMirrors(); 537 for (AnnotationMirror anno : annotationMirrors) { 538 if (utils.isFunctionalInterface(anno)) { 539 return true; 540 } 541 } 542 return false; 543 } 544 545 546 /** 547 * {@inheritDoc} 548 */ 549 @Override 550 public void addClassDeprecationInfo(Content classInfoTree) { 551 List<? extends DocTree> deprs = utils.getBlockTags(typeElement, DocTree.Kind.DEPRECATED); 552 if (utils.isDeprecated(typeElement)) { 553 Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(typeElement)); 554 Content div = HtmlTree.DIV(HtmlStyle.deprecationBlock, deprLabel); 555 if (!deprs.isEmpty()) { 556 CommentHelper ch = utils.getCommentHelper(typeElement); 557 DocTree dt = deprs.get(0); 558 List<? extends DocTree> commentTags = ch.getBody(configuration, dt); 559 if (!commentTags.isEmpty()) { 560 addInlineDeprecatedComment(typeElement, deprs.get(0), div); 561 } 562 } 563 classInfoTree.add(div); 564 } 565 } 566 567 /** 568 * Get links to the given classes. 569 * 570 * @param context the id of the context where the link will be printed 571 * @param list the list of classes 572 * @return a content tree for the class list 573 */ 574 private Content getClassLinks(LinkInfoImpl.Kind context, Collection<?> list) { 575 Content dd = new HtmlTree(HtmlTag.DD); 576 boolean isFirst = true; 577 for (Object type : list) { 578 if (!isFirst) { 579 Content separator = new StringContent(", "); 580 dd.add(separator); 581 } else { 582 isFirst = false; 583 } 584 // TODO: should we simply split this method up to avoid instanceof ? 585 if (type instanceof TypeElement) { 586 Content link = getLink( 587 new LinkInfoImpl(configuration, context, (TypeElement)(type))); 588 dd.add(HtmlTree.CODE(link)); 589 } else { 590 Content link = getLink( 591 new LinkInfoImpl(configuration, context, ((TypeMirror)type))); 592 dd.add(HtmlTree.CODE(link)); 593 } 594 } 595 return dd; 596 } 597 598 /** 599 * Return the TypeElement being documented. 600 * 601 * @return the TypeElement being documented. 602 */ 603 @Override 604 public TypeElement getTypeElement() { 605 return typeElement; 606 } 607 }