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