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