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