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