[doc] CPD Report Formats - add xslt examples

This commit is contained in:
Andreas Dangel 2023-05-12 12:22:49 +02:00
parent 50efdaa5d0
commit 16d92c2aba
No known key found for this signature in database
GPG Key ID: 93450DF2DF9A3FA3
4 changed files with 328 additions and 1 deletions

View File

@ -290,7 +290,7 @@ to be "debug".
## Available report formats ## Available report formats
* text : Default format * text : Default format
* xml * xml (and xslt)
* csv * csv
* csv_with_linecount_per_file * csv_with_linecount_per_file
* vs * vs
@ -404,6 +404,8 @@ the CPD task as usual and right after it invoke the Ant XSLT script like this:
<xslt in="cpd.xml" style="etc/xslt/cpdhtml.xslt" out="cpd.html" /> <xslt in="cpd.xml" style="etc/xslt/cpdhtml.xslt" out="cpd.html" />
``` ```
See [section "xslt" in CPD Report Formats](pmd_userdocs_cpd_report_formats.html#xslt) for more examples.
## GUI ## GUI
CPD also comes with a simple GUI. You can start it through the unified CLI interface provided in the `bin` folder: CPD also comes with a simple GUI. You can start it through the unified CLI interface provided in the `bin` folder:

View File

@ -95,6 +95,7 @@ Starting at line 110 of /home/pmd/source/pmd-core/src/test/java/net/sourceforge/
## xml ## xml
This format uses XML to output the duplications in a more structured format. This format uses XML to output the duplications in a more structured format.
The XML format can then further be processed using XSLT transformations. See [section xslt](#xslt) for examples.
Example: Example:
@ -220,3 +221,41 @@ Example:
/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java(88): Between lines 88 and 104 /home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java(88): Between lines 88 and 104
/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java(110): Between lines 110 and 126 /home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java(110): Between lines 110 and 126
``` ```
## xslt
This is not a direct report format. But you can use `xml` to generate an XML report and then use one of the following
XSLT stylesheets to convert the report into html. Or you can write your own stylesheet.
You can either use [Ant's XSLT task](https://ant.apache.org/manual/Tasks/style.html) or use any other (CLI) xslt processor,
e.g. `xalan` (see <https://xalan.apache.org/>).
### cpdhtml.xslt
This stylesheet is available in the sources or from GitHub at: <https://raw.githubusercontent.com/pmd/pmd/master/pmd-core/etc/xslt/cpdhtml.xslt>.
```shell
xalan -in cpd-report-sample.xml -xsl cpdhtml.xslt -out cpd-report-sample-cpdhtml.html
```
[Example](report-examples/cpdhtml.html)
This stylesheet by default only consideres duplications longer than 30 lines. You can change the default value with
the param `lines`:
```shell
xalan -in cpd-report-sample.xml -xsl cpdhtml.xslt -out cpd-report-sample-cpdhtml.html -param lines 10
```
### cpdhtml-v2.xslt
This stylesheet is available in the sources or from GitHub at: <https://raw.githubusercontent.com/pmd/pmd/master/pmd-core/etc/xslt/cpdhtml-v2.xslt>.
```shell
xalan -in pmd-core-cpd-report.xml -xsl etc/xslt/cpdhtml-v2.xslt -out pmd-core-cpd-report-v2.html
```
[Example](report-examples/cpdhtml-v2.html)
It requires javascript enabled and uses [Bootstrap](https://getbootstrap.com/),
[jQuery](https://jquery.com/), and [DataTables](https://datatables.net/).

View File

@ -0,0 +1,175 @@
<!DOCTYPE HTML SYSTEM "about:legacy-compat">
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
<link href="https://cdn.datatables.net/v/bs5/jszip-2.5.0/dt-1.13.4/b-2.3.6/b-html5-2.3.6/b-print-2.3.6/datatables.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/vfs_fonts.js"></script><script src="https://cdn.datatables.net/v/bs5/jszip-2.5.0/dt-1.13.4/b-2.3.6/b-html5-2.3.6/b-print-2.3.6/datatables.min.js"></script>
</head>
<body style="padding-top: 3.5rem;">
<nav class="navbar navbar-expand-lg fixed-top navbar-dark bg-dark">
<a class="navbar-brand" href="#">PMD - CPD (Copy and Paste Detector)</a><button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link active" href="#">Home</a>
</li>
<li class="nav-item" id="nav_enable_datatable">
<a class="nav-link" href="?d=">Enable datatable</a>
</li>
<li class="nav-item" id="nav_disable_datatable">
<a class="nav-link" href="?">Disable datatable</a>
</li>
<li class="nav-item">
<a class="nav-link" target="_blank" rel="noopener noreferrer" href="https://docs.pmd-code.org/latest/pmd_userdocs_cpd.html#refactoring-duplicates">About Refactoring Duplicates</a>
</li>
</ul>
</div>
</nav>
<div class="container">
<div class="row">
<div class="col">
<h4>Summary of duplicated code</h4>
<p>This page summarizes the code fragments that have been found to be replicated in the code.</p>
<table class="table table-light table-bordered table-striped table-hover">
<tr>
<th># Duplications</th><th>Total lines</th><th>Total tokens</th><th>Approximate number of bytes</th>
</tr>
<tr>
<td class="SummaryNumber">2</td><td class="SummaryNumber">49</td><td class="SummaryNumber">349</td><td class="SummaryNumber">1396</td>
</tr>
</table>
</div>
</div>
<div class="row">
<div class="col">
<h4>Details of duplicated code</h4>
</div>
<table style="width:100%" id="data_table" class="table table-light table-bordered table-striped table-hover">
<thead>
<tr>
<th>lines</th><th>tokens</th><th>files</th><th>codefragment</th>
</tr>
</thead>
<tbody>
<tr>
<td>33</td><td>239</td><td>
<table class="table table-light table-bordered table-striped table-hover">
<tr>
<th>column</th><th>endcolumn</th><th>line</th><th>endline</th><th>path</th>
</tr>
<tr>
<td>29</td><td>75</td><td>32</td><td>64</td><td>/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java</td>
</tr>
<tr>
<td>37</td><td>75</td><td>68</td><td>100</td><td>/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java</td>
</tr>
</table>
</td><td>
<pre> public void testOverride() {
final StringProperty PROPERTY1_DESCRIPTOR = new StringProperty("property1", "Test property", null, 0f);
MockRule rule = new MockRule();
rule.definePropertyDescriptor(PROPERTY1_DESCRIPTOR);
rule.setLanguage(LanguageRegistry.getLanguage(Dummy2LanguageModule.NAME));
rule.setName("name1");
rule.setProperty(PROPERTY1_DESCRIPTOR, "value1");
rule.setMessage("message1");
rule.setDescription("description1");
rule.addExample("example1");
rule.setExternalInfoUrl("externalInfoUrl1");
rule.setPriority(RulePriority.HIGH);
final StringProperty PROPERTY2_DESCRIPTOR = new StringProperty("property2", "Test property", null, 0f);
RuleReference ruleReference = new RuleReference();
ruleReference.setRule(rule);
ruleReference.definePropertyDescriptor(PROPERTY2_DESCRIPTOR);
ruleReference.setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME));
ruleReference
.setMinimumLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.3"));
ruleReference
.setMaximumLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7"));
ruleReference.setDeprecated(true);
ruleReference.setName("name2");
ruleReference.setProperty(PROPERTY1_DESCRIPTOR, "value2");
ruleReference.setProperty(PROPERTY2_DESCRIPTOR, "value3");
ruleReference.setMessage("message2");
ruleReference.setDescription("description2");
ruleReference.addExample("example2");
ruleReference.setExternalInfoUrl("externalInfoUrl2");
ruleReference.setPriority(RulePriority.MEDIUM_HIGH);
validateOverriddenValues(PROPERTY1_DESCRIPTOR, PROPERTY2_DESCRIPTOR, ruleReference);</pre>
</td>
</tr>
<tr>
<td>16</td><td>110</td><td>
<table class="table table-light table-bordered table-striped table-hover">
<tr>
<th>column</th><th>endcolumn</th><th>line</th><th>endline</th><th>path</th>
</tr>
<tr>
<td>9</td><td>28</td><td>66</td><td>81</td><td>/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java</td>
</tr>
<tr>
<td>9</td><td>28</td><td>88</td><td>103</td><td>/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java</td>
</tr>
<tr>
<td>9</td><td>28</td><td>110</td><td>125</td><td>/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java</td>
</tr>
</table>
</td><td>
<pre> JaxenXPathRuleQuery query = createQuery(xpath);
List&lt;String&gt; ruleChainVisits = query.getRuleChainVisits();
Assert.assertEquals(2, ruleChainVisits.size());
Assert.assertTrue(ruleChainVisits.contains("dummyNode"));
// Note: Having AST_ROOT in the rule chain visits is probably a mistake. But it doesn't hurt, it shouldn't
// match a real node name.
Assert.assertTrue(ruleChainVisits.contains(JaxenXPathRuleQuery.AST_ROOT));
DummyNodeWithListAndEnum dummy = new DummyNodeWithListAndEnum(1);
RuleContext data = new RuleContext();
data.setLanguageVersion(LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion());
query.evaluate(dummy, data);
// note: the actual xpath queries are only available after evaluating
Assert.assertEquals(2, query.nodeNameToXPaths.size());
Assert.assertEquals("self::node()[(attribute::Test1 = \"false\")][(attribute::Test2 = \"true\")]", query.nodeNameToXPaths.get("dummyNode").get(0).toString());</pre>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script>
let params = (new URL(document.location)).searchParams;
let showDatatable = false;
//------------ can be called with this parameter d
if (params.get('d') !== null) { // got it via query param d
showDatatable = true;
}
if (showDatatable) {
$("#nav_disable_datatable").show();
$("#nav_enable_datatable").hide();
$(document).ready( function () {
$('#data_table').DataTable({
dom: "<'row'<'col-sm-12 col-md-4'B><'col-sm-12 col-md-4'l><'col-sm-12 col-md-4'f>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
buttons: [
'copy', 'csv', 'excel', 'pdf', 'print'
]
}
);
} );
} else {
$("#nav_disable_datatable").hide();
$("#nav_enable_datatable").show();
}
</script>
</body>
</html>

View File

@ -0,0 +1,111 @@
<!DOCTYPE HTML SYSTEM "about:legacy-compat">
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="utf-8">
<script type="text/javascript">
function toggleCodeSection(btn, id)
{
area = document.getElementById(id);
if (area.style.display == 'none')
{
btn.innerHTML = '-';
area.style.display = 'inline';
}
else
{
btn.innerHTML = '+';
area.style.display = 'none';
}
}
</script>
<style type="text/css">
.SummaryTitle { }
.SummaryNumber { background-color:#DDDDDD; text-align: center; }
.ItemNumber { background-color: #DDDDDD; }
.CodeFragment { background-color: #BBBBBB; display:none; font:normal normal normal 9pt Courier; }
.ExpandButton { background-color: #FFFFFF; font-size: 8pt; width: 20px; height: 20px; margin:0px; }
</style>
</head>
<body>
<h2>Summary of duplicated code</h2>
This page summarizes the code fragments that have been found to be replicated in the code.
Only those fragments longer than 30 lines of code are shown.
<p></p>
<table border="1" class="summary" cellpadding="2">
<tr style="background-color:#CCCCCC;">
<th># duplications</th><th>Total lines</th><th>Total tokens</th><th>Approx # bytes</th>
</tr>
<tr>
<td class="SummaryNumber">1</td><td class="SummaryNumber">33</td><td class="SummaryNumber">239</td><td class="SummaryNumber">956</td>
</tr>
</table>
<p></p>
You expand and collapse the code fragments using the + buttons. You can also navigate to the source code by clicking
on the file names.
<p></p>
<table>
<tr style="background-color: #444444; color: #DDDDDD;">
<td>ID</td><td>Files</td><td>Lines</td>
</tr>
<tr>
<td class="ItemNumber">1</td><td>
<table>
<tr>
<td><a href="../src//home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java.html#32">/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java</a></td><td> line 32</td>
</tr>
<tr>
<td><a href="../src//home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java.html#68">/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java</a></td><td> line 68</td>
</tr>
</table>
</td><td># lines : 33</td>
</tr>
<tr>
<td></td><td colspan="2" valign="top">
<table>
<tr>
<td valign="top"><button class="ExpandButton" onclick="blur(); toggleCodeSection(this, 'frag_1')">+</button></td><td><textarea cols="100" wrap="off" class="CodeFragment" style="display:none;" rows="30" id="frag_1"> public void testOverride() {
final StringProperty PROPERTY1_DESCRIPTOR = new StringProperty("property1", "Test property", null, 0f);
MockRule rule = new MockRule();
rule.definePropertyDescriptor(PROPERTY1_DESCRIPTOR);
rule.setLanguage(LanguageRegistry.getLanguage(Dummy2LanguageModule.NAME));
rule.setName("name1");
rule.setProperty(PROPERTY1_DESCRIPTOR, "value1");
rule.setMessage("message1");
rule.setDescription("description1");
rule.addExample("example1");
rule.setExternalInfoUrl("externalInfoUrl1");
rule.setPriority(RulePriority.HIGH);
final StringProperty PROPERTY2_DESCRIPTOR = new StringProperty("property2", "Test property", null, 0f);
RuleReference ruleReference = new RuleReference();
ruleReference.setRule(rule);
ruleReference.definePropertyDescriptor(PROPERTY2_DESCRIPTOR);
ruleReference.setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME));
ruleReference
.setMinimumLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.3"));
ruleReference
.setMaximumLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7"));
ruleReference.setDeprecated(true);
ruleReference.setName("name2");
ruleReference.setProperty(PROPERTY1_DESCRIPTOR, "value2");
ruleReference.setProperty(PROPERTY2_DESCRIPTOR, "value3");
ruleReference.setMessage("message2");
ruleReference.setDescription("description2");
ruleReference.addExample("example2");
ruleReference.setExternalInfoUrl("externalInfoUrl2");
ruleReference.setPriority(RulePriority.MEDIUM_HIGH);
validateOverriddenValues(PROPERTY1_DESCRIPTOR, PROPERTY2_DESCRIPTOR, ruleReference);</textarea></td>
</tr>
</table>
</td>
</tr>
<tr>
<td colspan="2">
<hr>
</td>
</tr>
</table>
</body>
</html>