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