package jdepend.textui; import java.io.*; import java.util.*; import java.text.NumberFormat; import jdepend.framework.JavaClass; import jdepend.framework.JavaPackage; import jdepend.framework.PackageComparator; import jdepend.framework.PackageFilter; /** * The <code>JDepend</code> class analyzes directories of Java class files, * generates metrics for each Java package, and reports the metrics in a textual * format. * * @author <b>Mike Clark</b> * @author Clarkware Consulting, Inc. */ public class JDepend { private jdepend.framework.JDepend analyzer; private PrintWriter writer; protected NumberFormat formatter; /** * Constructs a <code>JDepend</code> instance using standard output. */ public JDepend() { this(new PrintWriter(System.out)); } /** * Constructs a <code>JDepend</code> instance with the specified writer. * * @param writer Writer. */ public JDepend(PrintWriter writer) { analyzer = new jdepend.framework.JDepend(); formatter = NumberFormat.getInstance(); formatter.setMaximumFractionDigits(2); setWriter(writer); } /** * Sets the output writer. * * @param writer Output writer. */ public void setWriter(PrintWriter writer) { this.writer = writer; } protected PrintWriter getWriter() { return writer; } /** * Sets the package filter. * * @param filter Package filter. */ public void setFilter(PackageFilter filter) { analyzer.setFilter(filter); } /** * Sets the comma-separated list of components. */ public void setComponents(String components) { analyzer.setComponents(components); } /** * Adds the specified directory name to the collection of directories to be * analyzed. * * @param name Directory name. * @throws IOException If the directory does not exist. */ public void addDirectory(String name) throws IOException { analyzer.addDirectory(name); } /** * Determines whether inner classes are analyzed. * * @param b <code>true</code> to analyze inner classes; <code>false</code> * otherwise. */ public void analyzeInnerClasses(boolean b) { analyzer.analyzeInnerClasses(b); } /** * Analyzes the registered directories, generates metrics for each Java * package, and reports the metrics. */ public void analyze() { printHeader(); Collection packages = analyzer.analyze(); ArrayList packageList = new ArrayList(packages); Collections.sort(packageList, new PackageComparator(PackageComparator .byName())); printPackages(packageList); printCycles(packageList); printSummary(packageList); printFooter(); getWriter().flush(); } protected void printPackages(Collection packages) { printPackagesHeader(); Iterator i = packages.iterator(); while (i.hasNext()) { printPackage((JavaPackage) i.next()); } printPackagesFooter(); } protected void printPackage(JavaPackage jPackage) { printPackageHeader(jPackage); if (jPackage.getClasses().size() == 0) { printNoStats(); printPackageFooter(jPackage); return; } printStatistics(jPackage); printSectionBreak(); printAbstractClasses(jPackage); printSectionBreak(); printConcreteClasses(jPackage); printSectionBreak(); printEfferents(jPackage); printSectionBreak(); printAfferents(jPackage); printPackageFooter(jPackage); } protected void printAbstractClasses(JavaPackage jPackage) { printAbstractClassesHeader(); ArrayList members = new ArrayList(jPackage.getClasses()); Collections.sort(members, new JavaClass.ClassComparator()); Iterator memberIter = members.iterator(); while (memberIter.hasNext()) { JavaClass jClass = (JavaClass) memberIter.next(); if (jClass.isAbstract()) { printClassName(jClass); } } printAbstractClassesFooter(); } protected void printConcreteClasses(JavaPackage jPackage) { printConcreteClassesHeader(); ArrayList members = new ArrayList(jPackage.getClasses()); Collections.sort(members, new JavaClass.ClassComparator()); Iterator memberIter = members.iterator(); while (memberIter.hasNext()) { JavaClass concrete = (JavaClass) memberIter.next(); if (!concrete.isAbstract()) { printClassName(concrete); } } printConcreteClassesFooter(); } protected void printEfferents(JavaPackage jPackage) { printEfferentsHeader(); ArrayList efferents = new ArrayList(jPackage.getEfferents()); Collections.sort(efferents, new PackageComparator(PackageComparator .byName())); Iterator efferentIter = efferents.iterator(); while (efferentIter.hasNext()) { JavaPackage efferent = (JavaPackage) efferentIter.next(); printPackageName(efferent); } if (efferents.size() == 0) { printEfferentsError(); } printEfferentsFooter(); } protected void printAfferents(JavaPackage jPackage) { printAfferentsHeader(); ArrayList afferents = new ArrayList(jPackage.getAfferents()); Collections.sort(afferents, new PackageComparator(PackageComparator .byName())); Iterator afferentIter = afferents.iterator(); while (afferentIter.hasNext()) { JavaPackage afferent = (JavaPackage) afferentIter.next(); printPackageName(afferent); } if (afferents.size() == 0) { printAfferentsError(); } printAfferentsFooter(); } protected void printCycles(Collection packages) { printCyclesHeader(); Iterator i = packages.iterator(); while (i.hasNext()) { printCycle((JavaPackage) i.next()); } printCyclesFooter(); } protected void printCycle(JavaPackage jPackage) { List list = new ArrayList(); jPackage.collectCycle(list); if (!jPackage.containsCycle()) { return; } JavaPackage cyclePackage = (JavaPackage) list.get(list.size() - 1); String cyclePackageName = cyclePackage.getName(); int i = 0; Iterator pkgIter = list.iterator(); while (pkgIter.hasNext()) { i++; JavaPackage pkg = (JavaPackage) pkgIter.next(); if (i == 1) { printCycleHeader(pkg); } else { if (pkg.getName().equals(cyclePackageName)) { printCycleTarget(pkg); } else { printCycleContributor(pkg); } } } printCycleFooter(); } protected void printHeader() { // do nothing } protected void printFooter() { // do nothing } protected void printPackagesHeader() { // do nothing } protected void printPackagesFooter() { // do nothing } protected void printNoStats() { getWriter().println( "No stats available: package referenced, but not analyzed."); } protected void printPackageHeader(JavaPackage jPackage) { getWriter().println( "\n--------------------------------------------------"); getWriter().println("- Package: " + jPackage.getName()); getWriter().println( "--------------------------------------------------"); } protected void printPackageFooter(JavaPackage jPackage) { // do nothing } protected void printStatistics(JavaPackage jPackage) { getWriter().println("\nStats:"); getWriter().println( tab() + "Total Classes: " + jPackage.getClassCount()); getWriter() .println( tab() + "Concrete Classes: " + jPackage.getConcreteClassCount()); getWriter() .println( tab() + "Abstract Classes: " + jPackage.getAbstractClassCount()); getWriter().println(""); getWriter().println(tab() + "Ca: " + jPackage.afferentCoupling()); getWriter().println(tab() + "Ce: " + jPackage.efferentCoupling()); getWriter().println(""); getWriter().println( tab() + "A: " + toFormattedString(jPackage.abstractness())); getWriter().println( tab() + "I: " + toFormattedString(jPackage.instability())); getWriter().println( tab() + "D: " + toFormattedString(jPackage.distance())); } protected void printClassName(JavaClass jClass) { getWriter().println(tab() + jClass.getName()); } protected void printPackageName(JavaPackage jPackage) { getWriter().println(tab() + jPackage.getName()); } protected void printAbstractClassesHeader() { getWriter().println("Abstract Classes:"); } protected void printAbstractClassesFooter() { // do nothing } protected void printConcreteClassesHeader() { getWriter().println("Concrete Classes:"); } protected void printConcreteClassesFooter() { // do nothing } protected void printEfferentsHeader() { getWriter().println("Depends Upon:"); } protected void printEfferentsFooter() { // do nothing } protected void printEfferentsError() { getWriter().println(tab() + "Not dependent on any packages."); } protected void printAfferentsHeader() { getWriter().println("Used By:"); } protected void printAfferentsFooter() { // do nothing } protected void printAfferentsError() { getWriter().println(tab() + "Not used by any packages."); } protected void printCyclesHeader() { printSectionBreak(); getWriter().println( "\n--------------------------------------------------"); getWriter().println("- Package Dependency Cycles:"); getWriter().println( "--------------------------------------------------\n"); } protected void printCyclesFooter() { // do nothing } protected void printCycleHeader(JavaPackage jPackage) { getWriter().println(jPackage.getName()); getWriter().println(tab() + "|"); } protected void printCycleTarget(JavaPackage jPackage) { getWriter().println(tab() + "|-> " + jPackage.getName()); } protected void printCycleContributor(JavaPackage jPackage) { getWriter().println(tab() + "| " + jPackage.getName()); } protected void printCycleFooter() { printSectionBreak(); } protected void printSummary(Collection packages) { getWriter().println( "\n--------------------------------------------------"); getWriter().println("- Summary:"); getWriter().println( "--------------------------------------------------\n"); getWriter() .println( "Name, Class Count, Abstract Class Count, Ca, Ce, A, I, D, V:\n"); Iterator i = packages.iterator(); while (i.hasNext()) { JavaPackage jPackage = (JavaPackage) i.next(); getWriter().print(jPackage.getName() + ","); getWriter().print(jPackage.getClassCount() + ","); getWriter().print(jPackage.getAbstractClassCount() + ","); getWriter().print(jPackage.afferentCoupling() + ","); getWriter().print(jPackage.efferentCoupling() + ","); getWriter().print(toFormattedString(jPackage.abstractness()) + ","); getWriter().print(toFormattedString(jPackage.instability()) + ","); getWriter().print(toFormattedString(jPackage.distance()) + ","); getWriter().println(jPackage.getVolatility()); } } protected void printSectionBreak() { getWriter().println(""); } protected String toFormattedString(float f) { return formatter.format(f); } protected String tab() { return " "; } protected String tab(int n) { StringBuffer s = new StringBuffer(); for (int i = 0; i < n; i++) { s.append(tab()); } return s.toString(); } protected void usage(String message) { if (message != null) { System.err.println("\n" + message); } String baseUsage = "\nJDepend "; System.err.println(""); System.err.println("usage: "); System.err.println(baseUsage + "[-components <components>]" + " [-file <output file>] <directory> " + "[directory2 [directory 3] ...]"); System.exit(1); } protected void instanceMain(String[] args) { if (args.length < 1) { usage("Must specify at least one directory."); } int directoryCount = 0; for (int i = 0; i < args.length; i++) { if (args[i].startsWith("-")) { if (args[i].equalsIgnoreCase("-file")) { if (args.length <= i + 1) { usage("Output file name not specified."); } try { setWriter(new PrintWriter(new OutputStreamWriter( new FileOutputStream(args[++i]), "UTF8"))); } catch (IOException ioe) { usage(ioe.getMessage()); } } else if (args[i].equalsIgnoreCase("-components")) { if (args.length <= i + 1) { usage("Components not specified."); } setComponents(args[++i]); } else { usage("Invalid argument: " + args[i]); } } else { try { addDirectory(args[i]); directoryCount++; } catch (IOException ioe) { usage("Directory does not exist: " + args[i]); } } } if (directoryCount == 0) { usage("Must specify at least one directory."); } analyze(); } public static void main(String args[]) { new JDepend().instanceMain(args); } }