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 com.sun.tools.doclets.internal.toolkit.util; 27 28 import java.util.*; 29 import java.util.regex.Pattern; 30 31 import com.sun.javadoc.*; 32 import com.sun.tools.doclets.internal.toolkit.*; 33 34 /** 35 * A data structure that encapsulates the visible members of a particular 36 * type for a given class tree. To use this data structor, you must specify 37 * the type of member you are interested in (nested class, field, constructor 38 * or method) and the leaf of the class tree. The data structure will map 39 * all visible members in the leaf and classes above the leaf in the tree. 40 * 41 * <p><b>This is NOT part of any supported API. 42 * If you write code that depends on this, you do so at your own risk. 43 * This code and its internal interfaces are subject to change or 44 * deletion without notice.</b> 45 * 46 * @author Atul M Dambalkar 47 * @author Jamie Ho (rewrite) 48 */ 49 public class VisibleMemberMap { 50 51 private boolean noVisibleMembers = true; 52 53 public static final int INNERCLASSES = 0; 54 public static final int ENUM_CONSTANTS = 1; 55 public static final int FIELDS = 2; 56 public static final int CONSTRUCTORS = 3; 57 public static final int METHODS = 4; 58 public static final int ANNOTATION_TYPE_FIELDS = 5; 59 public static final int ANNOTATION_TYPE_MEMBER_OPTIONAL = 6; 60 public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 7; 61 public static final int PROPERTIES = 8; 62 63 /** 64 * The total number of member types is {@value}. 65 */ 66 public static final int NUM_MEMBER_TYPES = 9; 67 68 public static final String STARTLEVEL = "start"; 69 70 /** 71 * List of ClassDoc objects for which ClassMembers objects are built. 72 */ 73 private final List<ClassDoc> visibleClasses = new ArrayList<>(); 74 75 /** 76 * Map for each member name on to a map which contains members with same 77 * name-signature. The mapped map will contain mapping for each MemberDoc 78 * onto it's respecive level string. 79 */ 80 private final Map<Object,Map<ProgramElementDoc,String>> memberNameMap = new HashMap<>(); 81 82 /** 83 * Map of class and it's ClassMembers object. 84 */ 85 private final Map<ClassDoc,ClassMembers> classMap = new HashMap<>(); 86 87 /** 88 * Type whose visible members are requested. This is the leaf of 89 * the class tree being mapped. 90 */ 91 private final ClassDoc classdoc; 92 93 /** 94 * Member kind: InnerClasses/Fields/Methods? 95 */ 96 private final int kind; 97 98 /** 99 * The configuration this VisibleMemberMap was created with. 100 */ 101 private final Configuration configuration; 102 private final Utils utils; 103 104 private static final Map<ClassDoc, ProgramElementDoc[]> propertiesCache = new HashMap<>(); 105 private static final Map<ProgramElementDoc, ProgramElementDoc> classPropertiesMap = new HashMap<>(); 106 private static final Map<ProgramElementDoc, GetterSetter> getterSetterMap = new HashMap<>(); 107 108 /** 109 * Construct a VisibleMemberMap of the given type for the given 110 * class. 111 * 112 * @param classdoc the class whose members are being mapped. 113 * @param kind the kind of member that is being mapped. 114 * @param configuration the configuration to use to construct this 115 * VisibleMemberMap. If the field configuration.nodeprecated is true the 116 * deprecated members are excluded from the map. If the field 117 * configuration.javafx is true the JavaFX features are used. 118 */ 119 public VisibleMemberMap(ClassDoc classdoc, 120 int kind, 121 Configuration configuration) { 122 this.classdoc = classdoc; 123 this.kind = kind; 124 this.configuration = configuration; 125 this.utils = configuration.utils; 126 new ClassMembers(classdoc, STARTLEVEL).build(); 127 } 128 129 /** 130 * Return the list of visible classes in this map. 131 * 132 * @return the list of visible classes in this map. 133 */ 134 public List<ClassDoc> getVisibleClassesList() { 135 sort(visibleClasses); 136 return visibleClasses; 137 } 138 139 /** 140 * Returns the property field documentation belonging to the given member. 141 * @param ped the member for which the property documentation is needed. 142 * @return the property field documentation, null if there is none. 143 */ 144 public ProgramElementDoc getPropertyMemberDoc(ProgramElementDoc ped) { 145 return classPropertiesMap.get(ped); 146 } 147 148 /** 149 * Returns the getter documentation belonging to the given property method. 150 * @param propertyMethod the method for which the getter is needed. 151 * @return the getter documentation, null if there is none. 152 */ 153 public ProgramElementDoc getGetterForProperty(ProgramElementDoc propertyMethod) { 154 return getterSetterMap.get(propertyMethod).getGetter(); 155 } 156 157 /** 158 * Returns the setter documentation belonging to the given property method. 159 * @param propertyMethod the method for which the setter is needed. 160 * @return the setter documentation, null if there is none. 161 */ 162 public ProgramElementDoc getSetterForProperty(ProgramElementDoc propertyMethod) { 163 return getterSetterMap.get(propertyMethod).getSetter(); 164 } 165 166 /** 167 * Return the package private members inherited by the class. Only return 168 * if parent is package private and not documented. 169 * 170 * @param configuration the current configuration of the doclet. 171 * @return the package private members inherited by the class. 172 */ 173 private List<ProgramElementDoc> getInheritedPackagePrivateMethods(Configuration configuration) { 174 List<ProgramElementDoc> results = new ArrayList<>(); 175 for (ClassDoc currentClass : visibleClasses) { 176 if (currentClass != classdoc && 177 currentClass.isPackagePrivate() && 178 !utils.isLinkable(currentClass, configuration)) { 179 // Document these members in the child class because 180 // the parent is inaccessible. 181 results.addAll(getMembersFor(currentClass)); 182 } 183 } 184 return results; 185 } 186 187 /** 188 * Return the visible members of the class being mapped. Also append at the 189 * end of the list members that are inherited by inaccessible parents. We 190 * document these members in the child because the parent is not documented. 191 * 192 * @param configuration the current configuration of the doclet. 193 */ 194 public List<ProgramElementDoc> getLeafClassMembers(Configuration configuration) { 195 List<ProgramElementDoc> result = getMembersFor(classdoc); 196 result.addAll(getInheritedPackagePrivateMethods(configuration)); 197 return result; 198 } 199 200 /** 201 * Retrn the list of members for the given class. 202 * 203 * @param cd the class to retrieve the list of visible members for. 204 * 205 * @return the list of members for the given class. 206 */ 207 public List<ProgramElementDoc> getMembersFor(ClassDoc cd) { 208 ClassMembers clmembers = classMap.get(cd); 209 if (clmembers == null) { 210 return new ArrayList<>(); 211 } 212 return clmembers.getMembers(); 213 } 214 215 /** 216 * Sort the given mixed list of classes and interfaces to a list of 217 * classes followed by interfaces traversed. Don't sort alphabetically. 218 */ 219 private void sort(List<ClassDoc> list) { 220 List<ClassDoc> classes = new ArrayList<>(); 221 List<ClassDoc> interfaces = new ArrayList<>(); 222 for (ClassDoc cd : list) { 223 if (cd.isClass()) { 224 classes.add(cd); 225 } 226 else { 227 interfaces.add(cd); 228 } 229 } 230 list.clear(); 231 list.addAll(classes); 232 list.addAll(interfaces); 233 } 234 235 private void fillMemberLevelMap(List<ProgramElementDoc> list, String level) { 236 for (ProgramElementDoc element : list) { 237 Object key = getMemberKey(element); 238 Map<ProgramElementDoc, String> memberLevelMap = memberNameMap.get(key); 239 if (memberLevelMap == null) { 240 memberLevelMap = new HashMap<>(); 241 memberNameMap.put(key, memberLevelMap); 242 } 243 memberLevelMap.put(element, level); 244 } 245 } 246 247 private void purgeMemberLevelMap(List<ProgramElementDoc> list, String level) { 248 for (ProgramElementDoc element : list) { 249 Object key = getMemberKey(element); 250 Map<ProgramElementDoc, String> memberLevelMap = memberNameMap.get(key); 251 if (memberLevelMap != null && level.equals(memberLevelMap.get(element))) 252 memberLevelMap.remove(element); 253 } 254 } 255 256 /** 257 * Represents a class member. We should be able to just use a 258 * ProgramElementDoc instead of this class, but that doesn't take 259 * type variables in consideration when comparing. 260 */ 261 private class ClassMember { 262 private Set<ProgramElementDoc> members; 263 264 public ClassMember(ProgramElementDoc programElementDoc) { 265 members = new HashSet<>(); 266 members.add(programElementDoc); 267 } 268 269 public void addMember(ProgramElementDoc programElementDoc) { 270 members.add(programElementDoc); 271 } 272 273 public boolean isEqual(MethodDoc member) { 274 for (ProgramElementDoc element : members) { 275 if (utils.executableMembersEqual(member, (MethodDoc) element)) { 276 members.add(member); 277 return true; 278 } 279 } 280 return false; 281 } 282 } 283 284 /** 285 * A data structure that represents the class members for 286 * a visible class. 287 */ 288 private class ClassMembers { 289 290 /** 291 * The mapping class, whose inherited members are put in the 292 * {@link #members} list. 293 */ 294 private ClassDoc mappingClass; 295 296 /** 297 * List of inherited members from the mapping class. 298 */ 299 private List<ProgramElementDoc> members = new ArrayList<>(); 300 301 /** 302 * Level/Depth of inheritance. 303 */ 304 private String level; 305 306 /** 307 * Return list of inherited members from mapping class. 308 * 309 * @return List Inherited members. 310 */ 311 public List<ProgramElementDoc> getMembers() { 312 return members; 313 } 314 315 private ClassMembers(ClassDoc mappingClass, String level) { 316 this.mappingClass = mappingClass; 317 this.level = level; 318 if (classMap.containsKey(mappingClass) && 319 level.startsWith(classMap.get(mappingClass).level)) { 320 //Remove lower level class so that it can be replaced with 321 //same class found at higher level. 322 purgeMemberLevelMap(getClassMembers(mappingClass, false), 323 classMap.get(mappingClass).level); 324 classMap.remove(mappingClass); 325 visibleClasses.remove(mappingClass); 326 } 327 if (!classMap.containsKey(mappingClass)) { 328 classMap.put(mappingClass, this); 329 visibleClasses.add(mappingClass); 330 } 331 332 } 333 334 private void build() { 335 if (kind == CONSTRUCTORS) { 336 addMembers(mappingClass); 337 } else { 338 mapClass(); 339 } 340 } 341 342 private void mapClass() { 343 addMembers(mappingClass); 344 ClassDoc[] interfaces = mappingClass.interfaces(); 345 for (ClassDoc anInterface : interfaces) { 346 String locallevel = level + 1; 347 ClassMembers cm = new ClassMembers(anInterface, locallevel); 348 cm.mapClass(); 349 } 350 if (mappingClass.isClass()) { 351 ClassDoc superclass = mappingClass.superclass(); 352 if (!(superclass == null || mappingClass.equals(superclass))) { 353 ClassMembers cm = new ClassMembers(superclass, 354 level + "c"); 355 cm.mapClass(); 356 } 357 } 358 } 359 360 /** 361 * Get all the valid members from the mapping class. Get the list of 362 * members for the class to be included into(ctii), also get the level 363 * string for ctii. If mapping class member is not already in the 364 * inherited member list and if it is visible in the ctii and not 365 * overridden, put such a member in the inherited member list. 366 * Adjust member-level-map, class-map. 367 */ 368 private void addMembers(ClassDoc fromClass) { 369 List<ProgramElementDoc> cdmembers = getClassMembers(fromClass, true); 370 List<ProgramElementDoc> incllist = new ArrayList<>(); 371 for (ProgramElementDoc pgmelem : cdmembers) { 372 if (!found(members, pgmelem) && 373 memberIsVisible(pgmelem) && 374 !isOverridden(pgmelem, level) && 375 !isTreatedAsPrivate(pgmelem)) { 376 incllist.add(pgmelem); 377 } 378 } 379 if (incllist.size() > 0) { 380 noVisibleMembers = false; 381 } 382 members.addAll(incllist); 383 fillMemberLevelMap(getClassMembers(fromClass, false), level); 384 } 385 386 private boolean isTreatedAsPrivate(ProgramElementDoc pgmelem) { 387 if (!configuration.javafx) { 388 return false; 389 } 390 391 Tag[] aspTags = pgmelem.tags("@treatAsPrivate"); 392 boolean result = (aspTags != null) && (aspTags.length > 0); 393 return result; 394 } 395 396 /** 397 * Is given doc item visible in given classdoc in terms fo inheritance? 398 * The given doc item is visible in the given classdoc if it is public 399 * or protected and if it is package-private if it's containing class 400 * is in the same package as the given classdoc. 401 */ 402 private boolean memberIsVisible(ProgramElementDoc pgmdoc) { 403 if (pgmdoc.containingClass().equals(classdoc)) { 404 //Member is in class that we are finding visible members for. 405 //Of course it is visible. 406 return true; 407 } else if (pgmdoc.isPrivate()) { 408 //Member is in super class or implemented interface. 409 //Private, so not inherited. 410 return false; 411 } else if (pgmdoc.isPackagePrivate()) { 412 //Member is package private. Only return true if its class is in 413 //same package. 414 return pgmdoc.containingClass().containingPackage().equals( 415 classdoc.containingPackage()); 416 } else { 417 //Public members are always inherited. 418 return true; 419 } 420 } 421 422 /** 423 * Return all available class members. 424 */ 425 private List<ProgramElementDoc> getClassMembers(ClassDoc cd, boolean filter) { 426 if (cd.isEnum() && kind == CONSTRUCTORS) { 427 //If any of these rules are hit, return empty array because 428 //we don't document these members ever. 429 return Arrays.asList(new ProgramElementDoc[] {}); 430 } 431 ProgramElementDoc[] members = null; 432 switch (kind) { 433 case ANNOTATION_TYPE_FIELDS: 434 members = cd.fields(filter); 435 break; 436 case ANNOTATION_TYPE_MEMBER_OPTIONAL: 437 members = cd.isAnnotationType() ? 438 filter((AnnotationTypeDoc) cd, false) : 439 new AnnotationTypeElementDoc[] {}; 440 break; 441 case ANNOTATION_TYPE_MEMBER_REQUIRED: 442 members = cd.isAnnotationType() ? 443 filter((AnnotationTypeDoc) cd, true) : 444 new AnnotationTypeElementDoc[] {}; 445 break; 446 case INNERCLASSES: 447 members = cd.innerClasses(filter); 448 break; 449 case ENUM_CONSTANTS: 450 members = cd.enumConstants(); 451 break; 452 case FIELDS: 453 members = cd.fields(filter); 454 break; 455 case CONSTRUCTORS: 456 members = cd.constructors(); 457 break; 458 case METHODS: 459 members = cd.methods(filter); 460 checkOnPropertiesTags((MethodDoc[])members); 461 break; 462 case PROPERTIES: 463 members = properties(cd, filter); 464 break; 465 default: 466 members = new ProgramElementDoc[0]; 467 } 468 // Deprected members should be excluded or not? 469 if (configuration.nodeprecated) { 470 return utils.excludeDeprecatedMembersAsList(members); 471 } 472 return Arrays.asList(members); 473 } 474 475 /** 476 * Filter the annotation type members and return either the required 477 * members or the optional members, depending on the value of the 478 * required parameter. 479 * 480 * @param doc The annotation type to process. 481 * @param required 482 * @return the annotation type members and return either the required 483 * members or the optional members, depending on the value of the 484 * required parameter. 485 */ 486 private AnnotationTypeElementDoc[] filter(AnnotationTypeDoc doc, 487 boolean required) { 488 AnnotationTypeElementDoc[] members = doc.elements(); 489 List<AnnotationTypeElementDoc> targetMembers = new ArrayList<>(); 490 for (AnnotationTypeElementDoc member : members) { 491 if ((required && member.defaultValue() == null) || 492 ((!required) && member.defaultValue() != null)) { 493 targetMembers.add(member); 494 } 495 } 496 return targetMembers.toArray(new AnnotationTypeElementDoc[]{}); 497 } 498 499 private boolean found(List<ProgramElementDoc> list, ProgramElementDoc elem) { 500 for (ProgramElementDoc pgmelem : list) { 501 if (utils.matches(pgmelem, elem)) { 502 return true; 503 } 504 } 505 return false; 506 } 507 508 509 /** 510 * Is member overridden? The member is overridden if it is found in the 511 * same level hierarchy e.g. member at level "11" overrides member at 512 * level "111". 513 */ 514 private boolean isOverridden(ProgramElementDoc pgmdoc, String level) { 515 Map<?,String> memberLevelMap = (Map<?,String>) memberNameMap.get(getMemberKey(pgmdoc)); 516 if (memberLevelMap == null) 517 return false; 518 for (String mappedlevel : memberLevelMap.values()) { 519 if (mappedlevel.equals(STARTLEVEL) || 520 (level.startsWith(mappedlevel) && 521 !level.equals(mappedlevel))) { 522 return true; 523 } 524 } 525 return false; 526 } 527 528 private ProgramElementDoc[] properties(final ClassDoc cd, final boolean filter) { 529 final MethodDoc[] allMethods = cd.methods(filter); 530 final FieldDoc[] allFields = cd.fields(false); 531 532 if (propertiesCache.containsKey(cd)) { 533 return propertiesCache.get(cd); 534 } 535 536 final List<MethodDoc> result = new ArrayList<>(); 537 538 for (final MethodDoc propertyMethod : allMethods) { 539 540 if (!isPropertyMethod(propertyMethod)) { 541 continue; 542 } 543 544 final MethodDoc getter = getterForField(allMethods, propertyMethod); 545 final MethodDoc setter = setterForField(allMethods, propertyMethod); 546 final FieldDoc field = fieldForProperty(allFields, propertyMethod); 547 548 addToPropertiesMap(setter, getter, propertyMethod, field); 549 getterSetterMap.put(propertyMethod, new GetterSetter(getter, setter)); 550 result.add(propertyMethod); 551 } 552 final ProgramElementDoc[] resultAray = 553 result.toArray(new ProgramElementDoc[result.size()]); 554 propertiesCache.put(cd, resultAray); 555 return resultAray; 556 } 557 558 private void addToPropertiesMap(MethodDoc setter, 559 MethodDoc getter, 560 MethodDoc propertyMethod, 561 FieldDoc field) { 562 if ((field == null) 563 || (field.getRawCommentText() == null) 564 || field.getRawCommentText().length() == 0) { 565 addToPropertiesMap(setter, propertyMethod); 566 addToPropertiesMap(getter, propertyMethod); 567 addToPropertiesMap(propertyMethod, propertyMethod); 568 } else { 569 addToPropertiesMap(getter, field); 570 addToPropertiesMap(setter, field); 571 addToPropertiesMap(propertyMethod, field); 572 } 573 } 574 575 private void addToPropertiesMap(ProgramElementDoc propertyMethod, 576 ProgramElementDoc commentSource) { 577 if (null == propertyMethod || null == commentSource) { 578 return; 579 } 580 final String methodRawCommentText = propertyMethod.getRawCommentText(); 581 582 /* The second condition is required for the property buckets. In 583 * this case the comment is at the property method (not at the field) 584 * and it needs to be listed in the map. 585 */ 586 if ((null == methodRawCommentText || 0 == methodRawCommentText.length()) 587 || propertyMethod.equals(commentSource)) { 588 classPropertiesMap.put(propertyMethod, commentSource); 589 } 590 } 591 592 private MethodDoc getterForField(MethodDoc[] methods, 593 MethodDoc propertyMethod) { 594 final String propertyMethodName = propertyMethod.name(); 595 final String fieldName = 596 propertyMethodName.substring(0, 597 propertyMethodName.lastIndexOf("Property")); 598 final String fieldNameUppercased = 599 "" + Character.toUpperCase(fieldName.charAt(0)) 600 + fieldName.substring(1); 601 final String getterNamePattern; 602 final String fieldTypeName = propertyMethod.returnType().toString(); 603 if ("boolean".equals(fieldTypeName) 604 || fieldTypeName.endsWith("BooleanProperty")) { 605 getterNamePattern = "(is|get)" + fieldNameUppercased; 606 } else { 607 getterNamePattern = "get" + fieldNameUppercased; 608 } 609 610 for (MethodDoc methodDoc : methods) { 611 if (Pattern.matches(getterNamePattern, methodDoc.name())) { 612 if (0 == methodDoc.parameters().length 613 && (methodDoc.isPublic() || methodDoc.isProtected())) { 614 return methodDoc; 615 } 616 } 617 } 618 return null; 619 } 620 621 private MethodDoc setterForField(MethodDoc[] methods, 622 MethodDoc propertyMethod) { 623 final String propertyMethodName = propertyMethod.name(); 624 final String fieldName = 625 propertyMethodName.substring(0, 626 propertyMethodName.lastIndexOf("Property")); 627 final String fieldNameUppercased = 628 "" + Character.toUpperCase(fieldName.charAt(0)) 629 + fieldName.substring(1); 630 final String setter = "set" + fieldNameUppercased; 631 632 for (MethodDoc methodDoc : methods) { 633 if (setter.equals(methodDoc.name())) { 634 if (1 == methodDoc.parameters().length 635 && "void".equals(methodDoc.returnType().simpleTypeName()) 636 && (methodDoc.isPublic() || methodDoc.isProtected())) { 637 return methodDoc; 638 } 639 } 640 } 641 return null; 642 } 643 644 private FieldDoc fieldForProperty(FieldDoc[] fields, MethodDoc property) { 645 646 for (FieldDoc field : fields) { 647 final String fieldName = field.name(); 648 final String propertyName = fieldName + "Property"; 649 if (propertyName.equals(property.name())) { 650 return field; 651 } 652 } 653 return null; 654 } 655 656 // properties aren't named setA* or getA* 657 private final Pattern pattern = Pattern.compile("[sg]et\\p{Upper}.*"); 658 private boolean isPropertyMethod(MethodDoc method) { 659 if (!configuration.javafx) { 660 return false; 661 } 662 if (!method.name().endsWith("Property")) { 663 return false; 664 } 665 666 if (! memberIsVisible(method)) { 667 return false; 668 } 669 670 if (pattern.matcher(method.name()).matches()) { 671 return false; 672 } 673 if (method.typeParameters().length > 0) { 674 return false; 675 } 676 return 0 == method.parameters().length 677 && !"void".equals(method.returnType().simpleTypeName()); 678 } 679 680 private void checkOnPropertiesTags(MethodDoc[] members) { 681 for (MethodDoc methodDoc: members) { 682 if (methodDoc.isIncluded()) { 683 for (Tag tag: methodDoc.tags()) { 684 String tagName = tag.name(); 685 if (tagName.equals("@propertySetter") 686 || tagName.equals("@propertyGetter") 687 || tagName.equals("@propertyDescription")) { 688 if (!isPropertyGetterOrSetter(members, methodDoc)) { 689 configuration.message.warning(tag.position(), 690 "doclet.javafx_tag_misuse"); 691 } 692 break; 693 } 694 } 695 } 696 } 697 } 698 699 private boolean isPropertyGetterOrSetter(MethodDoc[] members, 700 MethodDoc methodDoc) { 701 boolean found = false; 702 String propertyName = utils.propertyNameFromMethodName(configuration, methodDoc.name()); 703 if (!propertyName.isEmpty()) { 704 String propertyMethodName = propertyName + "Property"; 705 for (MethodDoc member: members) { 706 if (member.name().equals(propertyMethodName)) { 707 found = true; 708 break; 709 } 710 } 711 } 712 return found; 713 } 714 } 715 716 private class GetterSetter { 717 private final ProgramElementDoc getter; 718 private final ProgramElementDoc setter; 719 720 public GetterSetter(ProgramElementDoc getter, ProgramElementDoc setter) { 721 this.getter = getter; 722 this.setter = setter; 723 } 724 725 public ProgramElementDoc getGetter() { 726 return getter; 727 } 728 729 public ProgramElementDoc getSetter() { 730 return setter; 731 } 732 } 733 734 /** 735 * Return true if this map has no visible members. 736 * 737 * @return true if this map has no visible members. 738 */ 739 public boolean noVisibleMembers() { 740 return noVisibleMembers; 741 } 742 743 private ClassMember getClassMember(MethodDoc member) { 744 for (Object key : memberNameMap.keySet()) { 745 if (key instanceof String) { 746 continue; 747 } else if (((ClassMember) key).isEqual(member)) { 748 return (ClassMember) key; 749 } 750 } 751 return new ClassMember(member); 752 } 753 754 /** 755 * Return the key to the member map for the given member. 756 */ 757 private Object getMemberKey(ProgramElementDoc doc) { 758 if (doc.isConstructor()) { 759 return doc.name() + ((ExecutableMemberDoc)doc).signature(); 760 } else if (doc.isMethod()) { 761 return getClassMember((MethodDoc) doc); 762 } else if (doc.isField() || doc.isEnumConstant() || doc.isAnnotationTypeElement()) { 763 return doc.name(); 764 } else { // it's a class or interface 765 String classOrIntName = doc.name(); 766 //Strip off the containing class name because we only want the member name. 767 classOrIntName = classOrIntName.indexOf('.') != 0 ? classOrIntName.substring(classOrIntName.lastIndexOf('.'), classOrIntName.length()) : classOrIntName; 768 return "clint" + classOrIntName; 769 } 770 } 771 } | 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.util; 27 28 import java.util.*; 29 import java.util.regex.Pattern; 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 import javax.lang.model.type.TypeKind; 36 import javax.lang.model.type.TypeMirror; 37 38 import com.sun.source.doctree.DocCommentTree; 39 import com.sun.source.doctree.DocTree; 40 41 import jdk.javadoc.internal.doclets.toolkit.Configuration; 42 43 /** 44 * A data structure that encapsulates the visible members of a particular 45 * type for a given class tree. To use this data structure, you must specify 46 * the type of member you are interested in (nested class, field, constructor 47 * or method) and the leaf of the class tree. The data structure will map 48 * all visible members in the leaf and classes above the leaf in the tree. 49 * 50 * <p><b>This is NOT part of any supported API. 51 * If you write code that depends on this, you do so at your own risk. 52 * This code and its internal interfaces are subject to change or 53 * deletion without notice.</b> 54 * 55 * @author Atul M Dambalkar 56 * @author Jamie Ho (rewrite) 57 */ 58 public class VisibleMemberMap { 59 60 private boolean noVisibleMembers = true; 61 62 public static enum Kind { 63 INNER_CLASSES, 64 ENUM_CONSTANTS, 65 FIELDS, 66 CONSTRUCTORS, 67 METHODS, 68 ANNOTATION_TYPE_FIELDS, 69 ANNOTATION_TYPE_MEMBER_OPTIONAL, 70 ANNOTATION_TYPE_MEMBER_REQUIRED, 71 PROPERTIES; 72 73 public static final EnumSet<Kind> summarySet = EnumSet.range(INNER_CLASSES, METHODS); 74 public static final EnumSet<Kind> detailSet = EnumSet.range(ENUM_CONSTANTS, METHODS); 75 public static String getNavLinkLabels(Kind kind) { 76 switch (kind) { 77 case INNER_CLASSES: 78 return "doclet.navNested"; 79 case ENUM_CONSTANTS: 80 return "doclet.navEnum"; 81 case FIELDS: 82 return "doclet.navField"; 83 case CONSTRUCTORS: 84 return "doclet.navConstructor"; 85 case METHODS: 86 return "doclet.navMethod"; 87 default: 88 throw new AssertionError("unknown kind:" + kind); 89 } 90 } 91 } 92 93 public static final String STARTLEVEL = "start"; 94 95 // properties aren't named setA* or getA* 96 private static final Pattern GETTERSETTERPATTERN = Pattern.compile("[sg]et\\p{Upper}.*"); 97 /** 98 * List of TypeElement objects for which ClassMembers objects are built. 99 */ 100 private final Set<TypeElement> visibleClasses; 101 102 /** 103 * Map for each member name on to a map which contains members with same 104 * name-signature. The mapped map will contain mapping for each MemberDoc 105 * onto it's respecive level string. 106 */ 107 private final Map<Object, Map<Element, String>> memberNameMap = new HashMap<>(); 108 109 /** 110 * Map of class and it's ClassMembers object. 111 */ 112 private final Map<TypeElement, ClassMembers> classMap = new HashMap<>(); 113 114 /** 115 * Type whose visible members are requested. This is the leaf of 116 * the class tree being mapped. 117 */ 118 private final TypeElement typeElement; 119 120 /** 121 * Member kind: InnerClasses/Fields/Methods? 122 */ 123 private final Kind kind; 124 125 /** 126 * The configuration this VisibleMemberMap was created with. 127 */ 128 private final Configuration configuration; 129 private final Utils utils; 130 private final Comparator<Element> comparator; 131 132 private static final Map<TypeElement, List<Element>> propertiesCache = new HashMap<>(); 133 private static final Map<Element, Element> classPropertiesMap = new HashMap<>(); 134 private static final Map<Element, GetterSetter> getterSetterMap = new HashMap<>(); 135 136 /** 137 * Construct a VisibleMemberMap of the given type for the given class. 138 * 139 * @param typeElement whose members are being mapped. 140 * @param kind the kind of member that is being mapped. 141 * @param configuration the configuration to use to construct this 142 * VisibleMemberMap. If the field configuration.nodeprecated is true the 143 * deprecated members are excluded from the map. If the field 144 * configuration.javafx is true the JavaFX features are used. 145 */ 146 public VisibleMemberMap(TypeElement typeElement, 147 Kind kind, 148 Configuration configuration) { 149 this.typeElement = typeElement; 150 this.kind = kind; 151 this.configuration = configuration; 152 this.utils = configuration.utils; 153 comparator = utils.makeGeneralPurposeComparator(); 154 visibleClasses = new LinkedHashSet<>(); 155 new ClassMembers(typeElement, STARTLEVEL).build(); 156 } 157 158 /** 159 * Return the list of visible classes in this map. 160 * 161 * @return the list of visible classes in this map. 162 */ 163 public SortedSet<TypeElement> getVisibleClasses() { 164 SortedSet<TypeElement> vClasses = new TreeSet<>(comparator); 165 vClasses.addAll(visibleClasses); 166 return vClasses; 167 } 168 169 /** 170 * Returns the property field documentation belonging to the given member. 171 * @param element the member for which the property documentation is needed. 172 * @return the property field documentation, null if there is none. 173 */ 174 public Element getPropertyMemberDoc(Element element) { 175 return classPropertiesMap.get(element); 176 } 177 178 /** 179 * Returns the getter documentation belonging to the given property method. 180 * @param propertyMethod the method for which the getter is needed. 181 * @return the getter documentation, null if there is none. 182 */ 183 public Element getGetterForProperty(Element propertyMethod) { 184 return getterSetterMap.get(propertyMethod).getGetter(); 185 } 186 187 /** 188 * Returns the setter documentation belonging to the given property method. 189 * @param propertyMethod the method for which the setter is needed. 190 * @return the setter documentation, null if there is none. 191 */ 192 public Element getSetterForProperty(Element propertyMethod) { 193 return getterSetterMap.get(propertyMethod).getSetter(); 194 } 195 196 /** 197 * Return the package private members inherited by the class. Only return 198 * if parent is package private and not documented. 199 * 200 * @return the package private members inherited by the class. 201 */ 202 private List<Element> getInheritedPackagePrivateMethods() { 203 List<Element> results = new ArrayList<>(); 204 for (TypeElement currentClass : visibleClasses) { 205 if (currentClass != typeElement && 206 utils.isPackagePrivate(currentClass) && 207 !utils.isLinkable(currentClass)) { 208 // Document these members in the child class because 209 // the parent is inaccessible. 210 results.addAll(classMap.get(currentClass).members); 211 } 212 } 213 return results; 214 } 215 216 /** 217 * Return the visible members of the class being mapped. Also append at the 218 * end of the list members that are inherited by inaccessible parents. We 219 * document these members in the child because the parent is not documented. 220 * 221 * @param configuration the current configuration of the doclet. 222 */ 223 public SortedSet<Element> getLeafClassMembers() { 224 SortedSet<Element> result = getMembersFor(typeElement); 225 result.addAll(getInheritedPackagePrivateMethods()); 226 return result; 227 } 228 229 public Set<Element> getLeafClassMembersSourceOrder() { 230 Set<Element> result = new LinkedHashSet<>(classMap.get(typeElement).members); 231 result.addAll(getInheritedPackagePrivateMethods()); 232 return result; 233 } 234 235 /** 236 * Retrn the list of members for the given class. 237 * 238 * @param typeElement the class to retrieve the list of visible members for. 239 * 240 * @return the list of members for the given class. 241 */ 242 public SortedSet<Element> getMembersFor(TypeElement typeElement) { 243 return asSortedSet(classMap.get(typeElement).members); 244 } 245 246 public boolean hasMembersFor(TypeElement typeElement) { 247 return !classMap.get(typeElement).members.isEmpty(); 248 } 249 250 private SortedSet<Element> asSortedSet(Collection<Element> in) { 251 if (in == null) { 252 return Collections.emptySortedSet(); 253 } 254 TreeSet<Element> out = new TreeSet<>(comparator); 255 out.addAll(in); 256 return out; 257 } 258 259 private void fillMemberLevelMap(List<? extends Element> list, String level) { 260 for (Element element : list) { 261 Object key = getMemberKey(element); 262 Map<Element, String> memberLevelMap = memberNameMap.get(key); 263 if (memberLevelMap == null) { 264 memberLevelMap = new HashMap<>(); 265 memberNameMap.put(key, memberLevelMap); 266 } 267 memberLevelMap.put(element, level); 268 } 269 } 270 271 private void purgeMemberLevelMap(Iterable<? extends Element> list, String level) { 272 for (Element element : list) { 273 Object key = getMemberKey(element); 274 Map<Element, String> memberLevelMap = memberNameMap.get(key); 275 if (memberLevelMap != null && level.equals(memberLevelMap.get(element))) 276 memberLevelMap.remove(element); 277 } 278 } 279 280 /** 281 * Represents a class member. 282 */ 283 private class ClassMember { 284 private Set<Element> members; 285 286 public ClassMember(Element element) { 287 members = new HashSet<>(); 288 members.add(element); 289 } 290 291 public boolean isEqual(ExecutableElement member) { 292 for (Element element : members) { 293 if (utils.executableMembersEqual(member, (ExecutableElement) element)) { 294 members.add(member); 295 return true; 296 } 297 } 298 return false; 299 } 300 } 301 302 /** 303 * A data structure that represents the class members for 304 * a visible class. 305 */ 306 private class ClassMembers { 307 308 /** 309 * The mapping class, whose inherited members are put in the 310 * {@link #members} list. 311 */ 312 private final TypeElement typeElement; 313 314 /** 315 * List of inherited members from the mapping class. 316 */ 317 private Set<Element> members = new LinkedHashSet<>(); 318 319 /** 320 * Level/Depth of inheritance. 321 */ 322 private final String level; 323 324 private ClassMembers(TypeElement mappingClass, String level) { 325 this.typeElement = mappingClass; 326 this.level = level; 327 if (classMap.containsKey(mappingClass) && 328 level.startsWith(classMap.get(mappingClass).level)) { 329 //Remove lower level class so that it can be replaced with 330 //same class found at higher level. 331 purgeMemberLevelMap(getClassMembers(mappingClass, false), 332 classMap.get(mappingClass).level); 333 classMap.remove(mappingClass); 334 visibleClasses.remove(mappingClass); 335 } 336 if (!classMap.containsKey(mappingClass)) { 337 classMap.put(mappingClass, this); 338 visibleClasses.add(mappingClass); 339 } 340 } 341 342 private void build() { 343 if (kind == Kind.CONSTRUCTORS) { 344 addMembers(typeElement); 345 } else { 346 mapClass(); 347 } 348 } 349 350 private void mapClass() { 351 addMembers(typeElement); 352 List<? extends TypeMirror> interfaces = typeElement.getInterfaces(); 353 for (TypeMirror anInterface : interfaces) { 354 String locallevel = level + 1; 355 ClassMembers cm = new ClassMembers(utils.asTypeElement(anInterface), locallevel); 356 cm.mapClass(); 357 } 358 if (utils.isClass(typeElement)) { 359 TypeElement superclass = utils.getSuperClass(typeElement); 360 if (!(superclass == null || typeElement.equals(superclass))) { 361 ClassMembers cm = new ClassMembers(superclass, level + "c"); 362 cm.mapClass(); 363 } 364 } 365 } 366 367 /** 368 * Get all the valid members from the mapping class. Get the list of 369 * members for the class to be included into(ctii), also get the level 370 * string for ctii. If mapping class member is not already in the 371 * inherited member list and if it is visible in the ctii and not 372 * overridden, put such a member in the inherited member list. 373 * Adjust member-level-map, class-map. 374 */ 375 private void addMembers(TypeElement fromClass) { 376 List<? extends Element> classMembers = getClassMembers(fromClass, true); 377 List<Element> incllist = new ArrayList<>(); 378 for (Element element : classMembers) { 379 if (!found(members, element)) { 380 if (memberIsVisible(element)) { 381 if (!isOverridden(element, level)) { 382 if (!isTreatedAsPrivate(element)) { 383 incllist.add(element); 384 } 385 } 386 } 387 } 388 } 389 if (!incllist.isEmpty()) { 390 noVisibleMembers = false; 391 } 392 members.addAll(incllist); 393 fillMemberLevelMap(getClassMembers(fromClass, false), level); 394 } 395 396 private boolean isTreatedAsPrivate(Element pgmelem) { 397 if (!configuration.javafx) { 398 return false; 399 } 400 401 List<? extends DocTree> aspTags = utils.getBlockTags(pgmelem, "@treatAsPrivate"); 402 boolean result = (aspTags != null) && (!aspTags.isEmpty()); 403 return result; 404 } 405 406 /** 407 * Is given element visible in given typeElement in terms of inheritance? The given element 408 * is visible in the given typeElement if it is public or protected and if it is 409 * package-private if it's containing class is in the same package as the given typeElement. 410 */ 411 private boolean memberIsVisible(Element element) { 412 if (utils.getEnclosingTypeElement(element).equals(VisibleMemberMap.this.typeElement)) { 413 //Member is in class that we are finding visible members for. 414 //Of course it is visible. 415 return true; 416 } else if (utils.isPrivate(element)) { 417 //Member is in super class or implemented interface. 418 //Private, so not inherited. 419 return false; 420 } else if (utils.isPackagePrivate(element)) { 421 //Member is package private. Only return true if its class is in 422 //same package. 423 return utils.containingPackage(element).equals(utils.containingPackage(VisibleMemberMap.this.typeElement)); 424 } else { 425 //Public members are always inherited. 426 return true; 427 } 428 } 429 430 /** 431 * Return all available class members. 432 */ 433 private List<? extends Element> getClassMembers(TypeElement te, boolean filter) { 434 if (utils.isEnum(te) && kind == Kind.CONSTRUCTORS) { 435 //If any of these rules are hit, return empty array because 436 //we don't document these members ever. 437 return Collections.emptyList(); 438 } 439 List<? extends Element> list; 440 switch (kind) { 441 case ANNOTATION_TYPE_FIELDS: 442 list = (filter) 443 ? utils.getAnnotationFields(te) 444 : utils.getAnnotationFieldsUnfiltered(te); 445 break; 446 case ANNOTATION_TYPE_MEMBER_OPTIONAL: 447 list = utils.isAnnotationType(te) 448 ? filterAnnotations(te, false) 449 : Collections.emptyList(); 450 break; 451 case ANNOTATION_TYPE_MEMBER_REQUIRED: 452 list = utils.isAnnotationType(te) 453 ? filterAnnotations(te, true) 454 : Collections.emptyList(); 455 break; 456 case INNER_CLASSES: 457 List<TypeElement> xlist = filter 458 ? utils.getInnerClasses(te) 459 : utils.getInnerClassesUnfiltered(te); 460 list = new ArrayList<>(xlist); 461 break; 462 case ENUM_CONSTANTS: 463 list = utils.getEnumConstants(te); 464 break; 465 case FIELDS: 466 if (filter) { 467 list = utils.isAnnotationType(te) 468 ? utils.getAnnotationFields(te) 469 : utils.getFields(te); 470 } else { 471 list = utils.isAnnotationType(te) 472 ? utils.getAnnotationFieldsUnfiltered(te) 473 : utils.getFieldsUnfiltered(te); 474 } 475 break; 476 case CONSTRUCTORS: 477 list = utils.getConstructors(te); 478 break; 479 case METHODS: 480 list = filter ? utils.getMethods(te) : utils.getMethodsUnfiltered(te); 481 checkOnPropertiesTags(list); 482 break; 483 case PROPERTIES: 484 list = properties(te, filter); 485 break; 486 default: 487 list = Collections.emptyList(); 488 } 489 // Deprected members should be excluded or not? 490 if (configuration.nodeprecated) { 491 return utils.excludeDeprecatedMembers(list); 492 } 493 return list; 494 } 495 496 /** 497 * Filter the annotation type members and return either the required 498 * members or the optional members, depending on the value of the 499 * required parameter. 500 * 501 * @param typeElement The annotation type to process. 502 * @param required 503 * @return the annotation type members and return either the required 504 * members or the optional members, depending on the value of the 505 * required parameter. 506 */ 507 private List<Element> filterAnnotations(TypeElement typeElement, boolean required) { 508 List<Element> members = utils.getAnnotationMethods(typeElement); 509 List<Element> targetMembers = new ArrayList<>(); 510 for (Element member : members) { 511 ExecutableElement ee = (ExecutableElement)member; 512 if ((required && ee.getDefaultValue() == null) 513 || ((!required) && ee.getDefaultValue() != null)) { 514 targetMembers.add(member); 515 } 516 } 517 return targetMembers; 518 } 519 520 private boolean found(Iterable<Element> list, Element elem) { 521 for (Element pgmelem : list) { 522 if (utils.matches(pgmelem, elem)) { 523 return true; 524 } 525 } 526 return false; 527 } 528 529 530 /** 531 * Is member overridden? The member is overridden if it is found in the 532 * same level hierarchy e.g. member at level "11" overrides member at 533 * level "111". 534 */ 535 private boolean isOverridden(Element element, String level) { 536 Object key = getMemberKey(element); 537 Map<?, String> memberLevelMap = (Map<?, String>) memberNameMap.get(key); 538 if (memberLevelMap == null) 539 return false; 540 for (String mappedlevel : memberLevelMap.values()) { 541 if (mappedlevel.equals(STARTLEVEL) 542 || (level.startsWith(mappedlevel) 543 && !level.equals(mappedlevel))) { 544 return true; 545 } 546 } 547 return false; 548 } 549 550 private List<Element> properties(final TypeElement typeElement, final boolean filter) { 551 final List<ExecutableElement> allMethods = filter 552 ? utils.getMethods(typeElement) 553 : utils.getMethodsUnfiltered(typeElement); 554 final List<VariableElement> allFields = utils.getFieldsUnfiltered(typeElement); 555 556 if (propertiesCache.containsKey(typeElement)) { 557 return propertiesCache.get(typeElement); 558 } 559 560 final List<Element> result = new ArrayList<>(); 561 562 for (final Element propertyMethod : allMethods) { 563 ExecutableElement ee = (ExecutableElement)propertyMethod; 564 if (!isPropertyMethod(ee)) { 565 continue; 566 } 567 568 final ExecutableElement getter = getterForField(allMethods, ee); 569 final ExecutableElement setter = setterForField(allMethods, ee); 570 final VariableElement field = fieldForProperty(allFields, ee); 571 572 addToPropertiesMap(setter, getter, ee, field); 573 getterSetterMap.put(propertyMethod, new GetterSetter(getter, setter)); 574 result.add(ee); 575 } 576 propertiesCache.put(typeElement, result); 577 return result; 578 } 579 580 private void addToPropertiesMap(ExecutableElement setter, 581 ExecutableElement getter, 582 ExecutableElement propertyMethod, 583 VariableElement field) { 584 if (field == null || utils.getDocCommentTree(field) == null) { 585 addToPropertiesMap(setter, propertyMethod); 586 addToPropertiesMap(getter, propertyMethod); 587 addToPropertiesMap(propertyMethod, propertyMethod); 588 } else { 589 addToPropertiesMap(getter, field); 590 addToPropertiesMap(setter, field); 591 addToPropertiesMap(propertyMethod, field); 592 } 593 } 594 595 private void addToPropertiesMap(Element propertyMethod, 596 Element commentSource) { 597 if (null == propertyMethod || null == commentSource) { 598 return; 599 } 600 DocCommentTree docTree = utils.getDocCommentTree(propertyMethod); 601 602 /* The second condition is required for the property buckets. In 603 * this case the comment is at the property method (not at the field) 604 * and it needs to be listed in the map. 605 */ 606 if ((docTree == null) || propertyMethod.equals(commentSource)) { 607 classPropertiesMap.put(propertyMethod, commentSource); 608 } 609 } 610 611 private ExecutableElement getterForField(List<ExecutableElement> methods, 612 ExecutableElement propertyMethod) { 613 final String propertyMethodName = utils.getSimpleName(propertyMethod); 614 final String fieldName = propertyMethodName.substring(0, 615 propertyMethodName.lastIndexOf("Property")); 616 final String fieldNameUppercased = 617 "" + Character.toUpperCase(fieldName.charAt(0)) 618 + fieldName.substring(1); 619 final String getterNamePattern; 620 final String fieldTypeName = propertyMethod.getReturnType().toString(); 621 if ("boolean".equals(fieldTypeName) 622 || fieldTypeName.endsWith("BooleanProperty")) { 623 getterNamePattern = "(is|get)" + fieldNameUppercased; 624 } else { 625 getterNamePattern = "get" + fieldNameUppercased; 626 } 627 628 for (ExecutableElement method : methods) { 629 if (Pattern.matches(getterNamePattern, utils.getSimpleName(method))) { 630 if (method.getParameters().isEmpty() && 631 utils.isPublic(method) || utils.isProtected(method)) { 632 return method; 633 } 634 } 635 } 636 return null; 637 } 638 639 private ExecutableElement setterForField(List<ExecutableElement> methods, 640 ExecutableElement propertyMethod) { 641 final String propertyMethodName = utils.getSimpleName(propertyMethod); 642 final String fieldName = 643 propertyMethodName.substring(0, 644 propertyMethodName.lastIndexOf("Property")); 645 final String fieldNameUppercased = 646 "" + Character.toUpperCase(fieldName.charAt(0)) 647 + fieldName.substring(1); 648 final String setter = "set" + fieldNameUppercased; 649 650 for (ExecutableElement method : methods) { 651 if (setter.equals(utils.getSimpleName(method))) { 652 if (method.getParameters().size() == 1 653 && method.getReturnType().getKind() == TypeKind.VOID 654 && (utils.isPublic(method) || utils.isProtected(method))) { 655 return method; 656 } 657 } 658 } 659 return null; 660 } 661 662 private VariableElement fieldForProperty(List<VariableElement> fields, ExecutableElement property) { 663 664 for (VariableElement field : fields) { 665 final String fieldName = utils.getSimpleName(field); 666 final String propertyName = fieldName + "Property"; 667 if (propertyName.equals(utils.getSimpleName(property))) { 668 return field; 669 } 670 } 671 return null; 672 } 673 674 private boolean isPropertyMethod(ExecutableElement method) { 675 if (!configuration.javafx) { 676 return false; 677 } 678 if (!utils.getSimpleName(method).endsWith("Property")) { 679 return false; 680 } 681 682 if (!memberIsVisible(method)) { 683 return false; 684 } 685 686 if (GETTERSETTERPATTERN.matcher(utils.getSimpleName(method)).matches()) { 687 return false; 688 } 689 if (!method.getTypeParameters().isEmpty()) { 690 return false; 691 } 692 return method.getParameters().isEmpty() 693 && method.getReturnType().getKind() != TypeKind.VOID; 694 } 695 696 private void checkOnPropertiesTags(List<? extends Element> members) { 697 for (Element e: members) { 698 ExecutableElement ee = (ExecutableElement)e; 699 if (utils.isIncluded(ee)) { 700 CommentHelper ch = utils.getCommentHelper(ee); 701 for (DocTree tree: utils.getBlockTags(ee)) { 702 String tagName = ch.getTagName(tree); 703 if (tagName.equals("@propertySetter") 704 || tagName.equals("@propertyGetter") 705 || tagName.equals("@propertyDescription")) { 706 if (!isPropertyGetterOrSetter(members, ee)) { 707 configuration.message.warning(ch.getDocTreePath(tree), 708 "doclet.javafx_tag_misuse"); 709 } 710 break; 711 } 712 } 713 } 714 } 715 } 716 717 private boolean isPropertyGetterOrSetter(List<? extends Element> members, 718 ExecutableElement method) { 719 String propertyName = utils.propertyName(method); 720 if (!propertyName.isEmpty()) { 721 String propertyMethodName = propertyName + "Property"; 722 for (Element member: members) { 723 if (utils.getSimpleName(member).equals(propertyMethodName)) { 724 return true; 725 } 726 } 727 } 728 return false; 729 } 730 } 731 732 private class GetterSetter { 733 private final Element getter; 734 private final Element setter; 735 736 public GetterSetter(Element getter, Element setter) { 737 this.getter = getter; 738 this.setter = setter; 739 } 740 741 public Element getGetter() { 742 return getter; 743 } 744 745 public Element getSetter() { 746 return setter; 747 } 748 } 749 750 /** 751 * Return true if this map has no visible members. 752 * 753 * @return true if this map has no visible members. 754 */ 755 public boolean noVisibleMembers() { 756 return noVisibleMembers; 757 } 758 759 private ClassMember getClassMember(ExecutableElement member) { 760 for (Object key : memberNameMap.keySet()) { 761 if (key instanceof String) { 762 continue; 763 } 764 if (((ClassMember) key).isEqual(member)) { 765 return (ClassMember) key; 766 } 767 } 768 return new ClassMember(member); 769 } 770 771 /** 772 * Return the key to the member map for the given member. 773 */ 774 private Object getMemberKey(Element element) { 775 if (utils.isConstructor(element)) { 776 return utils.getSimpleName(element) + utils.flatSignature((ExecutableElement)element); 777 } else if (utils.isMethod(element)) { 778 return getClassMember((ExecutableElement) element); 779 } else if (utils.isField(element) || utils.isEnumConstant(element) || utils.isAnnotationType(element)) { 780 return utils.getSimpleName(element); 781 } else { // it's a class or interface 782 String classOrIntName = utils.getSimpleName(element); 783 //Strip off the containing class name because we only want the member name. 784 classOrIntName = classOrIntName.indexOf('.') != 0 785 ? classOrIntName.substring(classOrIntName.lastIndexOf('.')) 786 : classOrIntName; 787 return "clint" + classOrIntName; 788 } 789 } 790 } |