Skip to content

Commit c1962e3

Browse files
Merge pull request #35 from jimbethancourt/add-csv-report
#22 Add csv report
2 parents 65af55a + f51a0b1 commit c1962e3

File tree

3 files changed

+286
-7
lines changed

3 files changed

+286
-7
lines changed

pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,14 @@
238238
<goal>report</goal>
239239
</goals>
240240
</execution>
241+
242+
<execution>
243+
<id>csvreport</id>
244+
<phase>verify</phase>
245+
<goals>
246+
<goal>report</goal>
247+
</goals>
248+
</execution>
241249
</executions>
242250
</plugin>
243251

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
package org.hjug.mavenreport;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.apache.maven.plugin.AbstractMojo;
5+
import org.apache.maven.plugins.annotations.LifecyclePhase;
6+
import org.apache.maven.plugins.annotations.Mojo;
7+
import org.apache.maven.plugins.annotations.Parameter;
8+
import org.apache.maven.plugins.annotations.ResolutionScope;
9+
import org.apache.maven.project.MavenProject;
10+
import org.hjug.cbc.CostBenefitCalculator;
11+
import org.hjug.cbc.RankedDisharmony;
12+
import org.hjug.git.GitLogReader;
13+
14+
import java.io.BufferedWriter;
15+
import java.io.File;
16+
import java.io.FileWriter;
17+
import java.io.IOException;
18+
import java.time.Instant;
19+
import java.time.ZoneId;
20+
import java.time.format.DateTimeFormatter;
21+
import java.time.format.FormatStyle;
22+
import java.util.Comparator;
23+
import java.util.List;
24+
import java.util.Locale;
25+
import java.util.Optional;
26+
27+
@Slf4j
28+
@Mojo(
29+
name = "csvreport",
30+
defaultPhase = LifecyclePhase.SITE,
31+
requiresDependencyResolution = ResolutionScope.RUNTIME,
32+
requiresProject = true,
33+
threadSafe = true,
34+
inheritByDefault = false
35+
)
36+
public class RefactorFirstMavenCsvReport extends AbstractMojo {
37+
38+
@Parameter(property = "showDetails")
39+
private boolean showDetails = false;
40+
41+
@Parameter(defaultValue = "${project.name}")
42+
private String projectName;
43+
44+
@Parameter(defaultValue = "${project.version}")
45+
private String projectVersion;
46+
47+
@Parameter(readonly = true, defaultValue = "${project}")
48+
private MavenProject project;
49+
50+
@Parameter(property = "project.build.directory")
51+
protected File outputDirectory;
52+
53+
public String getOutputNamePrefix() {
54+
// This report will generate simple-report.html when invoked in a project with `mvn site`
55+
return "RefFirst";
56+
}
57+
58+
public String getName(Locale locale) {
59+
// Name of the report when listed in the project-reports.html page of a project
60+
return "Refactor First Report data";
61+
}
62+
63+
public String getDescription(Locale locale) {
64+
// Description of the report when listed in the project-reports.html page of a project
65+
return "DRACO Ranks the disharmonies in a codebase. The classes that should be refactored first "
66+
+ " have the highest priority values.";
67+
}
68+
69+
@Override
70+
public void execute() {
71+
StringBuilder fileNameSB = new StringBuilder();
72+
String publishedDate = createFileDateTimeFormatter().format(Instant.now());
73+
74+
fileNameSB
75+
.append(getOutputNamePrefix())
76+
.append("_P")
77+
.append(project.getArtifactId())
78+
.append("_PV")
79+
.append(project.getVersion())
80+
.append("_PD")
81+
.append(publishedDate)
82+
.append(".csv");
83+
String filename = fileNameSB.toString();
84+
85+
log.info("Generating {} for {} - {} date: {}", filename, projectName, projectVersion, publishedDate );
86+
87+
StringBuilder contentBuilder = new StringBuilder();
88+
89+
// git management
90+
GitLogReader gitLogReader = new GitLogReader();
91+
String projectBaseDir = project.getBasedir().getPath();
92+
Optional<File> optionalGitDir = Optional.ofNullable(gitLogReader.getGitDir(project.getBasedir()));
93+
File gitDir;
94+
95+
if (optionalGitDir.isPresent()) {
96+
gitDir = optionalGitDir.get();
97+
} else {
98+
log.info("Done! No Git repository found! Please initialize a Git repository and perform an initial commit.");
99+
contentBuilder.append("No Git repository found in project ")
100+
.append(projectName).append(" ")
101+
.append(projectVersion).append(". ");
102+
contentBuilder.append("Please initialize a Git repository and perform an initial commit.");
103+
writeReportToDisk(filename, contentBuilder);
104+
return;
105+
}
106+
107+
String parentOfGitDir = gitDir.getParentFile().getPath();
108+
log.info("Project Base Dir: {} ", projectBaseDir);
109+
log.info("Parent of Git Dir: {}", parentOfGitDir);
110+
111+
if(!projectBaseDir.equals(parentOfGitDir)) {
112+
log.warn("Project Base Directory does not match Git Parent Directory");
113+
contentBuilder.append(
114+
"Project Base Directory does not match Git Parent Directory. " +
115+
"Please refer to the report at the root of the site directory.");
116+
return;
117+
}
118+
119+
// actual calcualte
120+
CostBenefitCalculator costBenefitCalculator = new CostBenefitCalculator();
121+
List<RankedDisharmony> rankedDisharmonies = costBenefitCalculator.calculateCostBenefitValues(projectBaseDir);
122+
123+
rankedDisharmonies.sort(Comparator.comparing(RankedDisharmony::getPriority).reversed());
124+
125+
// perfect score: no god classes
126+
if(rankedDisharmonies.isEmpty()) {
127+
contentBuilder.append("Congratulations! ")
128+
.append(projectName).append(" ")
129+
.append(projectVersion).append(" has no God classes!");
130+
log.info("Done! No God classes found!");
131+
132+
writeReportToDisk(filename, contentBuilder);
133+
return;
134+
}
135+
136+
// create Content
137+
// header
138+
final String[] tableHeadings = getHeaderList();
139+
addsRow(contentBuilder, tableHeadings);
140+
contentBuilder.append("\n");
141+
// rows
142+
for (RankedDisharmony rankedDisharmony : rankedDisharmonies) {
143+
final String[] rankedDisharmonyData = getDataList(rankedDisharmony);
144+
145+
contentBuilder.append(project.getVersion()).append(",");
146+
addsRow(contentBuilder, rankedDisharmonyData);
147+
contentBuilder.append("eol" + "\n");
148+
}
149+
150+
log.info(contentBuilder.toString());
151+
152+
writeReportToDisk(filename, contentBuilder);
153+
}
154+
155+
private DateTimeFormatter createFileDateTimeFormatter() {
156+
return DateTimeFormatter.ofPattern("yyyyMMddhhmm")
157+
.withLocale( Locale.getDefault() )
158+
.withZone( ZoneId.systemDefault() );
159+
}
160+
161+
private DateTimeFormatter createCsvDateTimeFormatter() {
162+
return DateTimeFormatter.ISO_LOCAL_DATE_TIME
163+
.withLocale( Locale.getDefault() )
164+
.withZone( ZoneId.systemDefault() );
165+
}
166+
167+
168+
private String[] getDataList(RankedDisharmony rankedDisharmony ) {
169+
String[] simpleRankedDisharmonyData = { rankedDisharmony.getClassName(),
170+
rankedDisharmony.getPriority().toString(),
171+
rankedDisharmony.getChangePronenessRank().toString(),
172+
rankedDisharmony.getEffortRank().toString(),
173+
rankedDisharmony.getWmc().toString(),
174+
createCsvDateTimeFormatter().format(rankedDisharmony.getMostRecentCommitTime()),
175+
rankedDisharmony.getCommitCount().toString()};
176+
177+
String[] detailedRankedDisharmonyData = { rankedDisharmony.getClassName(),
178+
rankedDisharmony.getPriority().toString(),
179+
rankedDisharmony.getChangePronenessRank().toString(),
180+
rankedDisharmony.getEffortRank().toString(),
181+
rankedDisharmony.getWmc().toString(),
182+
rankedDisharmony.getWmcRank().toString(),
183+
rankedDisharmony.getAtfd().toString(),
184+
rankedDisharmony.getAtfdRank().toString(),
185+
rankedDisharmony.getTcc().toString(),
186+
rankedDisharmony.getTccRank().toString(),
187+
createCsvDateTimeFormatter().format(rankedDisharmony.getFirstCommitTime()),
188+
createCsvDateTimeFormatter().format(rankedDisharmony.getMostRecentCommitTime()),
189+
rankedDisharmony.getCommitCount().toString(),
190+
rankedDisharmony.getPath()};
191+
192+
193+
return showDetails ? detailedRankedDisharmonyData : simpleRankedDisharmonyData;
194+
}
195+
196+
private String[] getHeaderList() {
197+
198+
final String[] simpleTableHeadings = {
199+
"Ver",
200+
"Class",
201+
"Priority",
202+
"Change Proneness Rank",
203+
"Effort Rank",
204+
"Method Count",
205+
"Most Recent Commit Date",
206+
"Commit Count"};
207+
208+
final String[] detailedTableHeadings = {
209+
"Ver",
210+
"Class",
211+
"Priority",
212+
"Change Proneness Rank",
213+
"Effort Rank",
214+
"WMC",
215+
"WMC Rank",
216+
"ATFD",
217+
"ATFD Rank",
218+
"TCC",
219+
"TCC Rank",
220+
"Date of First Commit",
221+
"Most Recent Commit Date",
222+
"Commit Count",
223+
"Full Path"};
224+
225+
return showDetails ? detailedTableHeadings : simpleTableHeadings;
226+
}
227+
228+
private void addsRow(StringBuilder contentBuilder, String[] rankedDisharmonyData) {
229+
for (String rowData : rankedDisharmonyData) {
230+
contentBuilder.append(rowData).append(",");
231+
}
232+
}
233+
234+
private void writeReportToDisk(String filename, StringBuilder stringBuilder) {
235+
String reportOutputDirectory = project.getModel().getReporting().getOutputDirectory();
236+
File reportOutputDir = new File(reportOutputDirectory);
237+
if(!reportOutputDir.exists()) {
238+
reportOutputDir.mkdirs();
239+
}
240+
String pathname = reportOutputDirectory + File.separator + filename;
241+
242+
File reportFile = new File(pathname);
243+
try {
244+
reportFile.createNewFile();
245+
} catch (IOException e) {
246+
log.error("Failure creating chart script file", e);
247+
}
248+
249+
try(BufferedWriter writer = new BufferedWriter(new FileWriter(reportFile))) {
250+
writer.write(stringBuilder.toString());
251+
} catch (IOException e) {
252+
log.error("Error writing chart script file", e);
253+
}
254+
255+
log.info("Done! View the report at target/site/{}", filename);
256+
}
257+
}

refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenReport.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ public void execute() {
151151
" <link rel=\"stylesheet\" href=\"./css/maven-theme.css\" />\n" +
152152
" <link rel=\"stylesheet\" href=\"./css/site.css\" />\n" +
153153
" <link rel=\"stylesheet\" href=\"./css/print.css\" media=\"print\" />\n" +
154-
"<script type=\"text/javascript\" src=\"https://www.gstatic.com/charts/loader.js\"></script><script type=\"text/javascript\" src=\"./gchart.js\"></script> </head>\n" +
154+
"<script type=\"text/javascript\" src=\"https://www.gstatic.com/charts/loader.js\">" +
155+
"</script><script type=\"text/javascript\" src=\"./gchart.js\"></script> </head>\n" +
155156
" <body class=\"composite\">\n" +
156157
" <div id=\"banner\">\n" +
157158
" <div class=\"clear\">\n" +
@@ -161,8 +162,14 @@ public void execute() {
161162
" <div id=\"breadcrumbs\">\n" +
162163
" <div class=\"xleft\">");
163164

164-
stringBuilder.append("<span id=\"publishDate\">Last Published: ").append(formatter.format(Instant.now())).append("</span>");
165-
stringBuilder.append("<span id=\"projectVersion\"> Version: ").append(projectVersion).append("</span>");
165+
stringBuilder
166+
.append("<span id=\"publishDate\">Last Published: ")
167+
.append(formatter.format(Instant.now()))
168+
.append("</span>");
169+
stringBuilder
170+
.append("<span id=\"projectVersion\"> Version: ")
171+
.append(projectVersion)
172+
.append("</span>");
166173

167174
stringBuilder.append("</div>\n" +
168175
" <div class=\"xright\"> </div>\n" +
@@ -180,8 +187,11 @@ public void execute() {
180187
" <div id=\"bodyColumn\">\n" +
181188
" <div id=\"contentBox\">");
182189

183-
stringBuilder.append("<section>\n" + "<h2>God Class Report for ")
184-
.append(projectName).append(" ").append(projectVersion).append("</h2>\n").append("<div id=\"series_chart_div\"></div>");
190+
stringBuilder
191+
.append("<section>\n" + "<h2>God Class Report for ")
192+
.append(projectName).append(" ").append(projectVersion)
193+
.append("</h2>\n")
194+
.append("<div id=\"series_chart_div\"></div>");
185195

186196
GitLogReader gitLogReader = new GitLogReader();
187197
String projectBaseDir = project.getBasedir().getPath();
@@ -206,7 +216,9 @@ public void execute() {
206216

207217
if(!projectBaseDir.equals(parentOfGitDir)) {
208218
log.warn("Project Base Directory does not match Git Parent Directory");
209-
stringBuilder.append("Project Base Directory does not match Git Parent Directory. Please refer to the report at the root of the site directory.");
219+
stringBuilder.append(
220+
"Project Base Directory does not match Git Parent Directory. " +
221+
"Please refer to the report at the root of the site directory.");
210222
stringBuilder.append(THE_END);
211223
return;
212224
}
@@ -217,7 +229,9 @@ public void execute() {
217229
rankedDisharmonies.sort(Comparator.comparing(RankedDisharmony::getPriority).reversed());
218230

219231
if(rankedDisharmonies.isEmpty()) {
220-
stringBuilder.append("Congratulations! ").append(projectName).append(" ").append(projectVersion).append(" has no God classes!");
232+
stringBuilder.append("Congratulations! ")
233+
.append(projectName).append(" ")
234+
.append(projectVersion).append(" has no God classes!");
221235
log.info("Done! No God classes found!");
222236
stringBuilder.append(THE_END);
223237
writeReportToDisk(filename, stringBuilder);

0 commit comments

Comments
 (0)