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 }