From dcac1a4b30dde8d77f672ddc9f3759ff2a5cc2a0 Mon Sep 17 00:00:00 2001 From: "Richard O. Legendi" Date: Wed, 16 Mar 2011 23:30:47 +0100 Subject: [PATCH 1/4] Updated project config files for easyer project checkout. Modified settings: * Added default JUnit library * Created default .gitignore for the bin folder --- .classpath | 9 +++++---- .gitignore | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 .gitignore diff --git a/.classpath b/.classpath index c4a5e07..e72ef7c 100644 --- a/.classpath +++ b/.classpath @@ -1,7 +1,8 @@ - - - - + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bin From a95b5260da3b8b2e2214815ea32e1817291c2ea8 Mon Sep 17 00:00:00 2001 From: "Richard O. Legendi" Date: Wed, 16 Mar 2011 23:45:28 +0100 Subject: [PATCH 2/4] Enhanced debugging mode. Debugging is now can be adjusted with a "-Djdepend.debug=true" switch from the command line directly (without having to recompile the code). --- src/jdepend/framework/AbstractParser.java | 3 +-- src/jdepend/framework/ClassFileParser.java | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/jdepend/framework/AbstractParser.java b/src/jdepend/framework/AbstractParser.java index 17849d4..f621fd6 100755 --- a/src/jdepend/framework/AbstractParser.java +++ b/src/jdepend/framework/AbstractParser.java @@ -16,8 +16,7 @@ public abstract class AbstractParser { private ArrayList parseListeners; private PackageFilter filter; - public static boolean DEBUG = false; - + public static final boolean DEBUG = Boolean.parseBoolean( System.getProperty( "jdepend.debug", "false" ) ); public AbstractParser() { this(new PackageFilter()); diff --git a/src/jdepend/framework/ClassFileParser.java b/src/jdepend/framework/ClassFileParser.java index 93af36f..13b14a8 100755 --- a/src/jdepend/framework/ClassFileParser.java +++ b/src/jdepend/framework/ClassFileParser.java @@ -745,8 +745,6 @@ public String toString() { public static void main(String args[]) { try { - ClassFileParser.DEBUG = true; - if (args.length <= 0) { System.err.println("usage: ClassFileParser "); System.exit(0); From 5b255ff0c29027fb63c723a373a075ff83396232 Mon Sep 17 00:00:00 2001 From: "Richard O. Legendi" Date: Thu, 17 Mar 2011 01:20:52 +0100 Subject: [PATCH 3/4] Added basic HTML output format. One of the features I was missing when Yesterday I tried this tool out. Now it is implemented with minor features and basic style, but may be expended easily by updating the generated CSS file. First I thougth source attachment as hyperlinks would be cool, but I realized this is not always a feasible goal (see my TODO comment in the code). Hope you will be able to use this code for something. --- src/jdepend/htmlui/JDepend.java | 217 ++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 src/jdepend/htmlui/JDepend.java diff --git a/src/jdepend/htmlui/JDepend.java b/src/jdepend/htmlui/JDepend.java new file mode 100644 index 0000000..9814ad9 --- /dev/null +++ b/src/jdepend/htmlui/JDepend.java @@ -0,0 +1,217 @@ +package jdepend.htmlui; + +import java.io.*; +import java.util.*; + +import java.text.NumberFormat; + +import jdepend.framework.JavaClass; +import jdepend.framework.JavaPackage; + +/** + * The JDepend class analyzes directories of Java class files, generates metrics for each Java package, and + * reports the metrics in a HTML format. + * + * @author Richard Oliver Legendi (rlegendi) + */ +public class JDepend + extends jdepend.textui.JDepend { + + /** + * Constructs a JDepend instance using standard output. + */ + public JDepend() { + this( new PrintWriter( System.out ) ); + } + + /** + * Constructs a JDepend instance with the specified writer. + * + * @param writer Writer. + */ + public JDepend(final PrintWriter writer) { + super( writer ); + + formatter = NumberFormat.getInstance( Locale.ENGLISH ); + formatter.setMaximumFractionDigits( 2 ); + } + + // TODO This definitely needs some update. If there're more source folders (e.g. src, test), it may + // be impossible to locate them correctly. + private String findJavaClassLocation(final JavaClass javaClass) { + return "src" + File.separatorChar + javaClass.getName().replace( '.', File.separatorChar ); + } + + protected void printHeader() { + // TODO Some prettier CSS would be helpful + getWriter().println( "" ); + getWriter().println( "" ); + getWriter().println( "" ); + getWriter().println( "" ); + getWriter().println( "" ); + getWriter().println( "JDepend Analysis Report" ); + getWriter().println( "" ); + getWriter().println( "" ); + getWriter().println( "" ); + getWriter().println( "

JDepend Analysis Report

" ); + getWriter().println( "

Designed to use JDepend with Ant.

" ); + } + + protected void printFooter() { + getWriter().println( "" ); + getWriter().println( "" ); + } + + protected void printPackagesHeader() { + getWriter().println( "

Packages

" ); + } + + protected void printPackagesFooter() { + ; + } + + protected void printPackageHeader(final JavaPackage jPackage) { + printSectionBreak(); + getWriter().println( "

Package " + jPackage.getName() + "

" ); + } + + protected void printPackageFooter(final JavaPackage jPackage) { + getWriter().println( "
" ); + } + + protected void printNoStats() { + getWriter().println( tab() + "

No stats available: package referenced, but not analyzed.

" ); // CHECKME + } + + protected void printStatistics(final JavaPackage jPackage) { + getWriter().println( tab( 1 ) + "

Statistics

" ); + getWriter().println( tab( 1 ) + "
    " ); + getWriter().println( tab( 2 ) + "
  • Total Number of Classes: " + jPackage.getClassCount() + "
  • " ); + getWriter().println( tab( 2 ) + "
  • Concrete Classes:" + jPackage.getConcreteClassCount() + "
  • " ); + getWriter().println( tab( 2 ) + "
  • Abstract Classes:" + jPackage.getAbstractClassCount() + "
  • " ); + getWriter().println( tab( 2 ) + "
  • Ca:" + jPackage.afferentCoupling() + "
  • " ); + getWriter().println( tab( 2 ) + "
  • Ce:" + jPackage.efferentCoupling() + "
  • " ); + getWriter().println( tab( 2 ) + "
  • A:" + toFormattedString( jPackage.abstractness() ) + "
  • " ); + getWriter().println( tab( 2 ) + "
  • I:" + toFormattedString( jPackage.instability() ) + "
  • " ); + getWriter().println( tab( 2 ) + "
  • D:" + toFormattedString( jPackage.distance() ) + "
  • " ); + getWriter().println( tab( 2 ) + "
  • V:" + jPackage.getVolatility() + "
  • " ); + getWriter().println( tab( 1 ) + "
" ); + } + + protected void printClassName(final JavaClass jClass) { + getWriter().println( "Class " + jClass.getName() + "
" ); + } + + protected void printPackageName(final JavaPackage jPackage) { + getWriter().println( "Package " + jPackage.getName() + "
" ); + } + + protected void printAbstractClassesHeader() { + getWriter().println( "

AbstractClasses

" ); + } + + protected void printAbstractClassesFooter() { + ; + } + + protected void printConcreteClassesHeader() { + getWriter().println( "

ConcreteClasses

" ); + } + + protected void printConcreteClassesFooter() { + ; + } + + protected void printEfferentsHeader() { + getWriter().println( "

DependsUpon

" ); + } + + protected void printEfferentsFooter() { + ; + } + + protected void printEfferentsError() { + getWriter().println( tab() + "

Efferents not available.

" ); + } + + protected void printAfferentsHeader() { + getWriter().println( "

UsedBy

" ); + } + + protected void printAfferentsFooter() { + ; + } + + protected void printAfferentsError() { + getWriter().println( tab() + "

Afferents not available.

" ); + } + + protected void printCyclesHeader() { + printSectionBreak(); + getWriter().println( tab() + "

Package Dependency Cycles

" ); + } + + protected void printCyclesFooter() { + getWriter().println( "
" ); + } + + protected void printCycleHeader(final JavaPackage jPackage) { + getWriter().println( "

Package " + jPackage.getName() + "

" ); + getWriter().println( "
    " ); + } + + protected void printCycleFooter() { + getWriter().println( "
" ); + printSectionBreak(); + } + + protected void printCycleTarget(final JavaPackage jPackage) { + printCycleContributor( jPackage ); + } + + protected void printCycleContributor(final JavaPackage jPackage) { + getWriter().println( "
  • " + jPackage.getName() + "
  • " ); + } + + @SuppressWarnings("rawtypes") + protected void printSummary(final Collection packages) { + getWriter().println( "

    Summary

    " ); + getWriter().println( "" ); + getWriter().println( "" ); + getWriter().println( "" ); + getWriter().println( "" ); + + final Iterator i = packages.iterator(); + while ( i.hasNext() ) { + final JavaPackage jPackage = (JavaPackage) i.next(); + + getWriter().println( "" ); + getWriter().print( "" ); + getWriter().print( "" ); + getWriter().print( "" ); + getWriter().print( "" ); + getWriter().print( "" ); + getWriter().print( "" ); + getWriter().print( "" ); + getWriter().print( "" ); + getWriter().println( "" ); + getWriter().println( "" ); + } + + getWriter().println( "
    Name Class Count Abstract Class Count Ca Ce A I D V
    " + jPackage.getName() + "" + jPackage.getClassCount() + "" + jPackage.getAbstractClassCount() + "" + jPackage.afferentCoupling() + "" + jPackage.efferentCoupling() + "" + toFormattedString( jPackage.abstractness() ) + "" + toFormattedString( jPackage.instability() ) + "" + toFormattedString( jPackage.distance() ) + "" + jPackage.getVolatility() + "
    " ); + } + + /** + * Main function, which is a delegate itself to {@link JDepend#instanceMain(String[])}. + * + * @param args passed directly to the instanceMain() method + */ + public static void main(final String args[]) { + new JDepend().instanceMain( args ); + } + +} From 15e045ad50f71bdfff1c3541319843abae69d792 Mon Sep 17 00:00:00 2001 From: "Richard O. Legendi" Date: Thu, 17 Mar 2011 15:19:45 +0100 Subject: [PATCH 4/4] Added styled HTML output generation. The HTML generation now may include templates files direclty. Two templates were supported as an example: one for the CSS and a simple HTML for the general information in the header about the various metrics. It would be really nice to update the sample CSS I made to the one used at the official site at: http://www.clarkware.com/software/JDepend.html --- .classpath | 1 + res/info.html | 65 ++++++++++++++++++++ res/main.css | 62 +++++++++++++++++++ src/jdepend/htmlui/JDepend.java | 103 ++++++++++++++++++++++++-------- 4 files changed, 207 insertions(+), 24 deletions(-) create mode 100644 res/info.html create mode 100644 res/main.css diff --git a/.classpath b/.classpath index e72ef7c..3875dff 100644 --- a/.classpath +++ b/.classpath @@ -1,6 +1,7 @@ + diff --git a/res/info.html b/res/info.html new file mode 100644 index 0000000..cd977e3 --- /dev/null +++ b/res/info.html @@ -0,0 +1,65 @@ +The JDepend utility +analyzes directories of Java class files and generates the following metrics for +each Java package. + +

    +

      +
    • Afferent Coupling (Ca) +

      +The number of packages that depend upon the classes within the analyzed +package. +

      +
    • +
    • Efferent Coupling (Ce) +

      +The number of packages that the classes in the analyzed package depend upon. +

      +
    • +
    • Abstractness (A) +

      +The ratio of the number of abstract classes (and interfaces) in the analyzed +package to the total number of classes in the analyzed package. +

      +

      +The range for this metric is 0 to 1, with A=0 indicating a completely +concrete package and A=1 indicating a completely abstract package. +

      +
    • +
    • Instability (I) +

      +The ratio of efferent coupling (Ce) to total coupling (Ce + Ca) such that I = +Ce / (Ce + Ca). +

      +

      +The range for this metric is 0 to 1, with I=0 indicating a completely stable +package and I=1 indicating a completely instable package. +

      +
    • +
    • Distance from the Main Sequence (D) +

      +The perpendicular distance of a package from the idealized line A + I = 1. A +package coincident with the main sequence is optimally balanced with respect +to its abstractness and stability. Ideal packages are either completely +abstract and stable (x=0, y=1) or completely concrete and instable (x=1, +y=0). +

      +

      +The range for this metric is 0 to 1, with D=0 indicating a package that is +coincident with the main sequence and D=1 indicating a package that is as far +from the main sequence as possible. +

      +
    • +
    • Package Dependency Cycle +

      +Package dependency cycles are reported along with the paths of packages +participating in package dependency cycles. +

      +
    • +
    +

    +These metrics are hereafter referred to as the "Martin Metrics", as they are +credited to Robert Martin (Object Mentor Inc.) and referenced in the book +"Designing Object Oriented C++ Applications using the Booch Method", by +Robert C. Martin, Prentice Hall, 1995. +

    + diff --git a/res/main.css b/res/main.css new file mode 100644 index 0000000..772e009 --- /dev/null +++ b/res/main.css @@ -0,0 +1,62 @@ +body { + background-color: #eeeeee; + font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; + font-size: 10pt; +} + +h1 { + font-size: 24pt; + color: #023264; + font-family: Arial; + font-weight: bold; + font-family: Arial +} + +h2 { + color: white; + background-color: #023264; +} + +h3 { + font-size: 14pt; + color: #023264; +} + +#hor-minimalist-b { + font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif; + font-size: 12px; + margin: 45px; + width: 80%; + border-collapse: collapse; + text-align: left; +} + +#hor-minimalist-b th { + font-size: 14px; + font-weight: normal; + color: #039; + padding: 10px 8px; + border-bottom: 2px solid #6678b1; +} + +#hor-minimalist-b td { + border-bottom: 1px solid #ccc; + color: #669; + padding: 6px 8px; +} + +#hor-minimalist-b tbody tr:hover td { + color: #009; +} + +.message { + color: #aaaaaa; + font-size: 0.8em; +} + +.footer { + color: #aaaaaa; + font-size: 0.8em; + text-align: center; + margin-top: 3em; +} \ No newline at end of file diff --git a/src/jdepend/htmlui/JDepend.java b/src/jdepend/htmlui/JDepend.java index 9814ad9..4caaf4a 100644 --- a/src/jdepend/htmlui/JDepend.java +++ b/src/jdepend/htmlui/JDepend.java @@ -12,7 +12,7 @@ * The JDepend class analyzes directories of Java class files, generates metrics for each Java package, and * reports the metrics in a HTML format. * - * @author Richard Oliver Legendi (rlegendi) + * @author Richard Oliver Legendi (rlegendi) */ public class JDepend extends jdepend.textui.JDepend { @@ -42,23 +42,59 @@ private String findJavaClassLocation(final JavaClass javaClass) { return "src" + File.separatorChar + javaClass.getName().replace( '.', File.separatorChar ); } + /** + * Includes a file to the current PrintWriter object. + * + *

    + * File allocation is performed through the {@link Class#getResourceAsStream(String)} function, using the + * '/' prefix helps navigation with an absolute name of the referred resource. + *

    + * + * @param fileName the file to append to the current writer object + */ + private void includeFile(final String fileName) { + BufferedReader br = null; + + try { + br = new BufferedReader( new InputStreamReader( JDepend.class.getResourceAsStream( fileName ) ) ); + + String line = null; + while ( ( line = br.readLine() ) != null ) { + getWriter().println( line ); + } + } catch (final Exception e) { + usage( e.getMessage() ); + } finally { + if ( br != null ) { + try { + br.close(); + } catch (final IOException e) { + usage( e.getMessage() ); + } + } + } + } + protected void printHeader() { - // TODO Some prettier CSS would be helpful getWriter().println( "" ); getWriter().println( "" ); getWriter().println( "" ); getWriter().println( "" ); getWriter().println( "" ); getWriter().println( "JDepend Analysis Report" ); + getWriter().println( "" ); + getWriter().println( "" ); getWriter().println( "" ); getWriter().println( "

    JDepend Analysis Report

    " ); - getWriter().println( "

    Designed to use JDepend with Ant.

    " ); + getWriter().println( "

    Designed to use with JDepend and Ant.

    " ); + getWriter().println( "

    Report generated at " + new Date() ); + + getWriter().println( "

    General Information

    " ); + includeFile( "/info.html" ); } protected void printFooter() { @@ -84,7 +120,7 @@ protected void printPackageFooter(final JavaPackage jPackage) { } protected void printNoStats() { - getWriter().println( tab() + "

    No stats available: package referenced, but not analyzed.

    " ); // CHECKME + getWriter().println( tab() + "

    No stats available: package referenced, but not analyzed.

    " ); // CHECKME } protected void printStatistics(final JavaPackage jPackage) { @@ -135,7 +171,7 @@ protected void printEfferentsFooter() { } protected void printEfferentsError() { - getWriter().println( tab() + "

    Efferents not available.

    " ); + getWriter().println( tab() + "

    Efferents not available.

    " ); } protected void printAfferentsHeader() { @@ -147,7 +183,7 @@ protected void printAfferentsFooter() { } protected void printAfferentsError() { - getWriter().println( tab() + "

    Afferents not available.

    " ); + getWriter().println( tab() + "

    Afferents not available.

    " ); } protected void printCyclesHeader() { @@ -179,30 +215,49 @@ protected void printCycleContributor(final JavaPackage jPackage) { @SuppressWarnings("rawtypes") protected void printSummary(final Collection packages) { + getWriter().println( "" ); + getWriter().println( "

    Summary

    " ); - getWriter().println( "" ); - getWriter().println( "" ); - getWriter().println( "" ); - getWriter().println( "" ); + getWriter().println( "
    Name Class Count Abstract Class Count Ca Ce A I D V
    " ); + getWriter().println( "" ); + getWriter().println( tab( 1 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 1 ) + "" ); + getWriter().println( "" ); final Iterator i = packages.iterator(); while ( i.hasNext() ) { final JavaPackage jPackage = (JavaPackage) i.next(); - getWriter().println( "" ); - getWriter().print( "" ); - getWriter().print( "" ); - getWriter().print( "" ); - getWriter().print( "" ); - getWriter().print( "" ); - getWriter().print( "" ); - getWriter().print( "" ); - getWriter().print( "" ); - getWriter().println( "" ); - getWriter().println( "" ); + getWriter().println( "" ); + getWriter().println( tab( 1 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 2 ) + "" ); + getWriter().println( tab( 1 ) + "" ); + getWriter().println( "" ); } getWriter().println( "
    NameClass CountAbstract Class CountCaCeAIDV
    " + jPackage.getName() + "" + jPackage.getClassCount() + "" + jPackage.getAbstractClassCount() + "" + jPackage.afferentCoupling() + "" + jPackage.efferentCoupling() + "" + toFormattedString( jPackage.abstractness() ) + "" + toFormattedString( jPackage.instability() ) + "" + toFormattedString( jPackage.distance() ) + "" + jPackage.getVolatility() + "
    " + jPackage.getName() + "" + jPackage.getClassCount() + "" + jPackage.getAbstractClassCount() + "" + jPackage.afferentCoupling() + "" + jPackage.efferentCoupling() + "" + toFormattedString( jPackage.abstractness() ) + "" + toFormattedString( jPackage.instability() ) + "" + toFormattedString( jPackage.distance() ) + "" + jPackage.getVolatility() + "
    " ); + + getWriter().println(); + getWriter().println( "

    Generated by JDepend.
    " + + "HTML output formatting written by Richard O. Legendi.

    " ); + getWriter().println(); } /**