FileDocCategorySizeDatePackage
ClassUseMapper.javaAPI DocExample9153Wed Apr 19 11:17:14 BST 2000com.sun.tools.doclets.standard

ClassUseMapper.java

/*
 * @(#)ClassUseMapper.java	1.4 00/02/02
 *
 * Copyright 1998-2000 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * This software is the proprietary information of Sun Microsystems, Inc.  
 * Use is subject to license terms.
 * 
 */

package com.sun.tools.doclets.standard;

import com.sun.javadoc.*;
import com.sun.tools.doclets.ClassTree;
import com.sun.tools.doclets.DocletAbortException;
import java.util.*;

/**
 * @since JDK1.2
 * @author Robert G. Field
 */
public class ClassUseMapper {

    private final ClassTree classtree;

    /**
     * Mapping of ClassDocs to set of PackageDoc used by that class.
     * Entries may be null.
     */
    public Map classToPackage = new HashMap();

    /**
     * Mapping of ClassDocs to set of ClassDoc used by that class.
     * Entries may be null.
     */
    public Map classToClass = new HashMap();

    /**
     * Mapping of ClassDocs to list of ClassDoc which are direct or 
     * indirect subclasses of that class.
     * Entries may be null.
     */
    public Map classToSubclass = new HashMap();

    /**
     * Mapping of ClassDocs to list of ClassDoc which are direct or 
     * indirect subinterfaces of that interface.
     * Entries may be null.
     */
    public Map classToSubinterface = new HashMap();

    /**
     * Mapping of ClassDocs to list of ClassDoc which implement
     * this interface.
     * Entries may be null.
     */
    public Map classToImplementingClass = new HashMap();

    /**
     * Mapping of ClassDocs to list of FieldDoc declared as that class.
     * Entries may be null.
     */
    public Map classToField = new HashMap();

    /**
     * Mapping of ClassDocs to list of MethodDoc returning that class.
     * Entries may be null.
     */
    public Map classToMethodReturn = new HashMap();

    /**
     * Mapping of ClassDocs to list of MethodDoc having that class
     * as an arg.
     * Entries may be null.
     */
    public Map classToMethodArgs = new HashMap();

    /**
     * Mapping of ClassDocs to list of MethodDoc which throws that class.
     * Entries may be null.
     */
    public Map classToMethodThrows = new HashMap();

    /**
     * Mapping of ClassDocs to list of ConstructorDoc having that class
     * as an arg.
     * Entries may be null.
     */
    public Map classToConstructorArgs = new HashMap();

    /**
     * Mapping of ClassDocs to list of ConstructorDoc which throws that class.
     * Entries may be null.
     */
    public Map classToConstructorThrows = new HashMap();


    /**
     * Write out class use pages.
     */
    public static void generate(RootDoc root, 
                       ClassTree classtree) throws DocletAbortException {
        ClassUseMapper mapper = new ClassUseMapper(root, classtree);
        ClassDoc[] classes = root.classes();
        for (int i = 0; i < classes.length; i++) {
            ClassUseWriter.generate(mapper, classes[i]);
        }
        PackageDoc[] pkgs = Standard.configuration().packages;
        for (int i = 0; i < pkgs.length; i++) {
            PackageUseWriter.generate(mapper, pkgs[i]);
        }
    }

    private ClassUseMapper(RootDoc root, ClassTree classtree) {
        this.classtree = classtree;

        // Map subclassing, subinterfacing implementing, ...
        for (Iterator it = classtree.baseclasses().iterator(); it.hasNext();) {
	    subclasses((ClassDoc)it.next());
	}
        for (Iterator it = classtree.baseinterfaces().iterator(); it.hasNext();) {
	    // does subinterfacing as side-effect
	    implementingClasses((ClassDoc)it.next());
	}
	// Map methods, fields, constructors using a class.
        ClassDoc[] classes = root.classes();
        for (int i = 0; i < classes.length; i++) {
            ClassDoc cd = classes[i];
            FieldDoc[] fields = cd.fields();
            for (int j = 0; j < fields.length; j++) {
                FieldDoc fd = fields[j];
                ClassDoc tcd = fd.type().asClassDoc();
                if (tcd != null) {
                    add(classToField, tcd, fd);
                }
            }
            ConstructorDoc[] cons = cd.constructors();
            for (int j = 0; j < cons.length; j++) {
                mapExecutable(cons[j]);
            }
            MethodDoc[] meths = cd.methods();
            for (int j = 0; j < meths.length; j++) {
                MethodDoc md = meths[j];
                mapExecutable(md);
                ClassDoc tcd = md.returnType().asClassDoc();
                if (tcd != null) {
                    add(classToMethodReturn, tcd, md);
                }
            }
        }
    }

    /**
     * Return all subclasses of a class AND fill-in classToSubclass map.
     */
    private Collection subclasses(ClassDoc cd) {
        Collection ret = (Collection)classToSubclass.get(cd);
	if (ret == null) {
	    ret = new TreeSet();
	    List subs = classtree.subclasses(cd);
	    if (subs != null) {
	        ret.addAll(subs);
		for (Iterator it = subs.iterator(); it.hasNext();) {
		    ret.addAll(subclasses((ClassDoc)it.next()));
		}
	    }
	    addAll(classToSubclass, cd, ret);
	}
	return ret;
    }
	    
    /**
     * Return all subinterfaces of an interface AND fill-in classToSubinterface map.
     */
    private Collection subinterfaces(ClassDoc cd) {
        Collection ret = (Collection)classToSubinterface.get(cd);
	if (ret == null) {
	    ret = new TreeSet();
	    List subs = classtree.subinterfaces(cd);
	    if (subs != null) {
	        ret.addAll(subs);
		for (Iterator it = subs.iterator(); it.hasNext();) {
		    ret.addAll(subinterfaces((ClassDoc)it.next()));
		}
	    }
	    addAll(classToSubinterface, cd, ret);
	}
	return ret;
    }
	    
    /**
     * Return all implementing classes of an interface (including
     * all subclasses of implementing classes and all classes 
     * implementing subinterfaces) AND fill-in both classToImplementingClass 
     * and classToSubinterface maps.
     */
    private Collection implementingClasses(ClassDoc cd) {
        Collection ret = (List)classToImplementingClass.get(cd);
	if (ret == null) {
	    ret = new TreeSet();
	    List impl = classtree.implementingclasses(cd);
	    if (impl != null) {
	        ret.addAll(impl);
	        for (Iterator it = impl.iterator(); it.hasNext();) {
		    ret.addAll(subclasses((ClassDoc)it.next()));
		}
	    }
	    for (Iterator it = subinterfaces(cd).iterator(); it.hasNext();) {
	        ret.addAll(implementingClasses((ClassDoc)it.next()));
	    }
	    addAll(classToImplementingClass, cd, ret);
	}
	return ret;
    }
	    
    /**
     * Determine classes used by a method or constructor, so they can be
     * inverse mapped.
     */
    private void mapExecutable(ExecutableMemberDoc em) {
        Parameter[] params = em.parameters();
        boolean isConstructor = em.isConstructor();
        List classArgs = new ArrayList();
        for (int k = 0; k < params.length; k++) {
            ClassDoc pcd = params[k].type().asClassDoc();
            // primitives don't get mapped, also avoid dups
            if (pcd != null && !classArgs.contains(pcd)) {
                add(isConstructor? classToConstructorArgs :classToMethodArgs,
                    pcd, em);
                classArgs.add(pcd);
            }
        }
        ClassDoc[] thr = em.thrownExceptions();
        for (int k = 0; k < thr.length; k++) {
            add(isConstructor? classToConstructorThrows : classToMethodThrows, 
                thr[k], em);
        }        
    }

    private List refList(Map map, ClassDoc cd) {
        List list = (List)map.get(cd);
        if (list == null) {
            list = new ArrayList();
            map.put(cd, list);
        }
        return list;
    }

    private Set packageSet(ClassDoc cd) {
	Set pkgSet = (Set)classToPackage.get(cd);
        if (pkgSet == null) {
            pkgSet = new TreeSet();
            classToPackage.put(cd, pkgSet);
        }
        return pkgSet;
    }

    private Set classSet(ClassDoc cd) {
	Set clsSet = (Set)classToClass.get(cd);
        if (clsSet == null) {
            clsSet = new TreeSet();
            classToClass.put(cd, clsSet);
        }
        return clsSet;
    }

    private void add(Map map, ClassDoc cd, ProgramElementDoc ref) {
 	// add to specified map
        refList(map, cd).add(ref);

	// add ref's package to package map and class map
	packageSet(cd).add(ref.containingPackage());
        classSet(cd).add(ref instanceof MemberDoc? 
                         ((MemberDoc)ref).containingClass() :
                         ref);
    }

    private void addAll(Map map, ClassDoc cd, Collection refs) {
        if (refs == null) {
	    return;
	}
	// add to specified map
        refList(map, cd).addAll(refs);

	Set pkgSet = packageSet(cd);
	Set clsSet = classSet(cd);
	// add ref's package to package map and class map
	for (Iterator it = refs.iterator(); it.hasNext();) {
            ProgramElementDoc pedoc = (ProgramElementDoc)it.next();
	    pkgSet.add(pedoc.containingPackage());
            clsSet.add(pedoc instanceof MemberDoc? 
                       ((MemberDoc)pedoc).containingClass() :
                       pedoc);
	}
    }
}