Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap Maven / Gradle / Ivy
Go to download
A repackaged copy of javac for Error Prone to depend on
/*
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.javadoc.internal.doclets.toolkit.util;
import java.util.*;
import java.util.regex.Pattern;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.toolkit.Configuration;
import jdk.javadoc.internal.doclets.toolkit.Messages;
/**
* A data structure that encapsulates the visible members of a particular
* type for a given class tree. To use this data structure, you must specify
* the type of member you are interested in (nested class, field, constructor
* or method) and the leaf of the class tree. The data structure will map
* all visible members in the leaf and classes above the leaf in the tree.
*
* This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.
*
* @author Atul M Dambalkar
* @author Jamie Ho (rewrite)
*/
public class VisibleMemberMap {
private boolean noVisibleMembers = true;
public static enum Kind {
INNER_CLASSES,
ENUM_CONSTANTS,
FIELDS,
CONSTRUCTORS,
METHODS,
ANNOTATION_TYPE_FIELDS,
ANNOTATION_TYPE_MEMBER_OPTIONAL,
ANNOTATION_TYPE_MEMBER_REQUIRED,
PROPERTIES;
public static final EnumSet summarySet = EnumSet.range(INNER_CLASSES, METHODS);
public static final EnumSet detailSet = EnumSet.range(ENUM_CONSTANTS, METHODS);
public static String getNavLinkLabels(Kind kind) {
switch (kind) {
case INNER_CLASSES:
return "doclet.navNested";
case ENUM_CONSTANTS:
return "doclet.navEnum";
case FIELDS:
return "doclet.navField";
case CONSTRUCTORS:
return "doclet.navConstructor";
case METHODS:
return "doclet.navMethod";
default:
throw new AssertionError("unknown kind:" + kind);
}
}
}
public static final String STARTLEVEL = "start";
// properties aren't named setA* or getA*
private static final Pattern GETTERSETTERPATTERN = Pattern.compile("[sg]et\\p{Upper}.*");
/**
* List of TypeElement objects for which ClassMembers objects are built.
*/
private final Set visibleClasses;
/**
* Map for each member name on to a map which contains members with same
* name-signature. The mapped map will contain mapping for each MemberDoc
* onto it's respecive level string.
*/
private final Map> memberNameMap = new HashMap<>();
/**
* Map of class and it's ClassMembers object.
*/
private final Map classMap = new HashMap<>();
/**
* Type whose visible members are requested. This is the leaf of
* the class tree being mapped.
*/
private final TypeElement typeElement;
/**
* Member kind: InnerClasses/Fields/Methods?
*/
private final Kind kind;
/**
* The configuration this VisibleMemberMap was created with.
*/
private final Configuration configuration;
private final Messages messages;
private final Utils utils;
private final Comparator comparator;
private final Map> propertiesCache;
private final Map classPropertiesMap;
private final Map getterSetterMap;
/**
* Construct a VisibleMemberMap of the given type for the given class.
*
* @param typeElement whose members are being mapped.
* @param kind the kind of member that is being mapped.
* @param configuration the configuration to use to construct this
* VisibleMemberMap. If the field configuration.nodeprecated is true the
* deprecated members are excluded from the map. If the field
* configuration.javafx is true the JavaFX features are used.
*/
public VisibleMemberMap(TypeElement typeElement,
Kind kind,
Configuration configuration) {
this.typeElement = typeElement;
this.kind = kind;
this.configuration = configuration;
this.messages = configuration.getMessages();
this.utils = configuration.utils;
propertiesCache = configuration.propertiesCache;
classPropertiesMap = configuration.classPropertiesMap;
getterSetterMap = configuration.getterSetterMap;
comparator = utils.makeGeneralPurposeComparator();
visibleClasses = new LinkedHashSet<>();
new ClassMembers(typeElement, STARTLEVEL).build();
}
/**
* Return the list of visible classes in this map.
*
* @return the list of visible classes in this map.
*/
public SortedSet getVisibleClasses() {
SortedSet vClasses = new TreeSet<>(comparator);
vClasses.addAll(visibleClasses);
return vClasses;
}
/**
* Returns the property field documentation belonging to the given member.
* @param element the member for which the property documentation is needed.
* @return the property field documentation, null if there is none.
*/
public Element getPropertyMemberDoc(Element element) {
return classPropertiesMap.get(element);
}
/**
* Returns the getter documentation belonging to the given property method.
* @param propertyMethod the method for which the getter is needed.
* @return the getter documentation, null if there is none.
*/
public Element getGetterForProperty(Element propertyMethod) {
return getterSetterMap.get(propertyMethod).getGetter();
}
/**
* Returns the setter documentation belonging to the given property method.
* @param propertyMethod the method for which the setter is needed.
* @return the setter documentation, null if there is none.
*/
public Element getSetterForProperty(Element propertyMethod) {
return getterSetterMap.get(propertyMethod).getSetter();
}
/**
* Return the package private members inherited by the class. Only return
* if parent is package private and not documented.
*
* @return the package private members inherited by the class.
*/
private List getInheritedPackagePrivateMethods() {
List results = new ArrayList<>();
for (TypeElement currentClass : visibleClasses) {
if (currentClass != typeElement &&
utils.isPackagePrivate(currentClass) &&
!utils.isLinkable(currentClass)) {
// Document these members in the child class because
// the parent is inaccessible.
results.addAll(classMap.get(currentClass).members);
}
}
return results;
}
/**
* Returns a list of visible enclosed members of the type being mapped.
* This list may also contain appended members, inherited by inaccessible
* super types. These members are documented in the subtype when the
* super type is not documented.
*
* @return a list of visible enclosed members
*/
public List getLeafMembers() {
List result = new ArrayList<>();
result.addAll(classMap.get(typeElement).members);
result.addAll(getInheritedPackagePrivateMethods());
return result;
}
/**
* Returns a list of enclosed members for the given type.
*
* @param typeElement the given type
*
* @return a list of enclosed members
*/
public List getMembers(TypeElement typeElement) {
return classMap.get(typeElement).members;
}
public boolean hasMembers(TypeElement typeElement) {
return !classMap.get(typeElement).members.isEmpty();
}
private void fillMemberLevelMap(List extends Element> list, String level) {
for (Element element : list) {
Object key = getMemberKey(element);
Map memberLevelMap = memberNameMap.get(key);
if (memberLevelMap == null) {
memberLevelMap = new HashMap<>();
memberNameMap.put(key, memberLevelMap);
}
memberLevelMap.put(element, level);
}
}
private void purgeMemberLevelMap(Iterable extends Element> list, String level) {
for (Element element : list) {
Object key = getMemberKey(element);
Map memberLevelMap = memberNameMap.get(key);
if (memberLevelMap != null && level.equals(memberLevelMap.get(element)))
memberLevelMap.remove(element);
}
}
/**
* Represents a class member.
*/
private class ClassMember {
private Set members;
public ClassMember(Element element) {
members = new HashSet<>();
members.add(element);
}
public boolean isEqual(ExecutableElement member) {
for (Element element : members) {
if (utils.executableMembersEqual(member, (ExecutableElement) element)) {
members.add(member);
return true;
}
}
return false;
}
}
/**
* A data structure that represents the class members for
* a visible class.
*/
private class ClassMembers {
/**
* The mapping class, whose inherited members are put in the
* {@link #members} list.
*/
private final TypeElement typeElement;
/**
* List of members from the mapping class.
*/
private List members = null;
/**
* Level/Depth of inheritance.
*/
private final String level;
private ClassMembers(TypeElement mappingClass, String level) {
this.typeElement = mappingClass;
this.level = level;
if (classMap.containsKey(mappingClass) &&
level.startsWith(classMap.get(mappingClass).level)) {
//Remove lower level class so that it can be replaced with
//same class found at higher level.
purgeMemberLevelMap(getClassMembers(mappingClass, false),
classMap.get(mappingClass).level);
classMap.remove(mappingClass);
visibleClasses.remove(mappingClass);
}
if (!classMap.containsKey(mappingClass)) {
classMap.put(mappingClass, this);
visibleClasses.add(mappingClass);
}
}
private void build() {
if (kind == Kind.CONSTRUCTORS) {
addMembers(typeElement);
} else {
mapClass();
}
}
private void mapClass() {
addMembers(typeElement);
List extends TypeMirror> interfaces = typeElement.getInterfaces();
for (TypeMirror anInterface : interfaces) {
String locallevel = level + 1;
ClassMembers cm = new ClassMembers(utils.asTypeElement(anInterface), locallevel);
cm.mapClass();
}
if (utils.isClass(typeElement)) {
TypeElement superclass = utils.getSuperClass(typeElement);
if (!(superclass == null || typeElement.equals(superclass))) {
ClassMembers cm = new ClassMembers(superclass, level + "c");
cm.mapClass();
}
}
}
/**
* Get all the valid members from the mapping class. Get the list of
* members for the class to be included into(ctii), also get the level
* string for ctii. If mapping class member is not already in the
* inherited member list and if it is visible in the ctii and not
* overridden, put such a member in the inherited member list.
* Adjust member-level-map, class-map.
*/
private void addMembers(TypeElement fromClass) {
List result = new ArrayList<>();
for (Element element : getClassMembers(fromClass, true)) {
if (memberIsVisible(element)) {
if (!isOverridden(element, level)) {
if (!utils.isHidden(element)) {
result.add(element);
}
}
}
}
if (members != null) {
throw new AssertionError("members should not be null");
}
members = Collections.unmodifiableList(result);
if (!members.isEmpty()) {
noVisibleMembers = false;
}
fillMemberLevelMap(getClassMembers(fromClass, false), level);
}
/**
* Is given element visible in given typeElement in terms of inheritance? The given element
* is visible in the given typeElement if it is public or protected and if it is
* package-private if it's containing class is in the same package as the given typeElement.
*/
private boolean memberIsVisible(Element element) {
if (utils.getEnclosingTypeElement(element).equals(VisibleMemberMap.this.typeElement)) {
//Member is in class that we are finding visible members for.
//Of course it is visible.
return true;
} else if (utils.isPrivate(element)) {
//Member is in super class or implemented interface.
//Private, so not inherited.
return false;
} else if (utils.isPackagePrivate(element)) {
//Member is package private. Only return true if its class is in
//same package.
return utils.containingPackage(element).equals(utils.containingPackage(VisibleMemberMap.this.typeElement));
} else {
//Public members are always inherited.
return true;
}
}
/**
* Return all available class members.
*/
private List extends Element> getClassMembers(TypeElement te, boolean filter) {
if (utils.isEnum(te) && kind == Kind.CONSTRUCTORS) {
//If any of these rules are hit, return empty array because
//we don't document these members ever.
return Collections.emptyList();
}
List extends Element> list;
switch (kind) {
case ANNOTATION_TYPE_FIELDS:
list = (filter)
? utils.getAnnotationFields(te)
: utils.getAnnotationFieldsUnfiltered(te);
break;
case ANNOTATION_TYPE_MEMBER_OPTIONAL:
list = utils.isAnnotationType(te)
? filterAnnotations(te, false)
: Collections.emptyList();
break;
case ANNOTATION_TYPE_MEMBER_REQUIRED:
list = utils.isAnnotationType(te)
? filterAnnotations(te, true)
: Collections.emptyList();
break;
case INNER_CLASSES:
List xlist = filter
? utils.getInnerClasses(te)
: utils.getInnerClassesUnfiltered(te);
list = new ArrayList<>(xlist);
break;
case ENUM_CONSTANTS:
list = utils.getEnumConstants(te);
break;
case FIELDS:
if (filter) {
list = utils.isAnnotationType(te)
? utils.getAnnotationFields(te)
: utils.getFields(te);
} else {
list = utils.isAnnotationType(te)
? utils.getAnnotationFieldsUnfiltered(te)
: utils.getFieldsUnfiltered(te);
}
break;
case CONSTRUCTORS:
list = utils.getConstructors(te);
break;
case METHODS:
list = filter ? utils.getMethods(te) : utils.getMethodsUnfiltered(te);
checkOnPropertiesTags(list);
break;
case PROPERTIES:
list = properties(te, filter);
break;
default:
list = Collections.emptyList();
}
// Deprected members should be excluded or not?
if (configuration.nodeprecated) {
return utils.excludeDeprecatedMembers(list);
}
return list;
}
/**
* Filter the annotation type members and return either the required
* members or the optional members, depending on the value of the
* required parameter.
*
* @param typeElement The annotation type to process.
* @param required
* @return the annotation type members and return either the required
* members or the optional members, depending on the value of the
* required parameter.
*/
private List filterAnnotations(TypeElement typeElement, boolean required) {
List members = utils.getAnnotationMethods(typeElement);
List targetMembers = new ArrayList<>();
for (Element member : members) {
ExecutableElement ee = (ExecutableElement)member;
if ((required && ee.getDefaultValue() == null)
|| ((!required) && ee.getDefaultValue() != null)) {
targetMembers.add(member);
}
}
return targetMembers;
}
/**
* Is member overridden? The member is overridden if it is found in the
* same level hierarchy e.g. member at level "11" overrides member at
* level "111".
*/
private boolean isOverridden(Element element, String level) {
Object key = getMemberKey(element);
Map, String> memberLevelMap = (Map, String>) memberNameMap.get(key);
if (memberLevelMap == null)
return false;
for (String mappedlevel : memberLevelMap.values()) {
if (mappedlevel.equals(STARTLEVEL)
|| (level.startsWith(mappedlevel)
&& !level.equals(mappedlevel))) {
return true;
}
}
return false;
}
private List properties(final TypeElement typeElement, final boolean filter) {
final List allMethods = filter
? utils.getMethods(typeElement)
: utils.getMethodsUnfiltered(typeElement);
final List allFields = utils.getFieldsUnfiltered(typeElement);
if (propertiesCache.containsKey(typeElement)) {
return propertiesCache.get(typeElement);
}
final List result = new ArrayList<>();
for (final Element propertyMethod : allMethods) {
ExecutableElement ee = (ExecutableElement)propertyMethod;
if (!isPropertyMethod(ee)) {
continue;
}
final ExecutableElement getter = getterForField(allMethods, ee);
final ExecutableElement setter = setterForField(allMethods, ee);
final VariableElement field = fieldForProperty(allFields, ee);
addToPropertiesMap(setter, getter, ee, field);
getterSetterMap.put(propertyMethod, new GetterSetter(getter, setter));
result.add(ee);
}
propertiesCache.put(typeElement, result);
return result;
}
private void addToPropertiesMap(ExecutableElement setter,
ExecutableElement getter,
ExecutableElement propertyMethod,
VariableElement field) {
if (field == null || utils.getDocCommentTree(field) == null) {
addToPropertiesMap(setter, propertyMethod);
addToPropertiesMap(getter, propertyMethod);
addToPropertiesMap(propertyMethod, propertyMethod);
} else {
addToPropertiesMap(getter, field);
addToPropertiesMap(setter, field);
addToPropertiesMap(propertyMethod, field);
}
}
private void addToPropertiesMap(Element propertyMethod,
Element commentSource) {
if (null == propertyMethod || null == commentSource) {
return;
}
DocCommentTree docTree = utils.getDocCommentTree(propertyMethod);
/* The second condition is required for the property buckets. In
* this case the comment is at the property method (not at the field)
* and it needs to be listed in the map.
*/
if ((docTree == null) || propertyMethod.equals(commentSource)) {
classPropertiesMap.put(propertyMethod, commentSource);
}
}
private ExecutableElement getterForField(List methods,
ExecutableElement propertyMethod) {
final String propertyMethodName = utils.getSimpleName(propertyMethod);
final String fieldName = propertyMethodName.substring(0,
propertyMethodName.lastIndexOf("Property"));
final String fieldNameUppercased =
"" + Character.toUpperCase(fieldName.charAt(0))
+ fieldName.substring(1);
final String getterNamePattern;
final String fieldTypeName = propertyMethod.getReturnType().toString();
if ("boolean".equals(fieldTypeName)
|| fieldTypeName.endsWith("BooleanProperty")) {
getterNamePattern = "(is|get)" + fieldNameUppercased;
} else {
getterNamePattern = "get" + fieldNameUppercased;
}
for (ExecutableElement method : methods) {
if (Pattern.matches(getterNamePattern, utils.getSimpleName(method))) {
if (method.getParameters().isEmpty() &&
utils.isPublic(method) || utils.isProtected(method)) {
return method;
}
}
}
return null;
}
private ExecutableElement setterForField(List methods,
ExecutableElement propertyMethod) {
final String propertyMethodName = utils.getSimpleName(propertyMethod);
final String fieldName =
propertyMethodName.substring(0,
propertyMethodName.lastIndexOf("Property"));
final String fieldNameUppercased =
"" + Character.toUpperCase(fieldName.charAt(0))
+ fieldName.substring(1);
final String setter = "set" + fieldNameUppercased;
for (ExecutableElement method : methods) {
if (setter.equals(utils.getSimpleName(method))) {
if (method.getParameters().size() == 1
&& method.getReturnType().getKind() == TypeKind.VOID
&& (utils.isPublic(method) || utils.isProtected(method))) {
return method;
}
}
}
return null;
}
private VariableElement fieldForProperty(List fields, ExecutableElement property) {
for (VariableElement field : fields) {
final String fieldName = utils.getSimpleName(field);
final String propertyName = fieldName + "Property";
if (propertyName.equals(utils.getSimpleName(property))) {
return field;
}
}
return null;
}
private boolean isPropertyMethod(ExecutableElement method) {
if (!configuration.javafx) {
return false;
}
if (!utils.getSimpleName(method).endsWith("Property")) {
return false;
}
if (!memberIsVisible(method)) {
return false;
}
if (GETTERSETTERPATTERN.matcher(utils.getSimpleName(method)).matches()) {
return false;
}
if (!method.getTypeParameters().isEmpty()) {
return false;
}
return method.getParameters().isEmpty()
&& method.getReturnType().getKind() != TypeKind.VOID;
}
private void checkOnPropertiesTags(List extends Element> members) {
for (Element e: members) {
ExecutableElement ee = (ExecutableElement)e;
if (utils.isIncluded(ee)) {
CommentHelper ch = utils.getCommentHelper(ee);
for (DocTree tree: utils.getBlockTags(ee)) {
String tagName = ch.getTagName(tree);
if (tagName.equals("@propertySetter")
|| tagName.equals("@propertyGetter")
|| tagName.equals("@propertyDescription")) {
if (!isPropertyGetterOrSetter(members, ee)) {
messages.warning(ch.getDocTreePath(tree),
"doclet.javafx_tag_misuse");
}
break;
}
}
}
}
}
private boolean isPropertyGetterOrSetter(List extends Element> members,
ExecutableElement method) {
String propertyName = utils.propertyName(method);
if (!propertyName.isEmpty()) {
String propertyMethodName = propertyName + "Property";
for (Element member: members) {
if (utils.getSimpleName(member).equals(propertyMethodName)) {
return true;
}
}
}
return false;
}
}
public class GetterSetter {
private final Element getter;
private final Element setter;
public GetterSetter(Element getter, Element setter) {
this.getter = getter;
this.setter = setter;
}
public Element getGetter() {
return getter;
}
public Element getSetter() {
return setter;
}
}
/**
* Return true if this map has no visible members.
*
* @return true if this map has no visible members.
*/
public boolean noVisibleMembers() {
return noVisibleMembers;
}
private ClassMember getClassMember(ExecutableElement member) {
for (Object key : memberNameMap.keySet()) {
if (key instanceof String) {
continue;
}
if (((ClassMember) key).isEqual(member)) {
return (ClassMember) key;
}
}
return new ClassMember(member);
}
/**
* Return the key to the member map for the given member.
*/
private Object getMemberKey(Element element) {
if (utils.isConstructor(element)) {
return utils.getSimpleName(element) + utils.flatSignature((ExecutableElement)element);
} else if (utils.isMethod(element)) {
return getClassMember((ExecutableElement) element);
} else if (utils.isField(element) || utils.isEnumConstant(element) || utils.isAnnotationType(element)) {
return utils.getSimpleName(element);
} else { // it's a class or interface
String classOrIntName = utils.getSimpleName(element);
//Strip off the containing class name because we only want the member name.
classOrIntName = classOrIntName.indexOf('.') != 0
? classOrIntName.substring(classOrIntName.lastIndexOf('.'))
: classOrIntName;
return "clint" + classOrIntName;
}
}
}