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 26package jdk.javadoc.internal.doclets.toolkit.builders; 27 28import java.text.MessageFormat; 29import java.util.*; 30 31import javax.lang.model.element.Element; 32import javax.lang.model.element.ExecutableElement; 33import javax.lang.model.element.TypeElement; 34import javax.lang.model.element.VariableElement; 35import javax.lang.model.type.ArrayType; 36import javax.lang.model.type.DeclaredType; 37import javax.lang.model.type.ExecutableType; 38import javax.lang.model.type.PrimitiveType; 39import javax.lang.model.type.TypeMirror; 40import javax.lang.model.util.SimpleTypeVisitor9; 41 42import com.sun.source.doctree.DocTree; 43import com.sun.source.doctree.DocTree.Kind; 44import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter; 45import jdk.javadoc.internal.doclets.toolkit.ClassWriter; 46import jdk.javadoc.internal.doclets.toolkit.Content; 47import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; 48import jdk.javadoc.internal.doclets.toolkit.WriterFactory; 49import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; 50import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; 51import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap; 52import jdk.javadoc.internal.doclets.toolkit.CommentUtils; 53 54/** 55 * Builds the member summary. 56 * 57 * <p><b>This is NOT part of any supported API. 58 * If you write code that depends on this, you do so at your own risk. 59 * This code and its internal interfaces are subject to change or 60 * deletion without notice.</b> 61 * 62 * @author Jamie Ho 63 * @author Bhavesh Patel (Modified) 64 */ 65public class MemberSummaryBuilder extends AbstractMemberBuilder { 66 67 /** 68 * The XML root for this builder. 69 */ 70 public static final String NAME = "MemberSummary"; 71 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 } 94 95 /** 96 * Construct a new MemberSummaryBuilder. 97 * 98 * @param classWriter the writer for the class whose members are being 99 * summarized. 100 * @param context the build context. 101 */ 102 public static MemberSummaryBuilder getInstance( 103 ClassWriter classWriter, Context context) { 104 MemberSummaryBuilder builder = new MemberSummaryBuilder(context, 105 classWriter.getTypeElement()); 106 WriterFactory wf = context.configuration.getWriterFactory(); 107 for (VisibleMemberMap.Kind kind : VisibleMemberMap.Kind.values()) { 108 MemberSummaryWriter msw = builder.getVisibleMemberMap(kind).noVisibleMembers() 109 ? null 110 : wf.getMemberSummaryWriter(classWriter, kind); 111 builder.memberSummaryWriters.put(kind, msw); 112 } 113 return builder; 114 } 115 116 /** 117 * Construct a new MemberSummaryBuilder. 118 * 119 * @param annotationTypeWriter the writer for the class whose members are 120 * being summarized. 121 * @param configuration the current configuration of the doclet. 122 */ 123 public static MemberSummaryBuilder getInstance( 124 AnnotationTypeWriter annotationTypeWriter, Context context) { 125 MemberSummaryBuilder builder = new MemberSummaryBuilder(context, 126 annotationTypeWriter.getAnnotationTypeElement()); 127 WriterFactory wf = context.configuration.getWriterFactory(); 128 for (VisibleMemberMap.Kind kind : VisibleMemberMap.Kind.values()) { 129 MemberSummaryWriter msw = builder.getVisibleMemberMap(kind).noVisibleMembers() 130 ? null 131 : wf.getMemberSummaryWriter(annotationTypeWriter, kind); 132 builder.memberSummaryWriters.put(kind, msw); 133 } 134 return builder; 135 } 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override 141 public String getName() { 142 return NAME; 143 } 144 145 /** 146 * Return the specified visible member map. 147 * 148 * @param kind the kind of visible member map to return. 149 * @return the specified visible member map. 150 * @throws ArrayIndexOutOfBoundsException when the type is invalid. 151 * @see VisibleMemberMap 152 */ 153 public VisibleMemberMap getVisibleMemberMap(VisibleMemberMap.Kind kind) { 154 return configuration.getVisibleMemberMap(typeElement, kind); 155 } 156 157 /**. 158 * Return the specified member summary writer. 159 * 160 * @param kind the kind of member summary writer to return. 161 * @return the specified member summary writer. 162 * @throws ArrayIndexOutOfBoundsException when the type is invalid. 163 * @see VisibleMemberMap 164 */ 165 public MemberSummaryWriter getMemberSummaryWriter(VisibleMemberMap.Kind kind) { 166 return memberSummaryWriters.get(kind); 167 } 168 169 /** 170 * Returns a list of methods that will be documented for the given class. 171 * This information can be used for doclet specific documentation 172 * generation. 173 * 174 * @param kind the kind of elements to return. 175 * @return a list of methods that will be documented. 176 * @see VisibleMemberMap 177 */ 178 public SortedSet<Element> members(VisibleMemberMap.Kind kind) { 179 TreeSet<Element> out = new TreeSet<>(comparator); 180 out.addAll(getVisibleMemberMap(kind).getLeafMembers()); 181 return out; 182 } 183 184 /** 185 * Return true it there are any members to summarize. 186 * 187 * @return true if there are any members to summarize. 188 */ 189 @Override 190 public boolean hasMembersToDocument() { 191 if (utils.isAnnotationType(typeElement)) { 192 return !utils.getAnnotationMethods(typeElement).isEmpty(); 193 } 194 for (VisibleMemberMap.Kind kind : VisibleMemberMap.Kind.values()) { 195 VisibleMemberMap members = getVisibleMemberMap(kind); 196 if (!members.noVisibleMembers()) { 197 return true; 198 } 199 } 200 return false; 201 } 202 203 /** 204 * Build the summary for the enum constants. 205 * 206 * @param node the XML element that specifies which components to document 207 * @param memberSummaryTree the content tree to which the documentation will be added 208 */ 209 public void buildEnumConstantsSummary(XMLNode node, Content memberSummaryTree) { 210 MemberSummaryWriter writer = 211 memberSummaryWriters.get(VisibleMemberMap.Kind.ENUM_CONSTANTS); 212 VisibleMemberMap visibleMemberMap = 213 getVisibleMemberMap(VisibleMemberMap.Kind.ENUM_CONSTANTS); 214 addSummary(writer, visibleMemberMap, false, memberSummaryTree); 215 } 216 217 /** 218 * Build the summary for fields. 219 * 220 * @param node the XML element that specifies which components to document 221 * @param memberSummaryTree the content tree to which the documentation will be added 222 */ 223 public void buildAnnotationTypeFieldsSummary(XMLNode node, Content memberSummaryTree) { 224 MemberSummaryWriter writer = 225 memberSummaryWriters.get(VisibleMemberMap.Kind.ANNOTATION_TYPE_FIELDS); 226 VisibleMemberMap visibleMemberMap = 227 getVisibleMemberMap(VisibleMemberMap.Kind.ANNOTATION_TYPE_FIELDS); 228 addSummary(writer, visibleMemberMap, false, memberSummaryTree); 229 } 230 231 /** 232 * Build the summary for the optional members. 233 * 234 * @param node the XML element that specifies which components to document 235 * @param memberSummaryTree the content tree to which the documentation will be added 236 */ 237 public void buildAnnotationTypeOptionalMemberSummary(XMLNode node, Content memberSummaryTree) { 238 MemberSummaryWriter writer = 239 memberSummaryWriters.get(VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_OPTIONAL); 240 VisibleMemberMap visibleMemberMap = 241 getVisibleMemberMap(VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_OPTIONAL); 242 addSummary(writer, visibleMemberMap, false, memberSummaryTree); 243 } 244 245 /** 246 * Build the summary for the optional members. 247 * 248 * @param node the XML element that specifies which components to document 249 * @param memberSummaryTree the content tree to which the documentation will be added 250 */ 251 public void buildAnnotationTypeRequiredMemberSummary(XMLNode node, Content memberSummaryTree) { 252 MemberSummaryWriter writer = 253 memberSummaryWriters.get(VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_REQUIRED); 254 VisibleMemberMap visibleMemberMap = 255 getVisibleMemberMap(VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_REQUIRED); 256 addSummary(writer, visibleMemberMap, false, memberSummaryTree); 257 } 258 259 /** 260 * Build the summary for the fields. 261 * 262 * @param node the XML element that specifies which components to document 263 * @param memberSummaryTree the content tree to which the documentation will be added 264 */ 265 public void buildFieldsSummary(XMLNode node, 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 public void buildPropertiesSummary(XMLNode node, Content memberSummaryTree) { 277 MemberSummaryWriter writer = 278 memberSummaryWriters.get(VisibleMemberMap.Kind.PROPERTIES); 279 VisibleMemberMap visibleMemberMap = 280 getVisibleMemberMap(VisibleMemberMap.Kind.PROPERTIES); 281 addSummary(writer, visibleMemberMap, true, memberSummaryTree); 282 } 283 284 /** 285 * Build the summary for the nested classes. 286 * 287 * @param node the XML element that specifies which components to document 288 * @param memberSummaryTree the content tree to which the documentation will be added 289 */ 290 public void buildNestedClassesSummary(XMLNode node, Content memberSummaryTree) { 291 MemberSummaryWriter writer = 292 memberSummaryWriters.get(VisibleMemberMap.Kind.INNER_CLASSES); 293 VisibleMemberMap visibleMemberMap = 294 getVisibleMemberMap(VisibleMemberMap.Kind.INNER_CLASSES); 295 addSummary(writer, visibleMemberMap, true, memberSummaryTree); 296 } 297 298 /** 299 * Build the method summary. 300 * 301 * @param node the XML element that specifies which components to document 302 * @param memberSummaryTree the content tree to which the documentation will be added 303 */ 304 public void buildMethodsSummary(XMLNode node, 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 node the XML element that specifies which components to document 316 * @param memberSummaryTree the content tree to which the documentation will be added 317 */ 318 public void buildConstructorsSummary(XMLNode node, 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