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