blob: 9a1a54906cd4eb62c729d426e40f3ee0adfb4fda [file] [log] [blame]
/*
* Copyright (c) 1998, 2018, 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import jdk.javadoc.doclet.DocletEnvironment;
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
import jdk.javadoc.internal.doclets.toolkit.Messages;
/**
* Build Class Hierarchy for all the Classes. This class builds the Class
* Tree and the Interface Tree separately.
*
* <p><b>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.</b>
*
* @see java.util.HashMap
* @see java.util.List
* @author Atul M Dambalkar
*/
public class ClassTree {
/**
* List of base classes. Used to get the mapped listing of sub-classes.
*/
private final SortedSet<TypeElement> baseClasses;
/**
* Mapping for each Class with their sub classes
*/
private final Map<TypeElement, SortedSet<TypeElement>> subClasses = new HashMap<>();
/**
* List of base-interfaces. Contains set of all the interfaces who do not
* have super-interfaces. Can be used to get the mapped listing of
* sub-interfaces.
*/
private final SortedSet<TypeElement> baseInterfaces;
/**
* Mapping for each Interface with their SubInterfaces
*/
private final Map<TypeElement, SortedSet<TypeElement>> subInterfaces = new HashMap<>();
private final SortedSet<TypeElement> baseEnums;
private final Map<TypeElement, SortedSet<TypeElement>> subEnums = new HashMap<>();
private final SortedSet<TypeElement> baseAnnotationTypes;
private final Map<TypeElement, SortedSet<TypeElement>> subAnnotationTypes = new HashMap<>();
/**
* Mapping for each Interface with classes who implement it.
*/
private final Map<TypeElement, SortedSet<TypeElement>> implementingClasses = new HashMap<>();
private final BaseConfiguration configuration;
private final Utils utils;
private final Comparator<Element> comparator;
/**
* Constructor. Build the Tree using the Root of this Javadoc run.
*
* @param configuration the configuration of the doclet.
* @param noDeprecated Don't add deprecated classes in the class tree, if
* true.
*/
public ClassTree(BaseConfiguration configuration, boolean noDeprecated) {
this.configuration = configuration;
this.utils = configuration.utils;
Messages messages = configuration.getMessages();
messages.notice("doclet.Building_Tree");
comparator = utils.makeClassUseComparator();
baseAnnotationTypes = new TreeSet<>(comparator);
baseEnums = new TreeSet<>(comparator);
baseClasses = new TreeSet<>(comparator);
baseInterfaces = new TreeSet<>(comparator);
buildTree(configuration.getIncludedTypeElements());
}
/**
* Constructor. Build the Tree using the Root of this Javadoc run.
*
* @param docEnv the DocletEnvironment.
* @param configuration The current configuration of the doclet.
*/
public ClassTree(DocletEnvironment docEnv, BaseConfiguration configuration) {
this.configuration = configuration;
this.utils = configuration.utils;
comparator = utils.makeClassUseComparator();
baseAnnotationTypes = new TreeSet<>(comparator);
baseEnums = new TreeSet<>(comparator);
baseClasses = new TreeSet<>(comparator);
baseInterfaces = new TreeSet<>(comparator);
buildTree(configuration.getIncludedTypeElements());
}
/**
* Constructor. Build the tree for the given array of classes.
*
* @param classesSet a set of classes
* @param configuration The current configuration of the doclet.
*/
public ClassTree(SortedSet<TypeElement>classesSet, BaseConfiguration configuration) {
this.configuration = configuration;
this.utils = configuration.utils;
comparator = utils.makeClassUseComparator();
baseAnnotationTypes = new TreeSet<>(comparator);
baseEnums = new TreeSet<>(comparator);
baseClasses = new TreeSet<>(comparator);
baseInterfaces = new TreeSet<>(comparator);
buildTree(classesSet);
}
/**
* Generate mapping for the sub-classes for every class in this run.
* Return the sub-class set for java.lang.Object which will be having
* sub-class listing for itself and also for each sub-class itself will
* have their own sub-class lists.
*
* @param classes all the classes in this run.
*/
private void buildTree(Iterable<TypeElement> classes) {
for (TypeElement aClass : classes) {
// In the tree page (e.g overview-tree.html) do not include
// information of classes which are deprecated or are a part of a
// deprecated package.
if (configuration.nodeprecated &&
(utils.isDeprecated(aClass) ||
utils.isDeprecated(utils.containingPackage(aClass)))) {
continue;
}
if (utils.hasHiddenTag(aClass)) {
continue;
}
if (utils.isEnum(aClass)) {
processType(aClass, configuration, baseEnums, subEnums);
} else if (utils.isClass(aClass)) {
processType(aClass, configuration, baseClasses, subClasses);
} else if (utils.isInterface(aClass)) {
processInterface(aClass);
} else if (utils.isAnnotationType(aClass)) {
processType(aClass, configuration, baseAnnotationTypes,
subAnnotationTypes);
}
}
}
/**
* For the class passed map it to its own sub-class listing.
* For the Class passed, get the super class,
* if superclass is non null, (it is not "java.lang.Object")
* get the "value" from the hashmap for this key Class
* if entry not found create one and get that.
* add this Class as a sub class in the set
* Recurse till hits java.lang.Object Null SuperClass.
*
* @param typeElement for which sub class mapping is to be generated.
* @param configuration the current configuration of the doclet.
*/
private void processType(TypeElement typeElement, BaseConfiguration configuration,
Collection<TypeElement> bases, Map<TypeElement, SortedSet<TypeElement>> subs) {
TypeElement superclass = utils.getFirstVisibleSuperClassAsTypeElement(typeElement);
if (superclass != null) {
if (!add(subs, superclass, typeElement)) {
return;
} else {
processType(superclass, configuration, bases, subs);
}
} else { // typeElement is java.lang.Object, add it once to the set
if (!bases.contains(typeElement)) {
bases.add(typeElement);
}
}
Set<TypeMirror> intfacs = utils.getAllInterfaces(typeElement);
for (TypeMirror intfac : intfacs) {
add(implementingClasses, utils.asTypeElement(intfac), typeElement);
}
}
/**
* For the interface passed get the interfaces which it extends, and then
* put this interface in the sub-interface set of those interfaces. Do it
* recursively. If a interface doesn't have super-interface just attach
* that interface in the set of all the baseInterfaces.
*
* @param typeElement Interface under consideration.
*/
private void processInterface(TypeElement typeElement) {
List<? extends TypeMirror> intfacs = typeElement.getInterfaces();
if (!intfacs.isEmpty()) {
for (TypeMirror intfac : intfacs) {
if (!add(subInterfaces, utils.asTypeElement(intfac), typeElement)) {
return;
} else {
processInterface(utils.asTypeElement(intfac)); // Recurse
}
}
} else {
// we need to add all the interfaces who do not have
// super-interfaces to baseInterfaces set to traverse them
if (!baseInterfaces.contains(typeElement)) {
baseInterfaces.add(typeElement);
}
}
}
/**
* Adjust the Class Tree. Add the class interface in to it's super classes
* or super interface's sub-interface set.
*
* @param map the entire map.
* @param superclass java.lang.Object or the super-interface.
* @param typeElement sub-interface to be mapped.
* @returns boolean true if class added, false if class already processed.
*/
private boolean add(Map<TypeElement, SortedSet<TypeElement>> map, TypeElement superclass, TypeElement typeElement) {
SortedSet<TypeElement> sset = map.computeIfAbsent(superclass, s -> new TreeSet<>(comparator));
if (sset.contains(typeElement)) {
return false;
} else {
sset.add(typeElement);
}
return true;
}
/**
* From the map return the set of sub-classes or sub-interfaces. If set
* is null create a new one and return it.
*
* @param map The entire map.
* @param typeElement class for which the sub-class set is requested.
* @returns a list of sub classes.
*/
private SortedSet<TypeElement> get(Map<TypeElement, SortedSet<TypeElement>> map, TypeElement typeElement) {
return map.computeIfAbsent(typeElement, t -> new TreeSet<>(comparator));
}
/**
* Return the sub-class set for the class passed.
*
* @param typeElement class whose sub-class set is required.
*/
public SortedSet<TypeElement> subClasses(TypeElement typeElement) {
return get(subClasses, typeElement);
}
/**
* Return the sub-interface set for the interface passed.
*
* @param typeElement interface whose sub-interface set is required.
*/
public SortedSet<TypeElement> subInterfaces(TypeElement typeElement) {
return get(subInterfaces, typeElement);
}
/**
* Return the set of classes which implement the interface passed.
*
* @param typeElement interface whose implementing-classes set is required.
*/
public SortedSet<TypeElement> implementingClasses(TypeElement typeElement) {
SortedSet<TypeElement> result = get(implementingClasses, typeElement);
SortedSet<TypeElement> intfcs = allSubClasses(typeElement, false);
// If class x implements a subinterface of typeElement, then it follows
// that class x implements typeElement.
Iterator<TypeElement> subInterfacesIter = intfcs.iterator();
while (subInterfacesIter.hasNext()) {
Iterator<TypeElement> implementingClassesIter
= implementingClasses(subInterfacesIter.next()).iterator();
while (implementingClassesIter.hasNext()) {
TypeElement c = implementingClassesIter.next();
if (!result.contains(c)) {
result.add(c);
}
}
}
return result;
}
/**
* Return the sub-class/interface set for the class/interface passed.
*
* @param typeElement class/interface whose sub-class/interface set is required.
* @param isEnum true if the subClasses should be forced to come from the
* enum tree.
*/
public SortedSet<TypeElement> directSubClasses(TypeElement typeElement, boolean isEnum) {
return directSubClasses0(typeElement, isEnum);
}
private SortedSet<TypeElement> directSubClasses0(TypeElement typeElement, boolean isEnum) {
if (isEnum) {
return get(subEnums, typeElement);
} else if (utils.isAnnotationType(typeElement)) {
return get(subAnnotationTypes, typeElement);
} else if (utils.isInterface(typeElement)) {
return get(subInterfaces, typeElement);
} else if (utils.isClass(typeElement)) {
return get(subClasses, typeElement);
} else {
return Collections.emptySortedSet();
}
}
/**
* Return a set of all direct or indirect, sub-classes and subInterfaces
* of the TypeElement argument.
*
* @param typeElement TypeElement whose sub-classes or sub-interfaces are requested.
* @param isEnum true if the subClasses should be forced to come from the
* enum tree.
*/
public SortedSet<TypeElement> allSubClasses(TypeElement typeElement, boolean isEnum) {
// new entries added to the set are searched as well, this is
// really a work queue.
List<TypeElement> list = new ArrayList<>(directSubClasses(typeElement, isEnum));
for (int i = 0; i < list.size(); i++) {
TypeElement te = list.get(i);
SortedSet<TypeElement> tset = directSubClasses0(te, isEnum);
for (TypeElement tte : tset) {
if (!list.contains(tte)) {
list.add(tte);
}
}
}
SortedSet<TypeElement> out = new TreeSet<>(comparator);
out.addAll(list);
return out;
}
/**
* Return a set of base classes. This will have only one element namely
* the TypeElement for java.lang.Object, since this is the base class for all
* classes.
*/
public SortedSet<TypeElement> baseClasses() {
return baseClasses;
}
/**
* Return the set of base interfaces. This is the set of interfaces
* which do not have super-interface.
*/
public SortedSet<TypeElement> baseInterfaces() {
return baseInterfaces;
}
/**
* Return the set of base enums. This is the set of enums
* which do not have super-enums.
*/
public SortedSet<TypeElement> baseEnums() {
return baseEnums;
}
/**
* Return the set of base annotation types. This is the set
* of annotation types which do not have super-annotation types.
*/
public SortedSet<TypeElement> baseAnnotationTypes() {
return baseAnnotationTypes;
}
}