1 /*
   2  * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.javadoc.internal.doclets.toolkit.util;
  27 
  28 
  29 import java.util.ArrayList;
  30 import java.util.Collection;
  31 import java.util.Collections;
  32 import java.util.Comparator;
  33 import java.util.HashMap;
  34 import java.util.Iterator;
  35 import java.util.List;
  36 import java.util.Map;
  37 import java.util.Set;
  38 import java.util.SortedSet;
  39 import java.util.TreeSet;
  40 
  41 import javax.lang.model.element.Element;
  42 import javax.lang.model.element.TypeElement;
  43 import javax.lang.model.type.TypeMirror;
  44 
  45 import jdk.javadoc.doclet.DocletEnvironment;
  46 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
  47 import jdk.javadoc.internal.doclets.toolkit.Messages;
  48 
  49 /**
  50  * Build Class Hierarchy for all the Classes. This class builds the Class
  51  * Tree and the Interface Tree separately.
  52  *
  53  *  <p><b>This is NOT part of any supported API.
  54  *  If you write code that depends on this, you do so at your own risk.
  55  *  This code and its internal interfaces are subject to change or
  56  *  deletion without notice.</b>
  57  *
  58  * @see java.util.HashMap
  59  * @see java.util.List
  60  */
  61 public class ClassTree {
  62 
  63     /**
  64      * List of base classes. Used to get the mapped listing of sub-classes.
  65      */
  66     private final SortedSet<TypeElement> baseClasses;
  67 
  68     /**
  69      * Mapping for each Class with their sub classes
  70      */
  71     private final Map<TypeElement, SortedSet<TypeElement>> subClasses = new HashMap<>();
  72 
  73     /**
  74      * List of base-interfaces. Contains set of all the interfaces who do not
  75      * have super-interfaces. Can be used to get the mapped listing of
  76      * sub-interfaces.
  77      */
  78     private final SortedSet<TypeElement> baseInterfaces;
  79 
  80    /**
  81     * Mapping for each Interface with their SubInterfaces
  82     */
  83     private final Map<TypeElement, SortedSet<TypeElement>> subInterfaces = new HashMap<>();
  84 
  85     private final SortedSet<TypeElement> baseEnums;
  86     private final Map<TypeElement, SortedSet<TypeElement>> subEnums = new HashMap<>();
  87 
  88     private final SortedSet<TypeElement> baseAnnotationTypes;
  89     private final Map<TypeElement, SortedSet<TypeElement>> subAnnotationTypes = new HashMap<>();
  90 
  91    /**
  92     * Mapping for each Interface with classes who implement it.
  93     */
  94     private final Map<TypeElement, SortedSet<TypeElement>> implementingClasses = new HashMap<>();
  95 
  96     private final BaseConfiguration configuration;
  97     private final Utils utils;
  98     private final Comparator<Element> comparator;
  99 
 100     /**
 101      * Constructor. Build the Tree using the Root of this Javadoc run.
 102      *
 103      * @param configuration the configuration of the doclet.
 104      * @param noDeprecated Don't add deprecated classes in the class tree, if
 105      * true.
 106      */
 107     public ClassTree(BaseConfiguration configuration, boolean noDeprecated) {
 108         this.configuration = configuration;
 109         this.utils = configuration.utils;
 110 
 111         Messages messages = configuration.getMessages();
 112         messages.notice("doclet.Building_Tree");
 113 
 114         comparator = utils.makeClassUseComparator();
 115         baseAnnotationTypes = new TreeSet<>(comparator);
 116         baseEnums = new TreeSet<>(comparator);
 117         baseClasses = new TreeSet<>(comparator);
 118         baseInterfaces = new TreeSet<>(comparator);
 119         buildTree(configuration.getIncludedTypeElements());
 120     }
 121 
 122     /**
 123      * Constructor. Build the Tree using the Root of this Javadoc run.
 124      *
 125      * @param docEnv the DocletEnvironment.
 126      * @param configuration The current configuration of the doclet.
 127      */
 128     public ClassTree(DocletEnvironment docEnv, BaseConfiguration configuration) {
 129         this.configuration = configuration;
 130         this.utils = configuration.utils;
 131         comparator = utils.makeClassUseComparator();
 132         baseAnnotationTypes = new TreeSet<>(comparator);
 133         baseEnums = new TreeSet<>(comparator);
 134         baseClasses = new TreeSet<>(comparator);
 135         baseInterfaces = new TreeSet<>(comparator);
 136         buildTree(configuration.getIncludedTypeElements());
 137     }
 138 
 139     /**
 140      * Constructor. Build the tree for the given array of classes.
 141      *
 142      * @param classesSet a set of classes
 143      * @param configuration The current configuration of the doclet.
 144      */
 145     public ClassTree(SortedSet<TypeElement>classesSet, BaseConfiguration configuration) {
 146         this.configuration = configuration;
 147         this.utils = configuration.utils;
 148         comparator = utils.makeClassUseComparator();
 149         baseAnnotationTypes = new TreeSet<>(comparator);
 150         baseEnums = new TreeSet<>(comparator);
 151         baseClasses = new TreeSet<>(comparator);
 152         baseInterfaces = new TreeSet<>(comparator);
 153         buildTree(classesSet);
 154     }
 155 
 156     /**
 157      * Generate mapping for the sub-classes for every class in this run.
 158      * Return the sub-class set for java.lang.Object which will be having
 159      * sub-class listing for itself and also for each sub-class itself will
 160      * have their own sub-class lists.
 161      *
 162      * @param classes all the classes in this run.
 163      */
 164     private void buildTree(Iterable<TypeElement> classes) {
 165         for (TypeElement aClass : classes) {
 166             // In the tree page (e.g overview-tree.html) do not include
 167             // information of classes which are deprecated or are a part of a
 168             // deprecated package.
 169             if (configuration.getOptions().noDeprecated &&
 170                     (utils.isDeprecated(aClass) ||
 171                     utils.isDeprecated(utils.containingPackage(aClass)))) {
 172                 continue;
 173             }
 174 
 175             if (utils.hasHiddenTag(aClass)) {
 176                 continue;
 177             }
 178 
 179             if (utils.isEnum(aClass)) {
 180                 processType(aClass, configuration, baseEnums, subEnums);
 181             } else if (utils.isClass(aClass)) {
 182                 processType(aClass, configuration, baseClasses, subClasses);
 183             } else if (utils.isInterface(aClass)) {
 184                 processInterface(aClass);
 185             } else if (utils.isAnnotationType(aClass)) {
 186                 processType(aClass, configuration, baseAnnotationTypes,
 187                     subAnnotationTypes);
 188             }
 189         }
 190     }
 191 
 192     /**
 193      * For the class passed map it to its own sub-class listing.
 194      * For the Class passed, get the super class,
 195      * if superclass is non null, (it is not "java.lang.Object")
 196      * get the "value" from the hashmap for this key Class
 197      * if entry not found create one and get that.
 198      * add this Class as a sub class in the set
 199      * Recurse till hits java.lang.Object Null SuperClass.
 200      *
 201      * @param typeElement for which sub class mapping is to be generated.
 202      * @param configuration the current configuration of the doclet.
 203      */
 204     private void processType(TypeElement typeElement, BaseConfiguration configuration,
 205             Collection<TypeElement> bases, Map<TypeElement, SortedSet<TypeElement>> subs) {
 206         TypeElement superclass = utils.getFirstVisibleSuperClassAsTypeElement(typeElement);
 207         if (superclass != null) {
 208             if (!add(subs, superclass, typeElement)) {
 209                 return;
 210             } else {
 211                 processType(superclass, configuration, bases, subs);
 212             }
 213         } else {     // typeElement is java.lang.Object, add it once to the set
 214             if (!bases.contains(typeElement)) {
 215                 bases.add(typeElement);
 216             }
 217         }
 218         Set<TypeMirror> intfacs = utils.getAllInterfaces(typeElement);
 219         for (TypeMirror intfac : intfacs) {
 220             add(implementingClasses, utils.asTypeElement(intfac), typeElement);
 221         }
 222     }
 223 
 224     /**
 225      * For the interface passed get the interfaces which it extends, and then
 226      * put this interface in the sub-interface set of those interfaces. Do it
 227      * recursively. If a interface doesn't have super-interface just attach
 228      * that interface in the set of all the baseInterfaces.
 229      *
 230      * @param typeElement Interface under consideration.
 231      */
 232     private void processInterface(TypeElement typeElement) {
 233         List<? extends TypeMirror> intfacs = typeElement.getInterfaces();
 234         if (!intfacs.isEmpty()) {
 235             for (TypeMirror intfac : intfacs) {
 236                 if (!add(subInterfaces, utils.asTypeElement(intfac), typeElement)) {
 237                     return;
 238                 } else {
 239                     processInterface(utils.asTypeElement(intfac));   // Recurse
 240                 }
 241             }
 242         } else {
 243             // we need to add all the interfaces who do not have
 244             // super-interfaces to baseInterfaces set to traverse them
 245             if (!baseInterfaces.contains(typeElement)) {
 246                 baseInterfaces.add(typeElement);
 247             }
 248         }
 249     }
 250 
 251     /**
 252      * Adjust the Class Tree. Add the class interface  in to it's super classes
 253      * or super interface's sub-interface set.
 254      *
 255      * @param map the entire map.
 256      * @param superclass java.lang.Object or the super-interface.
 257      * @param typeElement sub-interface to be mapped.
 258      * @returns boolean true if class added, false if class already processed.
 259      */
 260     private boolean add(Map<TypeElement, SortedSet<TypeElement>> map, TypeElement superclass, TypeElement typeElement) {
 261         SortedSet<TypeElement> sset = map.computeIfAbsent(superclass, s ->  new TreeSet<>(comparator));
 262         if (sset.contains(typeElement)) {
 263             return false;
 264         } else {
 265             sset.add(typeElement);
 266         }
 267         return true;
 268     }
 269 
 270     /**
 271      * From the map return the set of sub-classes or sub-interfaces. If set
 272      * is null create a new one and return it.
 273      *
 274      * @param map The entire map.
 275      * @param typeElement class for which the sub-class set is requested.
 276      * @returns a list of sub classes.
 277      */
 278     private SortedSet<TypeElement> get(Map<TypeElement, SortedSet<TypeElement>> map, TypeElement typeElement) {
 279         return map.computeIfAbsent(typeElement, t ->  new TreeSet<>(comparator));
 280     }
 281 
 282     /**
 283      *  Return the sub-class set for the class passed.
 284      *
 285      * @param typeElement class whose sub-class set is required.
 286      */
 287     public SortedSet<TypeElement> subClasses(TypeElement typeElement) {
 288         return get(subClasses, typeElement);
 289     }
 290 
 291     /**
 292      *  Return the sub-interface set for the interface passed.
 293      *
 294      * @param typeElement interface whose sub-interface set is required.
 295      */
 296     public SortedSet<TypeElement> subInterfaces(TypeElement typeElement) {
 297         return get(subInterfaces, typeElement);
 298     }
 299 
 300     /**
 301      *  Return the set of classes which implement the interface passed.
 302      *
 303      * @param typeElement interface whose implementing-classes set is required.
 304      */
 305     public SortedSet<TypeElement> implementingClasses(TypeElement typeElement) {
 306         SortedSet<TypeElement> result = get(implementingClasses, typeElement);
 307         SortedSet<TypeElement> intfcs = allSubClasses(typeElement, false);
 308 
 309         // If class x implements a subinterface of typeElement, then it follows
 310         // that class x implements typeElement.
 311         Iterator<TypeElement> subInterfacesIter = intfcs.iterator();
 312         while (subInterfacesIter.hasNext()) {
 313             Iterator<TypeElement> implementingClassesIter
 314                     = implementingClasses(subInterfacesIter.next()).iterator();
 315             while (implementingClassesIter.hasNext()) {
 316                 TypeElement c = implementingClassesIter.next();
 317                 if (!result.contains(c)) {
 318                     result.add(c);
 319                 }
 320             }
 321         }
 322         return result;
 323     }
 324 
 325     /**
 326      *  Return the sub-class/interface set for the class/interface passed.
 327      *
 328      * @param typeElement class/interface whose sub-class/interface set is required.
 329      * @param isEnum true if the subClasses should be forced to come from the
 330      * enum tree.
 331      */
 332     public SortedSet<TypeElement> directSubClasses(TypeElement typeElement, boolean isEnum) {
 333         return directSubClasses0(typeElement, isEnum);
 334     }
 335 
 336     private SortedSet<TypeElement> directSubClasses0(TypeElement typeElement, boolean isEnum) {
 337         if (isEnum) {
 338             return get(subEnums, typeElement);
 339         } else if (utils.isAnnotationType(typeElement)) {
 340             return get(subAnnotationTypes, typeElement);
 341         } else if (utils.isInterface(typeElement)) {
 342             return get(subInterfaces, typeElement);
 343         } else if (utils.isClass(typeElement)) {
 344             return get(subClasses, typeElement);
 345         } else {
 346             return Collections.emptySortedSet();
 347         }
 348     }
 349 
 350     /**
 351      * Return a set of all direct or indirect, sub-classes and subInterfaces
 352      * of the TypeElement argument.
 353      *
 354      * @param typeElement TypeElement whose sub-classes or sub-interfaces are requested.
 355      * @param isEnum true if the subClasses should be forced to come from the
 356      * enum tree.
 357      */
 358     public SortedSet<TypeElement> allSubClasses(TypeElement typeElement, boolean isEnum) {
 359         // new entries added to the set are searched as well, this is
 360         // really a work queue.
 361         List<TypeElement> list = new ArrayList<>(directSubClasses(typeElement, isEnum));
 362         for (int i = 0; i < list.size(); i++) {
 363             TypeElement te = list.get(i);
 364             SortedSet<TypeElement> tset = directSubClasses0(te, isEnum);
 365             for (TypeElement tte : tset) {
 366                 if (!list.contains(tte)) {
 367                     list.add(tte);
 368                 }
 369             }
 370         }
 371         SortedSet<TypeElement> out = new TreeSet<>(comparator);
 372         out.addAll(list);
 373         return out;
 374     }
 375 
 376     /**
 377      *  Return a set of base classes. This will have only one element namely
 378      *  the TypeElement for java.lang.Object, since this is the base class for all
 379      *  classes.
 380      */
 381     public SortedSet<TypeElement> baseClasses() {
 382         return baseClasses;
 383     }
 384 
 385     /**
 386      *  Return the set of base interfaces. This is the set of interfaces
 387      * which do not have super-interface.
 388      */
 389     public SortedSet<TypeElement> baseInterfaces() {
 390         return baseInterfaces;
 391     }
 392 
 393     /**
 394      *  Return the set of base enums. This is the set of enums
 395      *  which do not have super-enums.
 396      */
 397     public SortedSet<TypeElement> baseEnums() {
 398         return baseEnums;
 399     }
 400 
 401     /**
 402      * Return the set of base annotation types. This is the set
 403      * of annotation types which do not have super-annotation types.
 404      */
 405     public SortedSet<TypeElement> baseAnnotationTypes() {
 406         return baseAnnotationTypes;
 407     }
 408 }