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