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

changeset 1606
ccbe7ffdd867
parent 1359
25e14ad23cef
child 2035
a2a5ad0853ed
     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      /**

mercurial