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