001    /*
002     * Copyright 1998-2008 Sun Microsystems, Inc.  All Rights Reserved.
003     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004     *
005     * This code is free software; you can redistribute it and/or modify it
006     * under the terms of the GNU General Public License version 2 only, as
007     * published by the Free Software Foundation.  Sun designates this
008     * particular file as subject to the "Classpath" exception as provided
009     * by Sun in the LICENSE file that accompanied this code.
010     *
011     * This code is distributed in the hope that it will be useful, but WITHOUT
012     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014     * version 2 for more details (a copy is included in the LICENSE file that
015     * accompanied this code).
016     *
017     * You should have received a copy of the GNU General Public License version
018     * 2 along with this work; if not, write to the Free Software Foundation,
019     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020     *
021     * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022     * CA 95054 USA or visit www.sun.com if you need additional information or
023     * have any questions.
024     */
025    
026    package com.sun.tools.doclets.internal.toolkit.util;
027    
028    import com.sun.tools.doclets.internal.toolkit.*;
029    import com.sun.javadoc.*;
030    import java.util.*;
031    
032    /**
033     * Build Class Hierarchy for all the Classes. This class builds the Class
034     * Tree and the Interface Tree separately.
035     *
036     * This code is not part of an API.
037     * It is implementation that is subject to change.
038     * Do not use it as an API
039     *
040     * @see java.util.HashMap
041     * @see java.util.List
042     * @see com.sun.javadoc.Type
043     * @see com.sun.javadoc.ClassDoc
044     * @author Atul M Dambalkar
045     */
046    public class ClassTree {
047    
048        /**
049         * List of baseclasses. Contains only java.lang.Object. Can be used to get
050         * the mapped listing of sub-classes.
051         */
052        private List<ClassDoc> baseclasses = new ArrayList<ClassDoc>();
053    
054        /**
055        * Mapping for each Class with their SubClasses
056        */
057        private Map<ClassDoc,List<ClassDoc>> subclasses = new HashMap<ClassDoc,List<ClassDoc>>();
058    
059        /**
060         * List of base-interfaces. Contains list of all the interfaces who do not
061         * have super-interfaces. Can be used to get the mapped listing of
062         * sub-interfaces.
063         */
064        private List<ClassDoc> baseinterfaces = new ArrayList<ClassDoc>();
065    
066        /**
067        * Mapping for each Interface with their SubInterfaces
068        */
069        private Map<ClassDoc,List<ClassDoc>> subinterfaces = new HashMap<ClassDoc,List<ClassDoc>>();
070    
071        private List<ClassDoc> baseEnums = new ArrayList<ClassDoc>();
072        private Map<ClassDoc,List<ClassDoc>> subEnums = new HashMap<ClassDoc,List<ClassDoc>>();
073    
074        private List<ClassDoc> baseAnnotationTypes = new ArrayList<ClassDoc>();
075        private Map<ClassDoc,List<ClassDoc>> subAnnotationTypes = new HashMap<ClassDoc,List<ClassDoc>>();
076    
077        /**
078        * Mapping for each Interface with classes who implement it.
079        */
080        private Map<ClassDoc,List<ClassDoc>> implementingclasses = new HashMap<ClassDoc,List<ClassDoc>>();
081    
082        /**
083         * Constructor. Build the Tree using the Root of this Javadoc run.
084         *
085         * @param configuration the configuration of the doclet.
086         * @param noDeprecated Don't add deprecated classes in the class tree, if
087         * true.
088         */
089        public ClassTree(Configuration configuration, boolean noDeprecated) {
090            configuration.message.notice("doclet.Building_Tree");
091            buildTree(configuration.root.classes(), configuration);
092        }
093    
094        /**
095         * Constructor. Build the Tree using the Root of this Javadoc run.
096         *
097         * @param root Root of the Document.
098         * @param configuration The curren configuration of the doclet.
099         */
100        public ClassTree(RootDoc root, Configuration configuration) {
101            buildTree(root.classes(), configuration);
102        }
103    
104        /**
105         * Constructor. Build the tree for the given array of classes.
106         *
107         * @param classes Array of classes.
108         * @param configuration The curren configuration of the doclet.
109         */
110        public ClassTree(ClassDoc[] classes, Configuration configuration) {
111            buildTree(classes, configuration);
112        }
113    
114        /**
115         * Generate mapping for the sub-classes for every class in this run.
116         * Return the sub-class list for java.lang.Object which will be having
117         * sub-class listing for itself and also for each sub-class itself will
118         * have their own sub-class lists.
119         *
120         * @param classes all the classes in this run.
121         * @param configuration the current configuration of the doclet.
122         */
123        private void buildTree(ClassDoc[] classes, Configuration configuration) {
124            for (int i = 0; i < classes.length; i++) {
125                if (configuration.nodeprecated &&
126                        classes[i].tags("deprecated").length > 0) {
127                    continue;
128                }
129                if (classes[i].isEnum()) {
130                    processType(classes[i], configuration, baseEnums, subEnums);
131                } else if (classes[i].isClass()) {
132                    processType(classes[i], configuration, baseclasses, subclasses);
133                } else if (classes[i].isInterface()) {
134                    processInterface(classes[i]);
135                    List<ClassDoc> list = implementingclasses.get(classes[i]);
136                    if (list != null) {
137                        Collections.sort(list);
138                    }
139                } else if (classes[i].isAnnotationType()) {
140                    processType(classes[i], configuration, baseAnnotationTypes,
141                        subAnnotationTypes);
142                }
143            }
144    
145            Collections.sort(baseinterfaces);
146            for (Iterator<List<ClassDoc>> it = subinterfaces.values().iterator(); it.hasNext(); ) {
147                Collections.sort(it.next());
148            }
149            for (Iterator<List<ClassDoc>> it = subclasses.values().iterator(); it.hasNext(); ) {
150                Collections.sort(it.next());
151            }
152        }
153    
154        /**
155         * For the class passed map it to it's own sub-class listing.
156         * For the Class passed, get the super class,
157         * if superclass is non null, (it is not "java.lang.Object")
158         *    get the "value" from the hashmap for this key Class
159         *    if entry not found create one and get that.
160         *    add this Class as a sub class in the list
161         *    Recurse till hits java.lang.Object Null SuperClass.
162         *
163         * @param cd class for which sub-class mapping to be generated.
164         * @param configuration the current configurtation of the doclet.
165         */
166        private void processType(ClassDoc cd, Configuration configuration,
167                List<ClassDoc> bases, Map<ClassDoc,List<ClassDoc>> subs) {
168            ClassDoc superclass = Util.getFirstVisibleSuperClassCD(cd, configuration);
169            if (superclass != null) {
170                if (!add(subs, superclass, cd)) {
171                    return;
172                } else {
173                    processType(superclass, configuration, bases, subs);
174                }
175            } else {     // cd is java.lang.Object, add it once to the list
176                if (!bases.contains(cd)) {
177                    bases.add(cd);
178                }
179            }
180            List<Type> intfacs = Util.getAllInterfaces(cd, configuration);
181            for (Iterator<Type> iter = intfacs.iterator(); iter.hasNext();) {
182                add(implementingclasses, iter.next().asClassDoc(), cd);
183            }
184        }
185    
186        /**
187         * For the interface passed get the interfaces which it extends, and then
188         * put this interface in the sub-interface list of those interfaces. Do it
189         * recursively. If a interface doesn't have super-interface just attach
190         * that interface in the list of all the baseinterfaces.
191         *
192         * @param cd Interface under consideration.
193         */
194        private void processInterface(ClassDoc cd) {
195            ClassDoc[] intfacs = cd.interfaces();
196            if (intfacs.length > 0) {
197                for (int i = 0; i < intfacs.length; i++) {
198                    if (!add(subinterfaces, intfacs[i], cd)) {
199                        return;
200                    } else {
201                        processInterface(intfacs[i]);   // Recurse
202                    }
203                }
204            } else {
205                // we need to add all the interfaces who do not have
206                // super-interfaces to baseinterfaces list to traverse them
207                if (!baseinterfaces.contains(cd)) {
208                    baseinterfaces.add(cd);
209                }
210            }
211        }
212    
213        /**
214         * Adjust the Class Tree. Add the class interface  in to it's super-class'
215         * or super-interface's sub-interface list.
216         *
217         * @param map the entire map.
218         * @param superclass java.lang.Object or the super-interface.
219         * @param cd sub-interface to be mapped.
220         * @returns boolean true if class added, false if class already processed.
221         */
222        private boolean add(Map<ClassDoc,List<ClassDoc>> map, ClassDoc superclass, ClassDoc cd) {
223            List<ClassDoc> list = map.get(superclass);
224            if (list == null) {
225                list = new ArrayList<ClassDoc>();
226                map.put(superclass, list);
227            }
228            if (list.contains(cd)) {
229                return false;
230            } else {
231                list.add(cd);
232            }
233            return true;
234        }
235    
236        /**
237         * From the map return the list of sub-classes or sub-interfaces. If list
238         * is null create a new one and return it.
239         *
240         * @param map The entire map.
241         * @param cd class for which the sub-class list is requested.
242         * @returns List Sub-Class list for the class passed.
243         */
244        private List<ClassDoc> get(Map<ClassDoc,List<ClassDoc>> map, ClassDoc cd) {
245            List<ClassDoc> list = map.get(cd);
246            if (list == null) {
247                return new ArrayList<ClassDoc>();
248            }
249            return list;
250        }
251    
252        /**
253         *  Return the sub-class list for the class passed.
254         *
255         * @param cd class whose sub-class list is required.
256         */
257        public List<ClassDoc> subclasses(ClassDoc cd) {
258            return get(subclasses, cd);
259        }
260    
261        /**
262         *  Return the sub-interface list for the interface passed.
263         *
264         * @param cd interface whose sub-interface list is required.
265         */
266        public List<ClassDoc> subinterfaces(ClassDoc cd) {
267            return get(subinterfaces, cd);
268        }
269    
270        /**
271         *  Return the list of classes which implement the interface passed.
272         *
273         * @param cd interface whose implementing-classes list is required.
274         */
275        public List<ClassDoc> implementingclasses(ClassDoc cd) {
276            List<ClassDoc> result = get(implementingclasses, cd);
277            List<ClassDoc> subinterfaces = allSubs(cd, false);
278    
279            //If class x implements a subinterface of cd, then it follows
280            //that class x implements cd.
281            Iterator<ClassDoc> implementingClassesIter, subInterfacesIter = subinterfaces.listIterator();
282            ClassDoc c;
283            while(subInterfacesIter.hasNext()){
284                implementingClassesIter = implementingclasses(
285                        subInterfacesIter.next()).listIterator();
286                while(implementingClassesIter.hasNext()){
287                    c = implementingClassesIter.next();
288                    if(! result.contains(c)){
289                        result.add(c);
290                    }
291                }
292            }
293            Collections.sort(result);
294            return result;
295        }
296    
297        /**
298         *  Return the sub-class/interface list for the class/interface passed.
299         *
300         * @param cd class/interface whose sub-class/interface list is required.
301         * @param isEnum true if the subclasses should be forced to come from the
302         * enum tree.
303         */
304        public List<ClassDoc> subs(ClassDoc cd, boolean isEnum) {
305            if (isEnum) {
306                return get(subEnums, cd);
307            } else if (cd.isAnnotationType()) {
308                return get(subAnnotationTypes, cd);
309            } else if (cd.isInterface()) {
310                return get(subinterfaces, cd);
311            } else if (cd.isClass()) {
312                return get(subclasses, cd);
313            } else {
314                return null;
315            }
316    
317        }
318    
319        /**
320         * Return a list of all direct or indirect, sub-classes and subinterfaces
321         * of the ClassDoc argument.
322         *
323         * @param cd ClassDoc whose sub-classes or sub-interfaces are requested.
324         * @param isEnum true if the subclasses should be forced to come from the
325         * enum tree.
326         */
327        public List<ClassDoc> allSubs(ClassDoc cd, boolean isEnum) {
328            List<ClassDoc> list = subs(cd, isEnum);
329            for (int i = 0; i < list.size(); i++) {
330                cd = list.get(i);
331                List<ClassDoc> tlist = subs(cd, isEnum);
332                for (int j = 0; j < tlist.size(); j++) {
333                    ClassDoc tcd = tlist.get(j);
334                    if (!list.contains(tcd)) {
335                        list.add(tcd);
336                    }
337                }
338            }
339            Collections.sort(list);
340            return list;
341        }
342    
343        /**
344         *  Return the base-classes list. This will have only one element namely
345         *  thw classdoc for java.lang.Object, since this is the base class for all
346         *  classes.
347         */
348        public List<ClassDoc> baseclasses() {
349            return baseclasses;
350        }
351    
352        /**
353         *  Return the list of base interfaces. This is the list of interfaces
354         *  which do not have super-interface.
355         */
356        public List<ClassDoc> baseinterfaces() {
357            return baseinterfaces;
358        }
359    
360        /**
361         *  Return the list of base enums. This is the list of enums
362         *  which do not have super-enums.
363         */
364        public List<ClassDoc> baseEnums() {
365            return baseEnums;
366        }
367    
368        /**
369         *  Return the list of base annotation types. This is the list of
370         *  annotation types which do not have super-annotation types.
371         */
372        public List<ClassDoc> baseAnnotationTypes() {
373            return baseAnnotationTypes;
374        }
375    }