1 /*
   2  * Copyright (c) 2003, 2017, 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.toolkit.builders;
  27 
  28 import java.text.MessageFormat;
  29 import java.util.*;
  30 
  31 import javax.lang.model.element.Element;
  32 import javax.lang.model.element.ExecutableElement;
  33 import javax.lang.model.element.TypeElement;
  34 import javax.lang.model.element.VariableElement;
  35 
  36 import com.sun.source.doctree.DocTree;
  37 import com.sun.source.doctree.DocTree.Kind;
  38 import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter;
  39 import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
  40 import jdk.javadoc.internal.doclets.toolkit.Content;
  41 import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter;
  42 import jdk.javadoc.internal.doclets.toolkit.WriterFactory;
  43 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
  44 import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
  45 import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap;
  46 import jdk.javadoc.internal.doclets.toolkit.CommentUtils;
  47 
  48 /**
  49  * Builds the member summary.
  50  * There are two anonymous subtype variants of this builder, created
  51  * in the {@link #getInstance} methods. One is for general types;
  52  * the other is for annotation types.
  53  *
  54  *  <p><b>This is NOT part of any supported API.
  55  *  If you write code that depends on this, you do so at your own risk.
  56  *  This code and its internal interfaces are subject to change or
  57  *  deletion without notice.</b>
  58  *
  59  * @author Jamie Ho
  60  * @author Bhavesh Patel (Modified)
  61  */
  62 public abstract class MemberSummaryBuilder extends AbstractMemberBuilder {
  63 
  64     /*
  65      * Comparator used to sort the members in the summary.
  66      */
  67     private final Comparator<Element> comparator;
  68 
  69     /**
  70      * The member summary writers for the given class.
  71      */
  72     private final EnumMap<VisibleMemberMap.Kind, MemberSummaryWriter> memberSummaryWriters;
  73 
  74     /**
  75      * The type being documented.
  76      */
  77     protected final TypeElement typeElement;
  78 
  79     /**
  80      * Construct a new MemberSummaryBuilder.
  81      *
  82      * @param classWriter   the writer for the class whose members are being
  83      *                      summarized.
  84      * @param context       the build context.
  85      */
  86     private MemberSummaryBuilder(Context context, TypeElement typeElement) {
  87         super(context);
  88         this.typeElement = typeElement;
  89         memberSummaryWriters = new EnumMap<>(VisibleMemberMap.Kind.class);
  90 
  91         comparator = utils.makeGeneralPurposeComparator();
  92     }
  93 
  94     /**
  95      * Construct a new MemberSummaryBuilder for a general type.
  96      *
  97      * @param classWriter   the writer for the class whose members are being
  98      *                      summarized.
  99      * @param context       the build context.
 100      * @return              the instance
 101      */
 102     public static MemberSummaryBuilder getInstance(
 103             ClassWriter classWriter, Context context) {
 104         MemberSummaryBuilder builder = new MemberSummaryBuilder(context, classWriter.getTypeElement()) {
 105             @Override
 106             public void build(Content contentTree) {
 107                 buildPropertiesSummary(contentTree);
 108                 buildNestedClassesSummary(contentTree);
 109                 buildEnumConstantsSummary(contentTree);
 110                 buildFieldsSummary(contentTree);
 111                 buildConstructorsSummary(contentTree);
 112                 buildMethodsSummary(contentTree);
 113             }
 114 
 115             @Override
 116             public boolean hasMembersToDocument() {
 117                 for (VisibleMemberMap.Kind kind : VisibleMemberMap.Kind.values()) {
 118                     VisibleMemberMap members = getVisibleMemberMap(kind);
 119                     if (!members.noVisibleMembers()) {
 120                         return true;
 121                     }
 122                 }
 123                 return false;
 124             }
 125         };
 126         WriterFactory wf = context.configuration.getWriterFactory();
 127         for (VisibleMemberMap.Kind kind : VisibleMemberMap.Kind.values()) {
 128             MemberSummaryWriter msw = builder.getVisibleMemberMap(kind).noVisibleMembers()
 129                     ? null
 130                     : wf.getMemberSummaryWriter(classWriter, kind);
 131             builder.memberSummaryWriters.put(kind, msw);
 132         }
 133         return builder;
 134     }
 135 
 136     /**
 137      * Construct a new MemberSummaryBuilder for an annotation type.
 138      *
 139      * @param annotationTypeWriter the writer for the class whose members are
 140      *                             being summarized.
 141      * @param context       the build context.
 142      * @return              the instance
 143      */
 144     public static MemberSummaryBuilder getInstance(
 145             AnnotationTypeWriter annotationTypeWriter, Context context) {
 146         MemberSummaryBuilder builder = new MemberSummaryBuilder(context,
 147                 annotationTypeWriter.getAnnotationTypeElement()) {
 148             @Override
 149             public void build(Content contentTree) {
 150                 buildAnnotationTypeFieldsSummary(contentTree);
 151                 buildAnnotationTypeRequiredMemberSummary(contentTree);
 152                 buildAnnotationTypeOptionalMemberSummary(contentTree);
 153             }
 154 
 155             @Override
 156             public boolean hasMembersToDocument() {
 157                 return !utils.getAnnotationMembers(typeElement).isEmpty();
 158             }
 159         };
 160         WriterFactory wf = context.configuration.getWriterFactory();
 161         for (VisibleMemberMap.Kind kind : VisibleMemberMap.Kind.values()) {
 162             MemberSummaryWriter msw = builder.getVisibleMemberMap(kind).noVisibleMembers()
 163                     ? null
 164                     : wf.getMemberSummaryWriter(annotationTypeWriter, kind);
 165             builder.memberSummaryWriters.put(kind, msw);
 166         }
 167         return builder;
 168     }
 169 
 170     /**
 171      * Return the specified visible member map.
 172      *
 173      * @param kind the kind of visible member map to return.
 174      * @return the specified visible member map.
 175      * @throws ArrayIndexOutOfBoundsException when the type is invalid.
 176      * @see VisibleMemberMap
 177      */
 178     public VisibleMemberMap getVisibleMemberMap(VisibleMemberMap.Kind kind) {
 179         return configuration.getVisibleMemberMap(typeElement, kind);
 180     }
 181 
 182     /**.
 183      * Return the specified member summary writer.
 184      *
 185      * @param kind the kind of member summary writer to return.
 186      * @return the specified member summary writer.
 187      * @throws ArrayIndexOutOfBoundsException when the type is invalid.
 188      * @see VisibleMemberMap
 189      */
 190     public MemberSummaryWriter getMemberSummaryWriter(VisibleMemberMap.Kind kind) {
 191         return memberSummaryWriters.get(kind);
 192     }
 193 
 194     /**
 195      * Returns a list of methods that will be documented for the given class.
 196      * This information can be used for doclet specific documentation
 197      * generation.
 198      *
 199      * @param kind the kind of elements to return.
 200      * @return a list of methods that will be documented.
 201      * @see VisibleMemberMap
 202      */
 203     public SortedSet<Element> members(VisibleMemberMap.Kind kind) {
 204         TreeSet<Element> out = new TreeSet<>(comparator);
 205         out.addAll(getVisibleMemberMap(kind).getLeafMembers());
 206         return out;
 207     }
 208 
 209     /**
 210      * Build the summary for the enum constants.
 211      *
 212      * @param memberSummaryTree the content tree to which the documentation will be added
 213      */
 214     protected void buildEnumConstantsSummary(Content memberSummaryTree) {
 215         MemberSummaryWriter writer =
 216                 memberSummaryWriters.get(VisibleMemberMap.Kind.ENUM_CONSTANTS);
 217         VisibleMemberMap visibleMemberMap =
 218                 getVisibleMemberMap(VisibleMemberMap.Kind.ENUM_CONSTANTS);
 219         addSummary(writer, visibleMemberMap, false, memberSummaryTree);
 220     }
 221 
 222     /**
 223      * Build the summary for fields.
 224      *
 225      * @param memberSummaryTree the content tree to which the documentation will be added
 226      */
 227     protected void buildAnnotationTypeFieldsSummary(Content memberSummaryTree) {
 228         MemberSummaryWriter writer =
 229                 memberSummaryWriters.get(VisibleMemberMap.Kind.ANNOTATION_TYPE_FIELDS);
 230         VisibleMemberMap visibleMemberMap =
 231                 getVisibleMemberMap(VisibleMemberMap.Kind.ANNOTATION_TYPE_FIELDS);
 232         addSummary(writer, visibleMemberMap, false, memberSummaryTree);
 233     }
 234 
 235     /**
 236      * Build the summary for the optional members.
 237      *
 238      * @param memberSummaryTree the content tree to which the documentation will be added
 239      */
 240     protected void buildAnnotationTypeOptionalMemberSummary(Content memberSummaryTree) {
 241         MemberSummaryWriter writer =
 242                 memberSummaryWriters.get(VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_OPTIONAL);
 243         VisibleMemberMap visibleMemberMap =
 244                 getVisibleMemberMap(VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_OPTIONAL);
 245         addSummary(writer, visibleMemberMap, false, memberSummaryTree);
 246     }
 247 
 248     /**
 249      * Build the summary for the optional members.
 250      *
 251      * @param memberSummaryTree the content tree to which the documentation will be added
 252      */
 253     protected void buildAnnotationTypeRequiredMemberSummary(Content memberSummaryTree) {
 254         MemberSummaryWriter writer =
 255                 memberSummaryWriters.get(VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_REQUIRED);
 256         VisibleMemberMap visibleMemberMap =
 257                 getVisibleMemberMap(VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_REQUIRED);
 258         addSummary(writer, visibleMemberMap, false, memberSummaryTree);
 259     }
 260 
 261     /**
 262      * Build the summary for the fields.
 263      *
 264      * @param memberSummaryTree the content tree to which the documentation will be added
 265      */
 266     protected void buildFieldsSummary(Content memberSummaryTree) {
 267         MemberSummaryWriter writer =
 268                 memberSummaryWriters.get(VisibleMemberMap.Kind.FIELDS);
 269         VisibleMemberMap visibleMemberMap =
 270                 getVisibleMemberMap(VisibleMemberMap.Kind.FIELDS);
 271         addSummary(writer, visibleMemberMap, true, memberSummaryTree);
 272     }
 273 
 274     /**
 275      * Build the summary for the fields.
 276      *
 277      * @param memberSummaryTree the content tree to which the documentation will be added
 278      */
 279     protected void buildPropertiesSummary(Content memberSummaryTree) {
 280         MemberSummaryWriter writer =
 281                 memberSummaryWriters.get(VisibleMemberMap.Kind.PROPERTIES);
 282         VisibleMemberMap visibleMemberMap =
 283                 getVisibleMemberMap(VisibleMemberMap.Kind.PROPERTIES);
 284         addSummary(writer, visibleMemberMap, true, memberSummaryTree);
 285     }
 286 
 287     /**
 288      * Build the summary for the nested classes.
 289      *
 290      * @param memberSummaryTree the content tree to which the documentation will be added
 291      */
 292     protected void buildNestedClassesSummary(Content memberSummaryTree) {
 293         MemberSummaryWriter writer =
 294                 memberSummaryWriters.get(VisibleMemberMap.Kind.INNER_CLASSES);
 295         VisibleMemberMap visibleMemberMap =
 296                 getVisibleMemberMap(VisibleMemberMap.Kind.INNER_CLASSES);
 297         addSummary(writer, visibleMemberMap, true, memberSummaryTree);
 298     }
 299 
 300     /**
 301      * Build the method summary.
 302      *
 303      * @param memberSummaryTree the content tree to which the documentation will be added
 304      */
 305     protected void buildMethodsSummary(Content memberSummaryTree) {
 306         MemberSummaryWriter writer =
 307                 memberSummaryWriters.get(VisibleMemberMap.Kind.METHODS);
 308         VisibleMemberMap visibleMemberMap =
 309                getVisibleMemberMap(VisibleMemberMap.Kind.METHODS);
 310         addSummary(writer, visibleMemberMap, true, memberSummaryTree);
 311     }
 312 
 313     /**
 314      * Build the constructor summary.
 315      *
 316      * @param memberSummaryTree the content tree to which the documentation will be added
 317      */
 318     protected void buildConstructorsSummary(Content memberSummaryTree) {
 319         MemberSummaryWriter writer =
 320                 memberSummaryWriters.get(VisibleMemberMap.Kind.CONSTRUCTORS);
 321         VisibleMemberMap visibleMemberMap =
 322                 getVisibleMemberMap(VisibleMemberMap.Kind.CONSTRUCTORS);
 323         addSummary(writer, visibleMemberMap, false, memberSummaryTree);
 324     }
 325 
 326     /**
 327      * Build the member summary for the given members.
 328      *
 329      * @param writer the summary writer to write the output.
 330      * @param visibleMemberMap the given members to summarize.
 331      * @param summaryTreeList list of content trees to which the documentation will be added
 332      */
 333     private void buildSummary(MemberSummaryWriter writer,
 334             VisibleMemberMap visibleMemberMap, LinkedList<Content> summaryTreeList) {
 335         SortedSet<Element> members = asSortedSet(visibleMemberMap.getLeafMembers());
 336         if (!members.isEmpty()) {
 337             List<Content> tableContents = new LinkedList<>();
 338             int counter = 0;
 339             for (Element member : members) {
 340                 final Element property = visibleMemberMap.getPropertyMemberDoc(member);
 341                 if (property != null) {
 342                     processProperty(visibleMemberMap, member, property);
 343                 }
 344                 List<? extends DocTree> firstSentenceTags = utils.getFirstSentenceTrees(member);
 345                 if (utils.isExecutableElement(member) && firstSentenceTags.isEmpty()) {
 346                     //Inherit comments from overriden or implemented method if
 347                     //necessary.
 348                     DocFinder.Output inheritedDoc =
 349                             DocFinder.search(configuration,
 350                                     new DocFinder.Input(utils, (ExecutableElement) member));
 351                     if (inheritedDoc.holder != null
 352                             && !utils.getFirstSentenceTrees(inheritedDoc.holder).isEmpty()) {
 353                         // let the comment helper know of the overridden element
 354                         CommentHelper ch = utils.getCommentHelper(member);
 355                         ch.setOverrideElement(inheritedDoc.holder);
 356                         firstSentenceTags = utils.getFirstSentenceTrees(inheritedDoc.holder);
 357                     }
 358                 }
 359                 writer.addMemberSummary(typeElement, member, firstSentenceTags,
 360                         tableContents, counter);
 361                 counter++;
 362             }
 363             summaryTreeList.add(writer.getSummaryTableTree(typeElement, tableContents));
 364         }
 365     }
 366 
 367     /**
 368      * Process the property method, property setter and/or property getter
 369      * comment text so that it contains the documentation from
 370      * the property field. The method adds the leading sentence,
 371      * copied documentation including the defaultValue tag and
 372      * the see tags if the appropriate property getter and setter are
 373      * available.
 374      *
 375      * @param visibleMemberMap the members information.
 376      * @param member the member which is to be augmented.
 377      * @param property the original property documentation.
 378      */
 379     private void processProperty(VisibleMemberMap visibleMemberMap,
 380                                  Element member,
 381                                  Element property) {
 382         CommentUtils cmtutils = configuration.cmtUtils;
 383         final boolean isSetter = isSetter(member);
 384         final boolean isGetter = isGetter(member);
 385 
 386         List<DocTree> fullBody = new ArrayList<>();
 387         List<DocTree> blockTags = new ArrayList<>();
 388         if (isGetter || isSetter) {
 389             //add "[GS]ets the value of the property PROPERTY_NAME."
 390             if (isSetter) {
 391                 String text = MessageFormat.format(
 392                         configuration.getText("doclet.PropertySetterWithName"),
 393                         utils.propertyName((ExecutableElement)member));
 394                 fullBody.addAll(cmtutils.makeFirstSentenceTree(text));
 395             }
 396             if (isGetter) {
 397                 String text = MessageFormat.format(
 398                         configuration.getText("doclet.PropertyGetterWithName"),
 399                         utils.propertyName((ExecutableElement) member));
 400                 fullBody.addAll(cmtutils.makeFirstSentenceTree(text));
 401             }
 402             List<? extends DocTree> propertyTags = utils.getBlockTags(property, "propertyDescription");
 403             if (propertyTags.isEmpty()) {
 404                 List<? extends DocTree> comment = utils.getFullBody(property);
 405                 blockTags.addAll(cmtutils.makePropertyDescriptionTree(comment));
 406             }
 407         } else {
 408             fullBody.addAll(utils.getFullBody(property));
 409         }
 410 
 411         // copy certain tags
 412         List<? extends DocTree> tags = utils.getBlockTags(property, Kind.SINCE);
 413         blockTags.addAll(tags);
 414 
 415         List<? extends DocTree> bTags = utils.getBlockTags(property, Kind.UNKNOWN_BLOCK_TAG);
 416         CommentHelper ch = utils.getCommentHelper(property);
 417         for (DocTree dt : bTags) {
 418             String tagName = ch.getTagName(dt);
 419             if ( "defaultValue".equals(tagName)) {
 420                 blockTags.add(dt);
 421             }
 422         }
 423 
 424         //add @see tags
 425         if (!isGetter && !isSetter) {
 426             ExecutableElement getter = (ExecutableElement) visibleMemberMap.getGetterForProperty(member);
 427             ExecutableElement setter = (ExecutableElement) visibleMemberMap.getSetterForProperty(member);
 428 
 429             if (null != getter) {
 430                 StringBuilder sb = new StringBuilder("#");
 431                 sb.append(utils.getSimpleName(getter)).append("()");
 432                 blockTags.add(cmtutils.makeSeeTree(sb.toString(), getter));
 433             }
 434 
 435             if (null != setter) {
 436                 VariableElement param = setter.getParameters().get(0);
 437                 StringBuilder sb = new StringBuilder("#");
 438                 sb.append(utils.getSimpleName(setter));
 439                 if (!utils.isTypeVariable(param.asType())) {
 440                     sb.append("(").append(utils.getTypeSignature(param.asType(), false, true)).append(")");
 441                 }
 442                 blockTags.add(cmtutils.makeSeeTree(sb.toString(), setter));
 443             }
 444         }
 445         cmtutils.setDocCommentTree(member, fullBody, blockTags, utils);
 446     }
 447 
 448     /**
 449      * Test whether the method is a getter.
 450      * @param element property method documentation. Needs to be either property
 451      * method, property getter, or property setter.
 452      * @return true if the given documentation belongs to a getter.
 453      */
 454     private boolean isGetter(Element element) {
 455         final String pedName = element.getSimpleName().toString();
 456         return pedName.startsWith("get") || pedName.startsWith("is");
 457     }
 458 
 459     /**
 460      * Test whether the method is a setter.
 461      * @param element property method documentation. Needs to be either property
 462      * method, property getter, or property setter.
 463      * @return true if the given documentation belongs to a setter.
 464      */
 465     private boolean isSetter(Element element) {
 466         return element.getSimpleName().toString().startsWith("set");
 467     }
 468 
 469     /**
 470      * Build the inherited member summary for the given methods.
 471      *
 472      * @param writer the writer for this member summary.
 473      * @param visibleMemberMap the map for the members to document.
 474      * @param summaryTreeList list of content trees to which the documentation will be added
 475      */
 476     private void buildInheritedSummary(MemberSummaryWriter writer,
 477             VisibleMemberMap visibleMemberMap, LinkedList<Content> summaryTreeList) {
 478         for (TypeElement inhclass : visibleMemberMap.getVisibleClasses()) {
 479             if (!(utils.isPublic(inhclass) || utils.isLinkable(inhclass))) {
 480                 continue;
 481             }
 482             if (inhclass == typeElement) {
 483                 continue;
 484             }
 485             SortedSet<Element> inhmembers = asSortedSet(visibleMemberMap.getMembers(inhclass));
 486             if (!inhmembers.isEmpty()) {
 487                 Content inheritedTree = writer.getInheritedSummaryHeader(inhclass);
 488                 Content linksTree = writer.getInheritedSummaryLinksTree();
 489                 for (Element member : inhmembers) {
 490                     TypeElement t= inhclass;
 491                     if (utils.isPackagePrivate(inhclass) && !utils.isLinkable(inhclass)) {
 492                         t = typeElement;
 493                     }
 494                     writer.addInheritedMemberSummary(t, member, inhmembers.first() == member,
 495                             inhmembers.last() == member, linksTree);
 496                 }
 497                 inheritedTree.addContent(linksTree);
 498                 summaryTreeList.add(writer.getMemberTree(inheritedTree));
 499             }
 500         }
 501     }
 502 
 503     /**
 504      * Add the summary for the documentation.
 505      *
 506      * @param writer the writer for this member summary.
 507      * @param visibleMemberMap the map for the members to document.
 508      * @param showInheritedSummary true if inherited summary should be documented
 509      * @param memberSummaryTree the content tree to which the documentation will be added
 510      */
 511     private void addSummary(MemberSummaryWriter writer,
 512             VisibleMemberMap visibleMemberMap, boolean showInheritedSummary,
 513             Content memberSummaryTree) {
 514         LinkedList<Content> summaryTreeList = new LinkedList<>();
 515         buildSummary(writer, visibleMemberMap, summaryTreeList);
 516         if (showInheritedSummary)
 517             buildInheritedSummary(writer, visibleMemberMap, summaryTreeList);
 518         if (!summaryTreeList.isEmpty()) {
 519             Content memberTree = writer.getMemberSummaryHeader(typeElement, memberSummaryTree);
 520             summaryTreeList.stream().forEach(memberTree::addContent);
 521             writer.addMemberTree(memberSummaryTree, memberTree);
 522         }
 523     }
 524 
 525     private SortedSet<Element> asSortedSet(Collection<Element> members) {
 526         SortedSet<Element> out = new TreeSet<>(comparator);
 527         out.addAll(members);
 528         return out;
 529     }
 530 }