src/share/classes/com/sun/tools/doclets/internal/toolkit/util/VisibleMemberMap.java

Tue, 19 Jun 2018 12:03:35 +0200

author
sgehwolf
date
Tue, 19 Jun 2018 12:03:35 +0200
changeset 3640
12470d69d5ae
parent 3006
9731ab1f18ee
child 3719
9a2d9c6eca1d
permissions
-rw-r--r--

8061305: Javadoc crashes when method name ends with "Property"
Reviewed-by: jjg

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

mercurial