1.1 --- a/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/VisibleMemberMap.java Fri Feb 22 18:19:51 2013 +0000 1.2 +++ b/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/VisibleMemberMap.java Sun Feb 24 11:36:58 2013 -0800 1.3 @@ -1,5 +1,5 @@ 1.4 /* 1.5 - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. 1.6 + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. 1.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.8 * 1.9 * This code is free software; you can redistribute it and/or modify it 1.10 @@ -26,6 +26,7 @@ 1.11 package com.sun.tools.doclets.internal.toolkit.util; 1.12 1.13 import java.util.*; 1.14 +import java.util.regex.Pattern; 1.15 1.16 import com.sun.javadoc.*; 1.17 import com.sun.tools.doclets.internal.toolkit.*; 1.18 @@ -56,11 +57,12 @@ 1.19 public static final int METHODS = 4; 1.20 public static final int ANNOTATION_TYPE_MEMBER_OPTIONAL = 5; 1.21 public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 6; 1.22 + public static final int PROPERTIES = 7; 1.23 1.24 /** 1.25 * The total number of member types is {@value}. 1.26 */ 1.27 - public static final int NUM_MEMBER_TYPES = 7; 1.28 + public static final int NUM_MEMBER_TYPES = 8; 1.29 1.30 public static final String STARTLEVEL = "start"; 1.31 1.32 @@ -93,23 +95,34 @@ 1.33 private final int kind; 1.34 1.35 /** 1.36 - * Deprected members should be excluded or not? 1.37 + * The configuration this VisibleMemberMap was created with. 1.38 */ 1.39 - private final boolean nodepr; 1.40 + private final Configuration configuration; 1.41 + 1.42 + private static final Map<ClassDoc, ProgramElementDoc[]> propertiesCache = 1.43 + new HashMap<ClassDoc, ProgramElementDoc[]>(); 1.44 + private static final Map<ProgramElementDoc, ProgramElementDoc> classPropertiesMap = 1.45 + new HashMap<ProgramElementDoc, ProgramElementDoc>(); 1.46 + private static final Map<ProgramElementDoc, GetterSetter> getterSetterMap = 1.47 + new HashMap<ProgramElementDoc, GetterSetter>(); 1.48 1.49 /** 1.50 * Construct a VisibleMemberMap of the given type for the given 1.51 - * class. If nodepr is true, exclude the deprecated members from 1.52 - * the map. 1.53 + * class. 1.54 * 1.55 * @param classdoc the class whose members are being mapped. 1.56 * @param kind the kind of member that is being mapped. 1.57 - * @param nodepr if true, exclude the deprecated members from the map. 1.58 + * @param configuration the configuration to use to construct this 1.59 + * VisibleMemberMap. If the field configuration.nodeprecated is true the 1.60 + * deprecated members are excluded from the map. If the field 1.61 + * configuration.javafx is true the JavaFX features are used. 1.62 */ 1.63 - public VisibleMemberMap(ClassDoc classdoc, int kind, boolean nodepr) { 1.64 + public VisibleMemberMap(ClassDoc classdoc, 1.65 + int kind, 1.66 + Configuration configuration) { 1.67 this.classdoc = classdoc; 1.68 - this.nodepr = nodepr; 1.69 this.kind = kind; 1.70 + this.configuration = configuration; 1.71 new ClassMembers(classdoc, STARTLEVEL).build(); 1.72 } 1.73 1.74 @@ -124,6 +137,33 @@ 1.75 } 1.76 1.77 /** 1.78 + * Returns the property field documentation belonging to the given member. 1.79 + * @param ped the member for which the property documentation is needed. 1.80 + * @return the property field documentation, null if there is none. 1.81 + */ 1.82 + public ProgramElementDoc getPropertyMemberDoc(ProgramElementDoc ped) { 1.83 + return classPropertiesMap.get(ped); 1.84 + } 1.85 + 1.86 + /** 1.87 + * Returns the getter documentation belonging to the given property method. 1.88 + * @param propertyMethod the method for which the getter is needed. 1.89 + * @return the getter documentation, null if there is none. 1.90 + */ 1.91 + public ProgramElementDoc getGetterForProperty(ProgramElementDoc propertyMethod) { 1.92 + return getterSetterMap.get(propertyMethod).getGetter(); 1.93 + } 1.94 + 1.95 + /** 1.96 + * Returns the setter documentation belonging to the given property method. 1.97 + * @param propertyMethod the method for which the setter is needed. 1.98 + * @return the setter documentation, null if there is none. 1.99 + */ 1.100 + public ProgramElementDoc getSetterForProperty(ProgramElementDoc propertyMethod) { 1.101 + return getterSetterMap.get(propertyMethod).getSetter(); 1.102 + } 1.103 + 1.104 + /** 1.105 * Return the package private members inherited by the class. Only return 1.106 * if parent is package private and not documented. 1.107 * 1.108 @@ -334,8 +374,9 @@ 1.109 ProgramElementDoc pgmelem = cdmembers.get(i); 1.110 if (!found(members, pgmelem) && 1.111 memberIsVisible(pgmelem) && 1.112 - !isOverridden(pgmelem, level)) { 1.113 - incllist.add(pgmelem); 1.114 + !isOverridden(pgmelem, level) && 1.115 + !isTreatedAsPrivate(pgmelem)) { 1.116 + incllist.add(pgmelem); 1.117 } 1.118 } 1.119 if (incllist.size() > 0) { 1.120 @@ -345,6 +386,16 @@ 1.121 fillMemberLevelMap(getClassMembers(fromClass, false), level); 1.122 } 1.123 1.124 + private boolean isTreatedAsPrivate(ProgramElementDoc pgmelem) { 1.125 + if (!configuration.javafx) { 1.126 + return false; 1.127 + } 1.128 + 1.129 + Tag[] aspTags = pgmelem.tags("@treatAsPrivate"); 1.130 + boolean result = (aspTags != null) && (aspTags.length > 0); 1.131 + return result; 1.132 + } 1.133 + 1.134 /** 1.135 * Is given doc item visible in given classdoc in terms fo inheritance? 1.136 * The given doc item is visible in the given classdoc if it is public 1.137 @@ -406,11 +457,16 @@ 1.138 break; 1.139 case METHODS: 1.140 members = cd.methods(filter); 1.141 + checkOnPropertiesTags((MethodDoc[])members); 1.142 + break; 1.143 + case PROPERTIES: 1.144 + members = properties(cd, filter); 1.145 break; 1.146 default: 1.147 members = new ProgramElementDoc[0]; 1.148 } 1.149 - if (nodepr) { 1.150 + // Deprected members should be excluded or not? 1.151 + if (configuration.nodeprecated) { 1.152 return Util.excludeDeprecatedMembersAsList(members); 1.153 } 1.154 return Arrays.asList(members); 1.155 @@ -472,6 +528,206 @@ 1.156 } 1.157 return false; 1.158 } 1.159 + 1.160 + private ProgramElementDoc[] properties(final ClassDoc cd, final boolean filter) { 1.161 + final MethodDoc[] allMethods = cd.methods(filter); 1.162 + final FieldDoc[] allFields = cd.fields(false); 1.163 + 1.164 + if (propertiesCache.containsKey(cd)) { 1.165 + return propertiesCache.get(cd); 1.166 + } 1.167 + 1.168 + final List<MethodDoc> result = new ArrayList<MethodDoc>(); 1.169 + 1.170 + for (final MethodDoc propertyMethod : allMethods) { 1.171 + 1.172 + if (!isPropertyMethod(propertyMethod)) { 1.173 + continue; 1.174 + } 1.175 + 1.176 + final MethodDoc getter = getterForField(allMethods, propertyMethod); 1.177 + final MethodDoc setter = setterForField(allMethods, propertyMethod); 1.178 + final FieldDoc field = fieldForProperty(allFields, propertyMethod); 1.179 + 1.180 + addToPropertiesMap(setter, getter, propertyMethod, field); 1.181 + getterSetterMap.put(propertyMethod, new GetterSetter(getter, setter)); 1.182 + result.add(propertyMethod); 1.183 + } 1.184 + final ProgramElementDoc[] resultAray = 1.185 + result.toArray(new ProgramElementDoc[result.size()]); 1.186 + propertiesCache.put(cd, resultAray); 1.187 + return resultAray; 1.188 + } 1.189 + 1.190 + private void addToPropertiesMap(MethodDoc setter, 1.191 + MethodDoc getter, 1.192 + MethodDoc propertyMethod, 1.193 + FieldDoc field) { 1.194 + if ((field == null) 1.195 + || (field.getRawCommentText() == null) 1.196 + || field.getRawCommentText().length() == 0) { 1.197 + addToPropertiesMap(setter, propertyMethod); 1.198 + addToPropertiesMap(getter, propertyMethod); 1.199 + addToPropertiesMap(propertyMethod, propertyMethod); 1.200 + } else { 1.201 + addToPropertiesMap(getter, field); 1.202 + addToPropertiesMap(setter, field); 1.203 + addToPropertiesMap(propertyMethod, field); 1.204 + } 1.205 + } 1.206 + 1.207 + private void addToPropertiesMap(ProgramElementDoc propertyMethod, 1.208 + ProgramElementDoc commentSource) { 1.209 + if (null == propertyMethod || null == commentSource) { 1.210 + return; 1.211 + } 1.212 + final String methodRawCommentText = propertyMethod.getRawCommentText(); 1.213 + 1.214 + /* The second condition is required for the property buckets. In 1.215 + * this case the comment is at the property method (not at the field) 1.216 + * and it needs to be listed in the map. 1.217 + */ 1.218 + if ((null == methodRawCommentText || 0 == methodRawCommentText.length()) 1.219 + || propertyMethod.equals(commentSource)) { 1.220 + classPropertiesMap.put(propertyMethod, commentSource); 1.221 + } 1.222 + } 1.223 + 1.224 + private MethodDoc getterForField(MethodDoc[] methods, 1.225 + MethodDoc propertyMethod) { 1.226 + final String propertyMethodName = propertyMethod.name(); 1.227 + final String fieldName = 1.228 + propertyMethodName.substring(0, 1.229 + propertyMethodName.lastIndexOf("Property")); 1.230 + final String fieldNameUppercased = 1.231 + "" + Character.toUpperCase(fieldName.charAt(0)) 1.232 + + fieldName.substring(1); 1.233 + final String getterNamePattern; 1.234 + final String fieldTypeName = propertyMethod.returnType().toString(); 1.235 + if ("boolean".equals(fieldTypeName) 1.236 + || fieldTypeName.endsWith("BooleanProperty")) { 1.237 + getterNamePattern = "(is|get)" + fieldNameUppercased; 1.238 + } else { 1.239 + getterNamePattern = "get" + fieldNameUppercased; 1.240 + } 1.241 + 1.242 + for (MethodDoc methodDoc : methods) { 1.243 + if (Pattern.matches(getterNamePattern, methodDoc.name())) { 1.244 + if (0 == methodDoc.parameters().length 1.245 + && (methodDoc.isPublic() || methodDoc.isProtected())) { 1.246 + return methodDoc; 1.247 + } 1.248 + } 1.249 + } 1.250 + return null; 1.251 + } 1.252 + 1.253 + private MethodDoc setterForField(MethodDoc[] methods, 1.254 + MethodDoc propertyMethod) { 1.255 + final String propertyMethodName = propertyMethod.name(); 1.256 + final String fieldName = 1.257 + propertyMethodName.substring(0, 1.258 + propertyMethodName.lastIndexOf("Property")); 1.259 + final String fieldNameUppercased = 1.260 + "" + Character.toUpperCase(fieldName.charAt(0)) 1.261 + + fieldName.substring(1); 1.262 + final String setter = "set" + fieldNameUppercased; 1.263 + 1.264 + for (MethodDoc methodDoc : methods) { 1.265 + if (setter.equals(methodDoc.name())) { 1.266 + if (1 == methodDoc.parameters().length 1.267 + && "void".equals(methodDoc.returnType().simpleTypeName()) 1.268 + && (methodDoc.isPublic() || methodDoc.isProtected())) { 1.269 + return methodDoc; 1.270 + } 1.271 + } 1.272 + } 1.273 + return null; 1.274 + } 1.275 + 1.276 + private FieldDoc fieldForProperty(FieldDoc[] fields, MethodDoc property) { 1.277 + 1.278 + for (FieldDoc field : fields) { 1.279 + final String fieldName = field.name(); 1.280 + final String propertyName = fieldName + "Property"; 1.281 + if (propertyName.equals(property.name())) { 1.282 + return field; 1.283 + } 1.284 + } 1.285 + return null; 1.286 + } 1.287 + 1.288 + // properties aren't named setA* or getA* 1.289 + private final Pattern pattern = Pattern.compile("[sg]et\\p{Upper}.*"); 1.290 + private boolean isPropertyMethod(MethodDoc method) { 1.291 + if (!method.name().endsWith("Property")) { 1.292 + return false; 1.293 + } 1.294 + 1.295 + if (! memberIsVisible(method)) { 1.296 + return false; 1.297 + } 1.298 + 1.299 + if (pattern.matcher(method.name()).matches()) { 1.300 + return false; 1.301 + } 1.302 + 1.303 + return 0 == method.parameters().length 1.304 + && !"void".equals(method.returnType().simpleTypeName()); 1.305 + } 1.306 + 1.307 + private void checkOnPropertiesTags(MethodDoc[] members) { 1.308 + for (MethodDoc methodDoc: members) { 1.309 + if (methodDoc.isIncluded()) { 1.310 + for (Tag tag: methodDoc.tags()) { 1.311 + String tagName = tag.name(); 1.312 + if (tagName.equals("@propertySetter") 1.313 + || tagName.equals("@propertyGetter") 1.314 + || tagName.equals("@propertyDescription")) { 1.315 + if (!isPropertyGetterOrSetter(members, methodDoc)) { 1.316 + configuration.message.warning(tag.position(), 1.317 + "doclet.javafx_tag_misuse"); 1.318 + } 1.319 + break; 1.320 + } 1.321 + } 1.322 + } 1.323 + } 1.324 + } 1.325 + 1.326 + private boolean isPropertyGetterOrSetter(MethodDoc[] members, 1.327 + MethodDoc methodDoc) { 1.328 + boolean found = false; 1.329 + String propertyName = Util.propertyNameFromMethodName(methodDoc.name()); 1.330 + if (!propertyName.isEmpty()) { 1.331 + String propertyMethodName = propertyName + "Property"; 1.332 + for (MethodDoc member: members) { 1.333 + if (member.name().equals(propertyMethodName)) { 1.334 + found = true; 1.335 + break; 1.336 + } 1.337 + } 1.338 + } 1.339 + return found; 1.340 + } 1.341 + } 1.342 + 1.343 + private class GetterSetter { 1.344 + private final ProgramElementDoc getter; 1.345 + private final ProgramElementDoc setter; 1.346 + 1.347 + public GetterSetter(ProgramElementDoc getter, ProgramElementDoc setter) { 1.348 + this.getter = getter; 1.349 + this.setter = setter; 1.350 + } 1.351 + 1.352 + public ProgramElementDoc getGetter() { 1.353 + return getter; 1.354 + } 1.355 + 1.356 + public ProgramElementDoc getSetter() { 1.357 + return setter; 1.358 + } 1.359 } 1.360 1.361 /**