/* -*- Mode: JDE; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * This file is part of the debugger and core tools for the AspectJ(tm) * programming language; see http://aspectj.org * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * either http://www.mozilla.org/MPL/ or http://aspectj.org/MPL/. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is AspectJ. * * The Initial Developer of the Original Code is Xerox Corporation. Portions * created by Xerox Corporation are Copyright (C) 1999-2002 Xerox Corporation. * All Rights Reserved. */ package org.aspectj.tools.ajdoc; import org.aspectj.compiler.base.ast.TypeDec; import org.aspectj.compiler.base.ast.World; import com.sun.javadoc.ClassDoc; import com.sun.javadoc.PackageDoc; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * This is responsible for constituting the world * of specified[classes|packages] and all classes. * It ensures that any classes compiled are included (if appropriate) * but does not ensure that linked classes are. */ public class RootDocImpl extends DocImpl implements org.aspectj.ajdoc.RootDoc, Quietable { /** The collection of packages specified to be documented. */ private final Set specifiedPackages; /** The collection of types specified to be documented. */ private final Set specifiedClasses; /** The collection of packages visible in this world. */ private final Set packages = new HashSet(); /** The collection of classes visible in this world. */ private final Set classes = new HashSet(); /** The documentation options. */ private final String[][] options; /** The World delegate. */ private final World world; /** Determines whether items are included */ private final AccessChecker filter; public RootDocImpl(World world, String[][] options, Collection pkgnames, Collection classnames, AccessChecker filter) { this.world = world; this.options = options; this.filter = (null != filter ? filter : AccessChecker.PUBLIC); Set set = createSpecifiedPackages(pkgnames); specifiedPackages = set; // modifiable to prune empty packages set = createSpecifiedClasses(classnames); specifiedClasses = Collections.unmodifiableSet(set); // adds all world classes and packages for classes and packages // addWorldTypes(); // todo re-enable as needed // make sure specified are added - should duplicate world // but should come after since packages are removed if empty addSpecifiedPackages(); addSpecifiedClasses(); setupDominatesRelations(); ensureWorldInclusion(); } /* ------------------------------------------------------------ * Implementation of RootDoc * ------------------------------------------------------------ */ /** * Returns the classes visible in this world. * * @return an array of ClassDoc representing the visible * classes in this world. */ public ClassDoc[] classes() { return (ClassDoc[])classes.toArray (new org.aspectj.ajdoc.ClassDoc[classes.size()]); } /** * Returns a type visible in this world * for the name className. If there is * no visible package, this method will return * null. * * @return an instance of ClassDoc in this world * that corresponds to className. * null is returned if there exists * no such visible type named className. */ public ClassDoc classNamed(String className) { ClassDoc[] docs = classes(); for (int i = 0; i < docs.length; i++) { ClassDoc doc = docs[i]; if (doc.name().equals(className)) { return doc; } } return null; } /** * Returns a package visible in this world * for the name packageName. If there is * no visible package, this method will return * null. * * @return an instance of PackageDoc in this world * that corresponds to packageName. * null is returned if there exists * no such visible package named packageName. */ public PackageDoc packageNamed(String packageName) { for (Iterator i = packages.iterator(); i.hasNext();) { PackageDoc doc = (PackageDoc)i.next(); if (doc.name().equals(packageName)) { return doc; } } return null; } /** * Returns the underlying world. * * @return an instance of World representing all * the CompilationUnits. */ public World world() { return world; } /** * Returns the documentation options. * * @return the documentation options. */ public String[][] options() { return options; } /** * Returns the types specified to be documented. * * @return an array of ClassDoc representing the * specified types. */ public ClassDoc[] specifiedClasses() { return (ClassDoc[])specifiedClasses.toArray (new org.aspectj.ajdoc.ClassDoc[specifiedClasses.size()]); } /** * Returns the packages specified to be documented. * * @return an array of PackageDoc representing the * specified packages. */ public PackageDoc[] specifiedPackages() { return (PackageDoc[])specifiedPackages.toArray (new org.aspectj.ajdoc.PackageDoc[specifiedPackages.size()]); } /* ------------------------------------------------------------ * Implementation of Quietable * ------------------------------------------------------------ */ /** true when notices should be printed. */ private boolean notice = true; /** Supresses output notices. */ public void quiet() { notice = false; } /** Allows output notices. */ public void speak() { notice = true; } /* ------------------------------------------------------------ * Implementation of DocErrReporter * ------------------------------------------------------------ */ /** * Prints the error message msg using * the current error handler. * * @param msg the error message. */ public void printError(String msg) { err().printError(msg); } /** * Prints the notice message msg using * the current error handler. * * @param msg the notice message. */ public void printNotice(String msg) { if (notice) err().printNotice(msg); } /** * Prints the warning message msg using * the current error handler. * * @param msg the warning message. */ public void printWarning(String msg) { err().printWarning(msg); } /* ------------------------------------------------------------ * Implementation of Doc * ------------------------------------------------------------ */ /** * Returns null. * * @return null. */ public String name() { return "who knows???"; } /* ------------------------------------------------------------ * Helper methods * ------------------------------------------------------------ */ /** * Creates only PackageDocs that were included on the command * line, even if they are empty. Should be used only for * specifiedPackages. */ private HashSet createSpecifiedPackages(Collection pkgnames) { HashSet result = new HashSet(); for (Iterator i = pkgnames.iterator(); i.hasNext();) { String pkgname = (String)i.next(); PackageDocImpl pkgdoc = PackageDocImpl.getPackageDoc(pkgname); pkgdoc.setIncluded(true); result.add(pkgdoc); } return result; } private void addWorldTypes() { for (Iterator i = world.getTypes().iterator(); i.hasNext();) { TypeDec td = (TypeDec)i.next(); ClassDocImpl cd = ClassDocImpl.getInstance(td); addClass(cd); cd.setIncluded(filter.canAccess(td)); } } /** * Creates only ClassDocs that were included on the command * line, and then only if they pass the filter. * Should be used only for specifiedClasses. * todo: createClasses uses to use all classes if no names */ private HashSet createSpecifiedClasses(Collection classnames) { HashSet result = new HashSet(); if (classnames != null) { for (Iterator i = classnames.iterator(); i.hasNext();) { String classname = (String)i.next(); for (Iterator j = world.getTypes().iterator(); j.hasNext();) { TypeDec td = (TypeDec)j.next(); if (filter.canAccess(td)) { ClassDoc cd = ClassDocImpl.getInstance(td); if (cd.qualifiedName().equals(classname)) { result.add(cd); // add inner classes since not specified explicitly ClassDoc[] inners = cd.innerClasses(); // no cycles, right? if (null != inners) { for (int l = 0; l < inners.length; l++) { result.add(inners[l]); } } break; } } } // todo: warn if class specified but not in world? } } return result; } private void addSpecifiedClasses() { for (Iterator i = new ArrayList(specifiedClasses).iterator(); i.hasNext();) { ClassDoc cd = (ClassDoc)i.next(); addClass(cd); } } private void addSpecifiedPackages() { for (Iterator i = new ArrayList(specifiedPackages).iterator(); i.hasNext();) { PackageDoc pd = (PackageDoc)i.next(); ClassDoc[] allClasses = pd.allClasses(); if (allClasses.length == 0) { specifiedPackages.remove(pd); } else { for (int j = 0; j < allClasses.length; j++) { addClass(allClasses[j]); } } } } /** * If filter accepts this ClassDoc, * Add it and and inner classes to classes * and add package to packages. */ private void addClass(ClassDoc cd) { if (null == cd) return; ClassDocImpl impl = (ClassDocImpl) cd; if (filter.canAccess(impl.typeDec()) && (!classes.contains(impl))) { impl.setIncluded(true); classes.add(impl); packages.add(impl.containingPackage()); ClassDoc[] inners = impl.innerClasses(); for (int i = 0; i < inners.length; i++) { addClass(inners[i]); } } // todo: flag classes not added? } /** Read all classes to find any dominates relations */ private void setupDominatesRelations() { // Find just the aspects List aspects = new ArrayList(); ClassDoc[] classes = classes(); for (int i = 0; i < classes.length; i++) { ClassDocImpl cd = (ClassDocImpl)classes[i]; if (cd.isAspect()) { aspects.add(cd); } } // Iterate over the aspects, if for (Iterator i = aspects.iterator(); i.hasNext();) { AspectDocImpl aspect1 = (AspectDocImpl)i.next(); for (Iterator j = aspects.iterator(); j.hasNext();) { AspectDocImpl aspect2 = (AspectDocImpl)j.next(); if (aspect1.dominates(aspect2)) { aspect1.addDominatee(aspect2); aspect2.addDominator(aspect1); } } } } /** * Ensure compiled classes are included if they pass the filter * and excluded otherwise. * todo: The set of types available includes the world plus reachable * types from there; I would like to exclude the reachable ones, * but do not know how. */ private void ensureWorldInclusion() { for (Iterator i = world.getTypes().iterator(); i.hasNext();) { TypeDec td = (TypeDec)i.next(); ClassDocImpl cd = ClassDocImpl.getInstance(td); boolean isIncluded = cd.isIncluded(); // todo: update to consider enclosing class privileges boolean shouldInclude = filter.canAccess(td); if (shouldInclude != isIncluded) { cd.setIncluded(shouldInclude); } } } }