Browse Source

I've just put together a patch for the JDepend task to use xml

output. A new
attribute "format" has been added that can take either "xml" or "text"
defaulting to the latter. �Appropriate changes have been made to the
documentation, and additionally a .xsl file is attached to
produce html with
a similar style to that of junitreport.

Submitted by: "Rob Oxspring" <roxspring@yahoo.com>


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@269796 13f79535-47bb-0310-9956-ffa450edef68
master
Peter Donald 23 years ago
parent
commit
11d421035c
3 changed files with 353 additions and 50 deletions
  1. +13
    -8
      docs/manual/OptionalTasks/jdepend.html
  2. +258
    -0
      src/etc/jdepend.xsl
  3. +82
    -42
      src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java

+ 13
- 8
docs/manual/OptionalTasks/jdepend.html View File

@@ -16,14 +16,14 @@
<P>Invokes the <a href="http://www.clarkware.com/software/JDepend.html">JDepend</a> parser.</P>

<P>This parser &quot;traverses a set of Java source file directories and generates design quality metrics for each Java package&quot;.
It allows to &quot;automatically measure the quality of a design in terms of its extensibility, reusability, and maintainability to
It allows to &quot;automatically measure the quality of a design in terms of its extensibility, reusability, and maintainability to
effectively manage and control package dependencies.&quot;</P>

<p>Source file directories are defined by nested <code>&lt;sourcespath&gt;</code>, see <a href="#nested">nested elements</a>.</p>

<p>Optionally, you can also set the <code>outputfile</code> name where the output is stored. By default the task writes its report to the standard output.</P>

<p> The task requires at least the JDepend 1.2 version. </p>
<p> The task requires at least the JDepend 1.2 version. </p>

<P>Note: whereas the JDepend tool can be customized to exclude some packages, the current jdepend And Task does not have parameters to allow these exclusions. Read JDepend specific documentation for that purpose.</P>

@@ -40,6 +40,11 @@ effectively manage and control package dependencies.&quot;</P>
<td VALIGN=TOP>The output file name. If not set, the output is printed on the standard output.</td>
<td ALIGN=CENTER VALIGN=TOP>No</td>
</tr>
<tr>
<td VALIGN=TOP>format</td>
<td VALIGN=TOP>The fomat to write the output in. The default is "text", the alternative is "xml"</td>
<td ALIGN=CENTER VALIGN=TOP>No</td>
</tr>
<tr>
<td VALIGN=TOP>fork</td>
<td VALIGN=TOP>Run the tests in a separate VM.</td>
@@ -74,8 +79,8 @@ effectively manage and control package dependencies.&quot;</P>

<h3><a name="nested">Nested Elements</a></h3>

<p><code>jdepend</code> supports two nested elements <code>&lt;classpath&gt;</code> and <code>&lt;sourcespath&gt;</code>,
that represent <a href="../using.html#path">PATH like structures</a>.</p>
<p><code>jdepend</code> supports two nested elements <code>&lt;classpath&gt;</code> and <code>&lt;sourcespath&gt;</code>,
that represent <a href="../using.html#path">PATH like structures</a>.</p>

<p><code>&lt;sourcespath&gt;</code> is used to define the paths of the source code to analyze.</p>

@@ -96,7 +101,7 @@ The classpath is defined using a classpath reference.

<blockquote>
<pre>
&lt;jdepend outputfile="docs/jdepend.txt" fork="yes">
&lt;jdepend outputfile="docs/jdepend.xml" fork="yes" format="xml">
&nbsp;&nbsp;&nbsp; &lt;sourcespath>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;pathelement location="src" />
&nbsp;&nbsp;&nbsp; &lt;/sourcespath>
@@ -106,10 +111,10 @@ The classpath is defined using a classpath reference.
&nbsp;&nbsp;&nbsp; &lt;/classpath>
&lt;/jdepend>
</pre>
</blockquote>
</blockquote>

This invokes JDepend in a separate VM on the <code>src</code> and <code>testsrc</code> directories, writing the output in the <code>&lt;docs/jdepend.txt&gt;</code> file.
The classpath is defined using nested elements.
This invokes JDepend in a separate VM on the <code>src</code> and <code>testsrc</code> directories, writing the output to the <code>&lt;docs/jdepend.xml&gt;</code> file in xml format.
The classpath is defined using nested elements.

<hr>
</body>


+ 258
- 0
src/etc/jdepend.xsl View File

@@ -0,0 +1,258 @@
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:decimal-format decimal-separator="." grouping-separator="," />

<xsl:template match="JDepend">
<html>
<head>
<title>JDepend Analysis</title>
<style type="text/css">
body {
font:normal 68% verdana,arial,helvetica;
color:#000000;
}
table tr td, tr th {
font-size: 68%;
}
table.details tr th{
font-weight: bold;
text-align:left;
background:#a6caf0;
}
table.details tr td{
background:#eeeee0;
}
p {
line-height:1.5em;
margin-top:0.5em; margin-bottom:1.0em;
margin-left:2em;
margin-right:2em;
}
h1 {
margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
}
h2 {
margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
}
h3 {
margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
}
h4 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
h5 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
h6 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
.Error {
font-weight:bold; color:red;
}
.Failure {
font-weight:bold; color:purple;
}
.Properties {
text-align:right;
}
</style>
</head>
<body>
<!--h1>JDepend Report</h1>
<ul>
<xsl:for-each select="./Packages/Package">
<xsl:sort select="@name"/>
<li><xsl:value-of select="@name"/></li>
</xsl:for-each>
</ul-->
<h1><a name="top">JDepend Analysis</a></h1>
<p align="right">Designed for use with <a href="http://www.clarkware.com/software/JDepend.html">JDepend</a> and <a href="http://jakarta.apache.org">Ant</a>.</p>
<hr size="2" />
<table width="100%"><tr><td>
<a name="NVsummary"><h2>Summary</h2></a>
</td><td align="right">
[<a href="#NVsummary">summary</a>]
[<a href="#NVpackages">packages</a>]
[<a href="#NVcycles">cycles</a>]
[<a href="#NVexplanations">explanations</a>]
</td></tr></table>
<table width="100%" class="details">
<tr>
<th>Package</th>
<th>Total Classes</th>
<th><a href="#EXnumber">Abstract Classes</a></th>
<th><a href="#EXnumber">Concrete Classes</a></th>
<th><a href="#EXafferent">Afferent Couplings</a></th>
<th><a href="#EXefferent">Efferent Couplings</a></th>
<th><a href="#EXabstractness">Abstractness</a></th>
<th><a href="#EXinstability">Instability</a></th>
<th><a href="#EXdistance">Distance</a></th>
</tr>
<xsl:for-each select="./Packages/Package">
<xsl:if test="count(error) = 0">
<tr>
<td align="left">
<a>
<xsl:attribute name="href">#PK<xsl:value-of select="@name"/>
</xsl:attribute>
<xsl:value-of select="@name"/>
</a>
</td>
<td align="right"><xsl:value-of select="Stats/TotalClasses"/></td>
<td align="right"><xsl:value-of select="Stats/AbstractClasses"/></td>
<td align="right"><xsl:value-of select="Stats/ConcreteClasses"/></td>
<td align="right"><xsl:value-of select="Stats/Ca"/></td>
<td align="right"><xsl:value-of select="Stats/Ce"/></td>
<td align="right"><xsl:value-of select="format-number(Stats/A, '0%')"/></td>
<td align="right"><xsl:value-of select="format-number(Stats/I, '0%')"/></td>
<td align="right"><xsl:value-of select="format-number(Stats/D, '0%')"/></td>

</tr>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="./Packages/Package">
<xsl:if test="count(error) &gt; 0">
<tr>
<td align="left">
<xsl:value-of select="@name"/>
</td>
<td align="left" colspan="8"><xsl:value-of select="error"/></td>
</tr>
</xsl:if>
</xsl:for-each>
</table>
<table width="100%"><tr><td>
<a name="NVpackages"><h2>Packages</h2></a>
</td><td align="right">
[<a href="#NVsummary">summary</a>]
[<a href="#NVpackages">packages</a>]
[<a href="#NVcycles">cycles</a>]
[<a href="#NVexplanations">explanations</a>]
</td></tr></table>
<xsl:for-each select="./Packages/Package">
<xsl:if test="count(error) = 0">
<h3><a><xsl:attribute name="name">PK<xsl:value-of select="@name"/></xsl:attribute>
<xsl:value-of select="@name"/></a></h3>
<table width="100%"><tr>
<td><a href="#EXafferent">Afferent Couplings</a>: <xsl:value-of select="Stats/Ca"/></td>
<td><a href="#EXefferent">Efferent Couplings</a>: <xsl:value-of select="Stats/Ce"/></td>
<td><a href="#EXabstractness">Abstractness</a>: <xsl:value-of select="format-number(Stats/A, '0%')"/></td>
<td><a href="#EXinstability">Instability</a>: <xsl:value-of select="format-number(Stats/I, '0%')"/></td>
<td><a href="#EXdistance">Distance</a>: <xsl:value-of select="format-number(Stats/D, '0%')"/></td>
</tr></table>
<table width="100%" class="details">
<tr>
<th>Abstract Classes</th>
<th>Concrete Classes</th>
<th>Used by Packages</th>
<th>Uses Packages</th>
</tr>
<tr>
<td valign="top" width="25%">
<xsl:if test="count(AbstractClasses/Class)=0">
<i>None</i>
</xsl:if>
<xsl:for-each select="AbstractClasses/Class">
<xsl:value-of select="node()"/><br/>
</xsl:for-each>
</td>
<td valign="top" width="25%">
<xsl:if test="count(ConcreteClasses/Class)=0">
<i>None</i>
</xsl:if>
<xsl:for-each select="ConcreteClasses/Class">
<xsl:value-of select="node()"/><br/>
</xsl:for-each>
</td>
<td valign="top" width="25%">
<xsl:if test="count(UsedBy/Package)=0">
<i>None</i>
</xsl:if>
<xsl:for-each select="UsedBy/Package">
<a>
<xsl:attribute name="href">#PK<xsl:value-of select="node()"/></xsl:attribute>
<xsl:value-of select="node()"/>
</a><br/>
</xsl:for-each>
</td>
<td valign="top" width="25%">
<xsl:if test="count(DependsUpon/Package)=0">
<i>None</i>
</xsl:if>
<xsl:for-each select="DependsUpon/Package">
<a>
<xsl:attribute name="href">#PK<xsl:value-of select="node()"/></xsl:attribute>
<xsl:value-of select="node()"/>
</a><br/>
</xsl:for-each>
</td>
</tr>
</table>
</xsl:if>
</xsl:for-each>
<table width="100%"><tr><td>
<a name="NVcycles"><h2>Cycles</h2></a>
</td><td align="right">
[<a href="#NVsummary">summary</a>]
[<a href="#NVpackages">packages</a>]
[<a href="#NVcycles">cycles</a>]
[<a href="#NVexplanations">explanations</a>]
</td></tr></table>
<xsl:if test="count(Cycles/Package) = 0">
<p>There are no cyclic dependancies.</p>
</xsl:if>
<xsl:for-each select="Cycles/Package">
<h3><xsl:value-of select="@Name"/></h3><p>
<xsl:for-each select="Package">
<xsl:value-of select="."/><br/>
</xsl:for-each></p>
</xsl:for-each>
<table width="100%"><tr><td>
<a name="NVexplanations"><h2>Explanations</h2></a>
</td><td align="right">
[<a href="#NVsummary">summary</a>]
[<a href="#NVpackages">packages</a>]
[<a href="#NVcycles">cycles</a>]
[<a href="#NVexplanations">explanations</a>]
</td></tr></table>
<p>The following explanations are for quick reference and are lifted directly from the original <a href="http://www.clarkware.com/software/JDepend.html">JDepend documentation</a>.</p>
<h3><a name="EXnumber">Number of Classes</a></h3>
<p>The number of concrete and abstract classes (and interfaces) in the package is an indicator of the extensibility of the package.</p>
<h3><a name="EXafferent">Afferent Couplings</a></h3>
<p>The number of other packages that depend upon classes within the package is an indicator of the package's responsibility. </p>
<h3><a name="EXefferent">Efferent Couplings</a></h3>
<p>The number of other packages that the classes in the package depend upon is an indicator of the package's independence. </p>
<h3><a name="EXabstractness">Abstractness</a></h3>
<p>The ratio of the number of abstract classes (and interfaces) in the analyzed package to the total number of classes in the analyzed package. </p>
<p>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. </p>
<h3><a name="EXinstability">Instability</a></h3>
<p>The ratio of efferent coupling (Ce) to total coupling (Ce / (Ce + Ca)). This metric is an indicator of the package's resilience to change. </p>
<p>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. </p>
<h3><a name="EXdistance">Distance</a></h3>
<p>The perpendicular distance of a package from the idealized line A + I = 1. This metric is an indicator of the package's balance between abstractness and stability. </p>
<p>A package squarely on 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). </p>
<p>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. </p>
</body>
</html>
</xsl:template>

</xsl:stylesheet>

+ 82
- 42
src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java View File

@@ -71,6 +71,7 @@ import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.CommandlineJava;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.EnumeratedAttribute;

/**
* Ant task to run JDepend tests.
@@ -82,10 +83,11 @@ import org.apache.tools.ant.types.Reference;
* The current implementation spawn a new Java VM.
*
* @author <a href="mailto:Jerome@jeromelacoste.com">Jerome Lacoste</a>
* @author <a href="mailto:roxspring@yahoo.com">Rob Oxspring</a>
*/
public class JDependTask extends Task {
private CommandlineJava commandline = new CommandlineJava();
//private CommandlineJava commandline = new CommandlineJava();
// required attributes
private Path _sourcesPath;

@@ -94,23 +96,26 @@ public class JDependTask extends Task {
private File _dir;
private Path _compileClasspath;
private boolean _haltonerror = false;
private boolean _fork = false;
private boolean _fork = false;
//private Integer _timeout = null;

private String _jvm = null;
private String format = "text";

public JDependTask() {
commandline.setClassname("jdepend.textui.JDepend");
}

/*
public void setTimeout(Integer value) {
_timeout = value;
}
public Integer getTimeout() {
return _timeout;
}
*/
public void setOutputFile(File outputFile) {
_outputFile = outputFile;
}
@@ -134,9 +139,9 @@ public class JDependTask extends Task {
* Tells whether a JVM should be forked for the task. Default: false.
* @param value <tt>true</tt> if a JVM should be forked, otherwise <tt>false<tt>
*/
public void setFork(boolean value) {
_fork = value;
}
public void setFork(boolean value) {
_fork = value;
}

public boolean getFork() {
return _fork;
@@ -148,9 +153,10 @@ public class JDependTask extends Task {
* @see #setFork(boolean)
*/
public void setJvm(String value) {
commandline.setVm(value);
_jvm = value;

}
/**
* Maybe creates a nested classpath element.
*/
@@ -165,7 +171,7 @@ public class JDependTask extends Task {
public Path getSourcespath() {
return _sourcesPath;
}
/**
* The directory to invoke the VM in. Ignored if no JVM is forked.
* @param dir the directory to invoke the JVM from.
@@ -210,7 +216,7 @@ public class JDependTask extends Task {
* @return create a new JVM argument so that any argument can be passed to the JVM.
* @see #setFork(boolean)
*/
public Commandline.Argument createJvmarg() {
public Commandline.Argument createJvmarg(CommandlineJava commandline ) {
return commandline.createVmArgument();
}

@@ -221,27 +227,56 @@ public class JDependTask extends Task {
createClasspath().setRefid(r);
}


public void setFormat(FormatAttribute ea)
{
format = ea.getValue();
}

public static class FormatAttribute extends EnumeratedAttribute
{
private String [] formats = new String[]{"xml","text"};

public String[] getValues()
{
return formats;
}
}


/**
* No problems with this test.
*/
private static final int SUCCESS = 0;
private static final int SUCCESS = 0;
/**
* An error occured.
*/
*/
private static final int ERRORS = 1;
public void execute() throws BuildException {

CommandlineJava commandline = new CommandlineJava();

if("text".equals(format))
commandline.setClassname("jdepend.textui.JDepend");
else
if("xml".equals(format))
commandline.setClassname("jdepend.xmlui.JDepend");

if(_jvm!=null)
commandline.setVm(_jvm);

if (getSourcespath() == null)
throw new BuildException("Missing Sourcepath required argument");
// execute the test and get the return code
int exitValue = JDependTask.ERRORS;
boolean wasKilled = false;
if (! getFork()) {
exitValue = executeInVM();
exitValue = executeInVM(commandline);
} else {
ExecuteWatchdog watchdog = createWatchdog();
exitValue = executeAsForked(watchdog);
ExecuteWatchdog watchdog = createWatchdog();
exitValue = executeAsForked(commandline,watchdog);
// null watchdog means no timeout, you'd better not check with null
if (watchdog != null) {
//info will be used in later version do nothing for now
@@ -260,21 +295,26 @@ public class JDependTask extends Task {
else
log("JDepend FAILED", Project.MSG_ERR);
}
}
}



// this comment extract from JUnit Task may also apply here
// "in VM is not very nice since it could probably hang the
// whole build. IMHO this method should be avoided and it would be best
// to remove it in future versions. TBD. (SBa)"
// to remove it in future versions. TBD. (SBa)"
/**
* Execute inside VM.
*/
public int executeInVM() throws BuildException {
jdepend.textui.JDepend jdepend = new jdepend.textui.JDepend();
public int executeInVM(CommandlineJava commandline) throws BuildException {
jdepend.textui.JDepend jdepend;

if("xml".equals(format))
jdepend = new jdepend.xmlui.JDepend();
else
jdepend = new jdepend.textui.JDepend();

if (getOutputFile() != null) {
FileWriter fw;
try {
@@ -286,21 +326,21 @@ public class JDependTask extends Task {
throw new BuildException(msg);
}
jdepend.setWriter(new PrintWriter(fw));
log("Ouptut to be stored in " + getOutputFile().getPath());
log("Output to be stored in " + getOutputFile().getPath());
}

PathTokenizer sourcesPath = new PathTokenizer(getSourcespath().toString());
while (sourcesPath.hasMoreTokens()) {
File f = new File(sourcesPath.nextToken());
// not necessary as JDepend would fail, but why loose some time?
// not necessary as JDepend would fail, but why loose some time?
if (! f.exists() || !f.isDirectory()) {
String msg = "\""+ f.getPath() + "\" does not represent a valid directory. JDepend would fail.";
log(msg);
throw new BuildException(msg);
}
try {
jdepend.addDirectory(f.getPath());
try {
jdepend.addDirectory(f.getPath());
}
catch (IOException e) {
String msg = "JDepend Failed when adding a source directory: " + e.getMessage();
@@ -311,7 +351,7 @@ public class JDependTask extends Task {
jdepend.analyze();
return SUCCESS;
}

/**
* Execute the task by forking a new JVM. The command will block until
@@ -322,15 +362,15 @@ public class JDependTask extends Task {
* the test could probably hang forever.
*/
// JL: comment extracted from JUnitTask (and slightly modified)
public int executeAsForked(ExecuteWatchdog watchdog) throws BuildException {
public int executeAsForked(CommandlineJava commandline,ExecuteWatchdog watchdog) throws BuildException {
// if not set, auto-create the ClassPath from the project
createClasspath();

// not sure whether this test is needed but cost nothing to put.
// hope it will be reviewed by anybody competent
if (getClasspath().toString().length() > 0) {
createJvmarg().setValue("-classpath");
createJvmarg().setValue(getClasspath().toString());
createJvmarg(commandline).setValue("-classpath");
createJvmarg(commandline).setValue(getClasspath().toString());
}

if (getOutputFile() != null) {
@@ -344,22 +384,22 @@ public class JDependTask extends Task {
PathTokenizer sourcesPath = new PathTokenizer(getSourcespath().toString());
while (sourcesPath.hasMoreTokens()) {
File f = new File(sourcesPath.nextToken());
// not necessary as JDepend would fail, but why loose some time?
if (! f.exists() || !f.isDirectory())
throw new BuildException("\""+ f.getPath() + "\" does not represent a valid directory. JDepend would fail.");
commandline.createArgument().setValue(f.getPath());
}
Execute execute = new Execute(new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN), watchdog);
Execute execute = new Execute(new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN), watchdog);
execute.setCommandline(commandline.getCommandline());
if (getDir() != null) {
execute.setWorkingDirectory(getDir());
execute.setAntRun(project);
}

if (getOutputFile() != null)
log("Ouptut to be stored in " + getOutputFile().getPath());
if (getOutputFile() != null)
log("Output to be stored in " + getOutputFile().getPath());
log("Executing: "+commandline.toString(), Project.MSG_VERBOSE);
try {
return execute.execute();


Loading…
Cancel
Save