diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 890984f9..186420da 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -17,10 +17,10 @@ jobs: with: ref: main - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Configure Git user run: | diff --git a/README.md b/README.md index fd2f7567..4afaa032 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,17 @@ It runs PMD's God Class Rule and Coupling Between Objects rule and scans your Gi The graphs generated in the report will look similar to this one: ![image info](./RefactorFirst_Sample_Report.png) +## Please Note: Java 11 is now required to run RefactorFirst +The change to require Java 11 is needed to address vulnerability CVE-2023-4759 in JGit +Java 21 features are not yet integrated since PMD APIs have changed. + ## There are several ways to run the analysis on your codebase: ### From The Command Line Run the following command from the root of your project (the source code does not need to be built): ```bash -mvn org.hjug.refactorfirst.plugin:refactor-first-maven-plugin:0.5.0:report +mvn org.hjug.refactorfirst.plugin:refactor-first-maven-plugin:0.5.0-M1:report ``` ### As Part of a Build @@ -24,7 +28,7 @@ Add the following to your project in the build section. **showDetails** will sh org.hjug.refactorfirst.plugin refactor-first-maven-plugin - 0.5.0 + 0.5.0-M1 true @@ -44,7 +48,7 @@ Add the following to your project in the reports section. org.hjug.refactorfirst.plugin refactor-first-maven-plugin - 0.5.0 + 0.5.0-M1 ... @@ -75,8 +79,9 @@ you will need to add the following to your pom.xml: ### As an HTML Report ```bash -mvn org.hjug.refactorfirst.plugin:refactor-first-maven-plugin:0.5.0:htmlReport +mvn org.hjug.refactorfirst.plugin:refactor-first-maven-plugin:0.5.0-M1:htmlReport ``` +View the report at ```target/site/refactor-first-report.html``` ## But I'm using Gradle / my project layout isn't typical! I would like to create a Gradle plugin and (possibly) support non-conventional projects in the future, but in the meantime you can create a dummy POM file in the same directory as your .git directory: @@ -94,10 +99,11 @@ I would like to create a Gradle plugin and (possibly) support non-conventional p and then (assuming Maven is installed) run ```bash -mvn org.hjug.refactorfirst.plugin:refactor-first-maven-plugin:0.4.0:report +mvn org.hjug.refactorfirst.plugin:refactor-first-maven-plugin:0.5.0-M1:htmlReport ``` ## Viewing the Report +View the report at ```target/site/refactor-first-report.html``` Once the plugin finishes executing (it may take a while for a large / old codebase), open the file **target/site/refactor-first-report.html** in the root of the project. It will contain a graph similar to the one above, and a table that lists God classes in the recommended order that they should be refactored. The classes in the top left of the graph are the easiest to refactor while also having the biggest positive impact to team productivity. If highly coupled classes are detected, a graph and table listing Highly Coupled Classes in will be generated. diff --git a/change-proneness-ranker/pom.xml b/change-proneness-ranker/pom.xml index 13c137d0..542a0251 100644 --- a/change-proneness-ranker/pom.xml +++ b/change-proneness-ranker/pom.xml @@ -5,7 +5,7 @@ org.hjug.refactorfirst refactor-first - 0.4.1-SNAPSHOT + 0.5.0-M1-SNAPSHOT org.hjug.refactorfirst.changepronenessranker diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 00000000..5ff6309b --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/cli/pom.xml b/cli/pom.xml new file mode 100644 index 00000000..230ff499 --- /dev/null +++ b/cli/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + org.hjug.refactorfirst + refactor-first + 0.5.0-M1-SNAPSHOT + + + jar + + org.hjug.refactorfirst.report + cli + + + 11 + 11 + UTF-8 + + + + + info.picocli + picocli + 4.7.4 + + + org.hjug.refactorfirst.report + report + + + org.slf4j + slf4j-simple + 2.0.7 + + + org.apache.maven + maven-core + 3.9.2 + compile + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.0 + + + package + + shade + + + + + org.hjug.refactorfirst.Main + + + + + + + + + org.skife.maven + really-executable-jar-maven-plugin + 2.1.1 + + rf + + + + package + + really-executable-jar + + + + + + + + \ No newline at end of file diff --git a/cli/src/main/java/org/hjug/refactorfirst/Main.java b/cli/src/main/java/org/hjug/refactorfirst/Main.java new file mode 100644 index 00000000..4b8b881a --- /dev/null +++ b/cli/src/main/java/org/hjug/refactorfirst/Main.java @@ -0,0 +1,12 @@ +package org.hjug.refactorfirst; + +import picocli.CommandLine; + +public class Main { + public static void main(String[] args) { + int exitCode = new CommandLine(new ReportCommand()) + .setCaseInsensitiveEnumValuesAllowed(true) + .execute(args); + System.exit(exitCode); + } +} diff --git a/cli/src/main/java/org/hjug/refactorfirst/ReportCommand.java b/cli/src/main/java/org/hjug/refactorfirst/ReportCommand.java new file mode 100644 index 00000000..219c06b0 --- /dev/null +++ b/cli/src/main/java/org/hjug/refactorfirst/ReportCommand.java @@ -0,0 +1,118 @@ +package org.hjug.refactorfirst; + +import static picocli.CommandLine.Option; + +import java.io.File; +import java.io.FileReader; +import java.util.concurrent.Callable; +import lombok.extern.slf4j.Slf4j; +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.project.MavenProject; +import org.hjug.refactorfirst.report.CsvReport; +import org.hjug.refactorfirst.report.HtmlReport; +import org.hjug.refactorfirst.report.json.JsonReportExecutor; +import picocli.CommandLine.Command; + +@Command(mixinStandardHelpOptions = true, description = "Generate a report") +@Slf4j +public class ReportCommand implements Callable { + + @Option( + names = {"-d", "--details"}, + description = "Show detailed report") + private boolean showDetails; + + @Option( + names = {"-p", "--project"}, + description = "Project name") + private String projectName; + + @Option( + names = {"-v", "--version"}, + description = "Project version") + private String projectVersion; + + @Option( + names = {"-o", "--output"}, + defaultValue = ".", + description = "Output directory") + private String outputDirectory; + + @Option( + names = {"-b", "--base-dir"}, + defaultValue = ".", + description = "Base directory of the project") + private File baseDir; + + @Option( + names = {"-t", "--type"}, + description = "Report type: ${COMPLETION-CANDIDATES}", + defaultValue = "HTML") + private ReportType reportType; + + @Override + public Integer call() { + + // TODO: add support for inferring arguments from gradle properties + inferArgumentsFromMavenProject(); + populateDefaultArguments(); + switch (reportType) { + case HTML: + HtmlReport htmlReport = new HtmlReport(); + htmlReport.execute(showDetails, projectName, projectVersion, outputDirectory, baseDir); + return 0; + case JSON: + JsonReportExecutor jsonReportExecutor = new JsonReportExecutor(); + jsonReportExecutor.execute(baseDir, outputDirectory); + return 0; + case CSV: + CsvReport csvReport = new CsvReport(); + csvReport.execute(showDetails, projectName, projectVersion, outputDirectory, baseDir); + return 0; + } + + return 0; + } + + private void populateDefaultArguments() { + if (projectName == null || projectName.isEmpty()) { + projectName = "my-project"; + } + if (projectVersion == null || projectVersion.isEmpty()) { + projectVersion = "0.0.0"; + } + } + + private void inferArgumentsFromMavenProject() { + if (baseDir.isDirectory()) { + File[] potentialPomFiles = baseDir.listFiles(f -> f.getName().equals("pom.xml")); + File pomFile = null; + if (potentialPomFiles != null && potentialPomFiles.length > 0) { + pomFile = potentialPomFiles[0]; + } + if (pomFile != null) { + Model model; + FileReader reader; + MavenXpp3Reader mavenreader = new MavenXpp3Reader(); + try { + reader = new FileReader(pomFile); + model = mavenreader.read(reader); + model.setPomFile(pomFile); + } catch (Exception ex) { + log.info("Unable to infer arguments from pom file"); + return; + } + MavenProject project = new MavenProject(model); + + // only override project name and version if they are not set + if (projectName == null || projectName.isEmpty()) { + projectName = project.getName(); + } + if (projectVersion == null || projectVersion.isEmpty()) { + projectVersion = project.getVersion(); + } + } + } + } +} diff --git a/cli/src/main/java/org/hjug/refactorfirst/ReportType.java b/cli/src/main/java/org/hjug/refactorfirst/ReportType.java new file mode 100644 index 00000000..563e0f70 --- /dev/null +++ b/cli/src/main/java/org/hjug/refactorfirst/ReportType.java @@ -0,0 +1,7 @@ +package org.hjug.refactorfirst; + +public enum ReportType { + HTML, + JSON, + CSV; +} diff --git a/cost-benefit-calculator/pom.xml b/cost-benefit-calculator/pom.xml index 2bc9c5c0..3d8da12e 100644 --- a/cost-benefit-calculator/pom.xml +++ b/cost-benefit-calculator/pom.xml @@ -5,7 +5,7 @@ org.hjug.refactorfirst refactor-first - 0.4.1-SNAPSHOT + 0.5.0-M1-SNAPSHOT org.hjug.refactorfirst.costbenefitcalculator diff --git a/coverage/pom.xml b/coverage/pom.xml index f6fdfed9..4b1b71e3 100644 --- a/coverage/pom.xml +++ b/coverage/pom.xml @@ -7,7 +7,7 @@ org.hjug.refactorfirst refactor-first - 0.4.1-SNAPSHOT + 0.5.0-M1-SNAPSHOT coverage diff --git a/effort-ranker/pom.xml b/effort-ranker/pom.xml index c6f819c8..4a6e1273 100644 --- a/effort-ranker/pom.xml +++ b/effort-ranker/pom.xml @@ -5,7 +5,7 @@ org.hjug.refactorfirst refactor-first - 0.4.1-SNAPSHOT + 0.5.0-M1-SNAPSHOT org.hjug.refactorfirst.effortranker @@ -20,7 +20,7 @@ org.hjug.refactorfirst.testresources test-resources - 0.4.1-SNAPSHOT + 0.5.0-M1-SNAPSHOT diff --git a/graph-data-generator/pom.xml b/graph-data-generator/pom.xml index e2309fb6..b993e84a 100644 --- a/graph-data-generator/pom.xml +++ b/graph-data-generator/pom.xml @@ -5,7 +5,7 @@ org.hjug.refactorfirst refactor-first - 0.4.1-SNAPSHOT + 0.5.0-M1-SNAPSHOT org.hjug.refactorfirst.graphdatagenerator @@ -15,7 +15,7 @@ org.hjug.refactorfirst.costbenefitcalculator cost-benefit-calculator - 0.4.1-SNAPSHOT + 0.5.0-M1-SNAPSHOT diff --git a/pom.xml b/pom.xml index caf7ed92..1346ed7f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.hjug.refactorfirst refactor-first - 0.4.1-SNAPSHOT + 0.5.0-M1-SNAPSHOT pom https://github.com/jimbethancourt/RefactorFirst @@ -51,15 +51,12 @@ UTF-8 - 1.8 - 1.8 - - - 9+181-r4173-1 + 11 + 11 - 2.10.0 - 1.18.28 + + 1.18.30 4.0.0 4.0.3 @@ -84,6 +81,8 @@ refactor-first-maven-plugin coverage + report + cli @@ -118,6 +117,12 @@ ${project.version} + + org.hjug.refactorfirst.report + report + ${project.version} + + org.hjug.refactorfirst.testresources test-resources @@ -128,14 +133,14 @@ org.eclipse.jgit org.eclipse.jgit - 5.10.0.202012080955-r + 6.7.0.202309050840-r compile net.sourceforge.pmd pmd-java - 6.46.0 + 6.55.0 compile @@ -208,25 +213,24 @@ org.apache.maven.plugins maven-compiler-plugin 3.8.1 - - 8 - 8 - - -XDcompilePolicy=simple - -Xplugin:ErrorProne - - - - org.projectlombok - lombok - ${lombok.version} - - + + + -XDcompilePolicy=simple + + + + + org.projectlombok + lombok + ${lombok.version} + + + + 11 @@ -370,30 +374,7 @@ - - - jdk8 - - 1.8 - - - - - org.apache.maven.plugins - maven-compiler-plugin - - true - - - -J-Xbootclasspath/p:${settings.localRepository}/com/google/errorprone/javac/${javac.version}/javac-${javac.version}.jar - - - - - - - snapshot-release diff --git a/refactor-first-gradle-plugin/build.gradle b/refactor-first-gradle-plugin/build.gradle index d5703a9f..34cc3076 100644 --- a/refactor-first-gradle-plugin/build.gradle +++ b/refactor-first-gradle-plugin/build.gradle @@ -12,7 +12,7 @@ repositories { dependencies { compileOnly gradleApi() - api "org.hjug.refactorfirst.graphdatagenerator:graph-data-generator:${version}" +// api "org.hjug.refactorfirst.graphdatagenerator:graph-data-generator:${version}" } pluginBundle { diff --git a/refactor-first-maven-plugin/pom.xml b/refactor-first-maven-plugin/pom.xml index 79403830..ede281e5 100644 --- a/refactor-first-maven-plugin/pom.xml +++ b/refactor-first-maven-plugin/pom.xml @@ -5,7 +5,7 @@ org.hjug.refactorfirst refactor-first - 0.4.1-SNAPSHOT + 0.5.0-M1-SNAPSHOT org.hjug.refactorfirst.plugin @@ -18,6 +18,10 @@ graph-data-generator + + org.hjug.refactorfirst.report + report + diff --git a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenCsvReport.java b/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenCsvReport.java index f8272675..08bf21fc 100644 --- a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenCsvReport.java +++ b/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenCsvReport.java @@ -1,25 +1,14 @@ package org.hjug.mavenreport; -import static org.hjug.mavenreport.ReportWriter.writeReportToDisk; - import java.io.File; -import java.nio.file.Paths; -import java.time.Instant; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.*; -import lombok.extern.slf4j.Slf4j; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; -import org.hjug.cbc.CostBenefitCalculator; -import org.hjug.cbc.RankedDisharmony; -import org.hjug.git.GitLogReader; +import org.hjug.refactorfirst.report.CsvReport; -@Slf4j @Mojo( name = "csvreport", defaultPhase = LifecyclePhase.SITE, @@ -44,209 +33,17 @@ public class RefactorFirstMavenCsvReport extends AbstractMojo { @Parameter(property = "project.build.directory") protected File outputDirectory; - public String getOutputNamePrefix() { - // This report will generate simple-report.html when invoked in a project with `mvn site` - return "RefFirst"; - } - - public String getName(Locale locale) { - // Name of the report when listed in the project-reports.html page of a project - return "Refactor First Report data"; - } - - public String getDescription(Locale locale) { - // Description of the report when listed in the project-reports.html page of a project - return "DRACO Ranks the disharmonies in a codebase. The classes that should be refactored first " - + " have the highest priority values."; - } - @Override public void execute() { - StringBuilder fileNameSB = new StringBuilder(); - String publishedDate = createFileDateTimeFormatter().format(Instant.now()); - - fileNameSB - .append(getOutputNamePrefix()) - .append("_P") - .append(project.getArtifactId()) - .append("_PV") - .append(project.getVersion()) - .append("_PD") - .append(publishedDate) - .append(".csv"); - String filename = fileNameSB.toString(); - - if (Objects.equals(project.getName(), "Maven Stub Project (No POM)")) { - projectName = new File(Paths.get("").toAbsolutePath().toString()).getName(); - } - - log.info("Generating {} for {} - {} date: {}", filename, projectName, projectVersion, publishedDate); - - StringBuilder contentBuilder = new StringBuilder(); - - // git management - GitLogReader gitLogReader = new GitLogReader(); - String projectBaseDir; - Optional optionalGitDir; - - File baseDir = project.getBasedir(); - if (baseDir != null) { - projectBaseDir = baseDir.getPath(); - optionalGitDir = Optional.ofNullable(gitLogReader.getGitDir(baseDir)); - } else { - projectBaseDir = Paths.get("").toAbsolutePath().toString(); - optionalGitDir = Optional.ofNullable(gitLogReader.getGitDir(new File(projectBaseDir))); - } - - File gitDir; - if (optionalGitDir.isPresent()) { - gitDir = optionalGitDir.get(); - } else { - log.info( - "Done! No Git repository found! Please initialize a Git repository and perform an initial commit."); - contentBuilder - .append("No Git repository found in project ") - .append(projectName) - .append(" ") - .append(projectVersion) - .append(". "); - contentBuilder.append("Please initialize a Git repository and perform an initial commit."); - writeReportToDisk(project, filename, contentBuilder); - return; - } - - String parentOfGitDir = gitDir.getParentFile().getPath(); - log.info("Project Base Dir: {} ", projectBaseDir); - log.info("Parent of Git Dir: {}", parentOfGitDir); - - if (!projectBaseDir.equals(parentOfGitDir)) { - log.warn("Project Base Directory does not match Git Parent Directory"); - contentBuilder.append("Project Base Directory does not match Git Parent Directory. " - + "Please refer to the report at the root of the site directory."); - return; - } - - // actual calcualte - CostBenefitCalculator costBenefitCalculator = new CostBenefitCalculator(); - List rankedDisharmonies = - costBenefitCalculator.calculateGodClassCostBenefitValues(projectBaseDir); - - rankedDisharmonies.sort( - Comparator.comparing(RankedDisharmony::getRawPriority).reversed()); - - // perfect score: no god classes - if (rankedDisharmonies.isEmpty()) { - contentBuilder - .append("Congratulations! ") - .append(projectName) - .append(" ") - .append(projectVersion) - .append(" has no God classes!"); - log.info("Done! No God classes found!"); - - writeReportToDisk(project, filename, contentBuilder); - return; - } - - // create Content - // header - final String[] tableHeadings = getHeaderList(); - addsRow(contentBuilder, tableHeadings); - contentBuilder.append("\n"); - // rows - for (RankedDisharmony rankedDisharmony : rankedDisharmonies) { - final String[] rankedDisharmonyData = getDataList(rankedDisharmony); - - contentBuilder.append(project.getVersion()).append(","); - addsRow(contentBuilder, rankedDisharmonyData); - contentBuilder.append("eol" + "\n"); - } - - log.info(contentBuilder.toString()); - - writeReportToDisk(project, filename, contentBuilder); - } - - private DateTimeFormatter createFileDateTimeFormatter() { - return DateTimeFormatter.ofPattern("yyyyMMddhhmm") - .withLocale(Locale.getDefault()) - .withZone(ZoneId.systemDefault()); - } - - private DateTimeFormatter createCsvDateTimeFormatter() { - return DateTimeFormatter.ISO_LOCAL_DATE_TIME - .withLocale(Locale.getDefault()) - .withZone(ZoneId.systemDefault()); - } - - private String[] getDataList(RankedDisharmony rankedDisharmony) { - String[] simpleRankedDisharmonyData = { - rankedDisharmony.getFileName(), - rankedDisharmony.getRawPriority().toString(), - rankedDisharmony.getChangePronenessRank().toString(), - rankedDisharmony.getEffortRank().toString(), - rankedDisharmony.getWmc().toString(), - createCsvDateTimeFormatter().format(rankedDisharmony.getMostRecentCommitTime()), - rankedDisharmony.getCommitCount().toString() - }; - - String[] detailedRankedDisharmonyData = { - rankedDisharmony.getFileName(), - rankedDisharmony.getRawPriority().toString(), - rankedDisharmony.getChangePronenessRank().toString(), - rankedDisharmony.getEffortRank().toString(), - rankedDisharmony.getWmc().toString(), - rankedDisharmony.getWmcRank().toString(), - rankedDisharmony.getAtfd().toString(), - rankedDisharmony.getAtfdRank().toString(), - rankedDisharmony.getTcc().toString(), - rankedDisharmony.getTccRank().toString(), - createCsvDateTimeFormatter().format(rankedDisharmony.getFirstCommitTime()), - createCsvDateTimeFormatter().format(rankedDisharmony.getMostRecentCommitTime()), - rankedDisharmony.getCommitCount().toString(), - rankedDisharmony.getPath() - }; - - return showDetails ? detailedRankedDisharmonyData : simpleRankedDisharmonyData; - } - - private String[] getHeaderList() { - - final String[] simpleTableHeadings = { - "Ver", - "Class", - "Priority", - "Change Proneness Rank", - "Effort Rank", - "Method Count", - "Most Recent Commit Date", - "Commit Count" - }; - - final String[] detailedTableHeadings = { - "Ver", - "Class", - "Priority", - "Change Proneness Rank", - "Effort Rank", - "WMC", - "WMC Rank", - "ATFD", - "ATFD Rank", - "TCC", - "TCC Rank", - "Date of First Commit", - "Most Recent Commit Date", - "Commit Count", - "Full Path" - }; - - return showDetails ? detailedTableHeadings : simpleTableHeadings; - } - - private void addsRow(StringBuilder contentBuilder, String[] rankedDisharmonyData) { - for (String rowData : rankedDisharmonyData) { - contentBuilder.append(rowData).append(","); - } + CsvReport csvReport = new CsvReport(); + csvReport.execute( + showDetails, + projectName, + projectVersion, + project.getModel() + .getReporting() + .getOutputDirectory() + .replace("${project.basedir}" + File.separator, ""), + project.getBasedir()); } } diff --git a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenJsonReport.java b/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenJsonReport.java new file mode 100644 index 00000000..c84f2943 --- /dev/null +++ b/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenJsonReport.java @@ -0,0 +1,38 @@ +package org.hjug.mavenreport; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.hjug.refactorfirst.report.json.JsonReportExecutor; + +@Mojo( + name = "jsonreport", + defaultPhase = LifecyclePhase.SITE, + requiresDependencyResolution = ResolutionScope.RUNTIME, + requiresProject = false, + threadSafe = true, + inheritByDefault = false) +public class RefactorFirstMavenJsonReport extends AbstractMojo { + private static final String FILE_NAME = "refactor-first-data.json"; + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Parameter(readonly = true, defaultValue = "${project}") + private MavenProject project; + + @Override + public void execute() { + JsonReportExecutor jsonReportExecutor = new JsonReportExecutor(); + jsonReportExecutor.execute( + project.getBasedir(), + project.getModel() + .getReporting() + .getOutputDirectory() + .replace("${project.basedir}" + File.separator, "")); + } +} diff --git a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenReport.java b/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenReport.java index 140d0aaa..864b7fc7 100644 --- a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenReport.java +++ b/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstMavenReport.java @@ -1,17 +1,6 @@ package org.hjug.mavenreport; -import static org.hjug.mavenreport.ReportWriter.writeReportToDisk; - -import java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.Paths; -import java.time.Instant; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; -import java.util.*; import lombok.extern.slf4j.Slf4j; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -19,10 +8,7 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; -import org.hjug.cbc.CostBenefitCalculator; -import org.hjug.cbc.RankedDisharmony; -import org.hjug.gdg.GraphDataGenerator; -import org.hjug.git.GitLogReader; +import org.hjug.refactorfirst.report.HtmlReport; @Slf4j @Mojo( @@ -34,49 +20,6 @@ inheritByDefault = false) public class RefactorFirstMavenReport extends AbstractMojo { - private static final String THE_BEGINNING = - "\n" + " \n" - + " \n" - + " \n" - + " "; - - private static final String GOD_CLASS_CHART_LEGEND = - "

God Class Chart Legend:

" + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "
X-Axis: Effort to refactor to a non-God class
Y-Axis: Relative churn
Color: Priority of what to fix first
Circle size: Priority (Visual) of what to fix first
" - + "
"; - - private static final String COUPLING_BETWEEN_OBJECT_CHART_LEGEND = - "

Coupling Between Objects Chart Legend:

" + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "
X-Axis: Number of objects the class is coupled to
Y-Axis: Relative churn
Color: Priority of what to fix first
Circle size: Priority (Visual) of what to fix first
" - + "
"; - - private static final String THE_END = "\n" + " \n" - + "
\n" - + "
\n" - + "
\n" - + "
\n" - + "
\n" - + " Copyright © 2002–2021The Apache Software Foundation.\n" - + ".
\n" - + "
\n" - + "
\n" - + "
\n" - + "
\n" - + " \n" - + "\n"; - @Parameter(property = "showDetails") private boolean showDetails = false; @@ -92,381 +35,19 @@ public class RefactorFirstMavenReport extends AbstractMojo { @Parameter(property = "project.build.directory") protected File outputDirectory; - public String getOutputName() { - // This report will generate simple-report.html when invoked in a project with `mvn site` - return "refactor-first-report"; - } - - public String getName(Locale locale) { - // Name of the report when listed in the project-reports.html page of a project - return "Refactor First Report"; - } - - public String getDescription(Locale locale) { - // Description of the report when listed in the project-reports.html page of a project - return "Ranks the disharmonies in a codebase. The classes that should be refactored first " - + " have the highest priority values."; - } - @Override public void execute() { - final String[] godClassSimpleTableHeadings = { - "Class", - "Priority", - "Change Proneness Rank", - "Effort Rank", - "Method Count", - "Most Recent Commit Date", - "Commit Count" - }; - - final String[] godClassDetailedTableHeadings = { - "Class", - "Priority", - "Raw Priority", - "Change Proneness Rank", - "Effort Rank", - "WMC", - "WMC Rank", - "ATFD", - "ATFD Rank", - "TCC", - "TCC Rank", - "Date of First Commit", - "Most Recent Commit Date", - "Commit Count", - "Full Path" - }; - - final String[] cboTableHeadings = { - "Class", "Priority", "Change Proneness Rank", "Coupling Count", "Most Recent Commit Date", "Commit Count" - }; - - final String[] godClassTableHeadings = - showDetails ? godClassDetailedTableHeadings : godClassSimpleTableHeadings; - - String filename = getOutputName() + ".html"; - - if (Objects.equals(project.getName(), "Maven Stub Project (No POM)")) { - projectName = new File(Paths.get("").toAbsolutePath().toString()).getName(); - } - - log.info("Generating {} for {} - {}", filename, projectName, projectVersion); - - DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) - .withLocale(Locale.getDefault()) - .withZone(ZoneId.systemDefault()); - - StringBuilder stringBuilder = new StringBuilder(); - - stringBuilder.append(THE_BEGINNING); - - stringBuilder - .append("Refactor First Report for ") - .append(projectName) - .append(" ") - .append(projectVersion) - .append(" "); - - stringBuilder.append("\n" - + " \n" - + " \n" - + " \n" - + "" - + "" - + " \n" - + " \n" - + "
\n" - + "
\n" - + "
\n" - + "
\n" - + "
\n" - + "
\n" - + "
"); - - stringBuilder - .append("Last Published: ") - .append(formatter.format(Instant.now())) - .append(""); - stringBuilder - .append(" Version: ") - .append(projectVersion) - .append(""); - - stringBuilder.append("
\n" + "
\n" - + "
\n" - + "
\n" - + "
\n" - + "
\n" - + "
\n" - + "
"); - - stringBuilder - .append("
\n" + "

RefactorFirst Report for ") - .append(projectName) - .append(" ") - .append(projectVersion) - .append("

\n"); - - GitLogReader gitLogReader = new GitLogReader(); - String projectBaseDir; - Optional optionalGitDir; - - File baseDir = project.getBasedir(); - if (baseDir != null) { - projectBaseDir = baseDir.getPath(); - optionalGitDir = Optional.ofNullable(gitLogReader.getGitDir(baseDir)); - } else { - projectBaseDir = Paths.get("").toAbsolutePath().toString(); - optionalGitDir = Optional.ofNullable(gitLogReader.getGitDir(new File(projectBaseDir))); - } - - File gitDir; - if (optionalGitDir.isPresent()) { - gitDir = optionalGitDir.get(); - } else { - log.info( - "Done! No Git repository found! Please initialize a Git repository and perform an initial commit."); - stringBuilder - .append("No Git repository found in project ") - .append(projectName) - .append(" ") - .append(projectVersion) - .append(". "); - stringBuilder.append("Please initialize a Git repository and perform an initial commit."); - stringBuilder.append(THE_END); - writeReportToDisk(project, filename, stringBuilder); - return; - } - - String parentOfGitDir = gitDir.getParentFile().getPath(); - log.info("Project Base Dir: {} ", projectBaseDir); - log.info("Parent of Git Dir: {}", parentOfGitDir); - - if (!projectBaseDir.equals(parentOfGitDir)) { - log.warn("Project Base Directory does not match Git Parent Directory"); - stringBuilder.append("Project Base Directory does not match Git Parent Directory. " - + "Please refer to the report at the root of the site directory."); - stringBuilder.append(THE_END); - return; - } - - CostBenefitCalculator costBenefitCalculator = new CostBenefitCalculator(); - List rankedGodClassDisharmonies = - costBenefitCalculator.calculateGodClassCostBenefitValues(projectBaseDir); - - List rankedCBODisharmonies = - costBenefitCalculator.calculateCBOCostBenefitValues(projectBaseDir); - - if (rankedGodClassDisharmonies.isEmpty() && rankedCBODisharmonies.isEmpty()) { - stringBuilder - .append("Congratulations! ") - .append(projectName) - .append(" ") - .append(projectVersion) - .append(" has no God classes or highly coupled classes!"); - log.info("Done! No Disharmonies found!"); - stringBuilder.append(THE_END); - writeReportToDisk(project, filename, stringBuilder); - return; - } - - if (!rankedGodClassDisharmonies.isEmpty() && !rankedCBODisharmonies.isEmpty()) { - stringBuilder.append("God Classes"); - stringBuilder.append("
"); - stringBuilder.append("Highly Coupled Classes"); - } - - if (!rankedGodClassDisharmonies.isEmpty()) { - rankedGodClassDisharmonies.sort( - Comparator.comparing(RankedDisharmony::getRawPriority).reversed()); - - int godClassPriority = 1; - for (RankedDisharmony rankedGodClassDisharmony : rankedGodClassDisharmonies) { - rankedGodClassDisharmony.setPriority(godClassPriority++); - } - - stringBuilder.append(""); - - writeGodClassGchartJs(rankedGodClassDisharmonies, godClassPriority - 1); - stringBuilder.append("
"); - stringBuilder.append(GOD_CLASS_CHART_LEGEND); - - stringBuilder.append("

God classes by the numbers: (Refactor Starting with Priority 1)

"); - stringBuilder.append(""); - - // Content - stringBuilder.append(""); - for (String heading : godClassTableHeadings) { - stringBuilder.append(""); - } - stringBuilder.append(""); - - stringBuilder.append(""); - for (RankedDisharmony rankedGodClassDisharmony : rankedGodClassDisharmonies) { - stringBuilder.append(""); - - String[] simpleRankedGodClassDisharmonyData = { - rankedGodClassDisharmony.getFileName(), - rankedGodClassDisharmony.getPriority().toString(), - rankedGodClassDisharmony.getChangePronenessRank().toString(), - rankedGodClassDisharmony.getEffortRank().toString(), - rankedGodClassDisharmony.getWmc().toString(), - formatter.format(rankedGodClassDisharmony.getMostRecentCommitTime()), - rankedGodClassDisharmony.getCommitCount().toString() - }; - - String[] detailedRankedGodClassDisharmonyData = { - rankedGodClassDisharmony.getFileName(), - rankedGodClassDisharmony.getPriority().toString(), - rankedGodClassDisharmony.getRawPriority().toString(), - rankedGodClassDisharmony.getChangePronenessRank().toString(), - rankedGodClassDisharmony.getEffortRank().toString(), - rankedGodClassDisharmony.getWmc().toString(), - rankedGodClassDisharmony.getWmcRank().toString(), - rankedGodClassDisharmony.getAtfd().toString(), - rankedGodClassDisharmony.getAtfdRank().toString(), - rankedGodClassDisharmony.getTcc().toString(), - rankedGodClassDisharmony.getTccRank().toString(), - formatter.format(rankedGodClassDisharmony.getFirstCommitTime()), - formatter.format(rankedGodClassDisharmony.getMostRecentCommitTime()), - rankedGodClassDisharmony.getCommitCount().toString(), - rankedGodClassDisharmony.getPath() - }; - - final String[] rankedDisharmonyData = - showDetails ? detailedRankedGodClassDisharmonyData : simpleRankedGodClassDisharmonyData; - - for (String rowData : rankedDisharmonyData) { - stringBuilder.append(""); - } - - stringBuilder.append(""); - } - - stringBuilder.append(""); - stringBuilder.append("
").append(heading).append("
").append(rowData).append("
"); - } - - if (!rankedCBODisharmonies.isEmpty()) { - - stringBuilder.append("
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
"); - - rankedCBODisharmonies.sort( - Comparator.comparing(RankedDisharmony::getRawPriority).reversed()); - - int cboPriority = 1; - for (RankedDisharmony rankedCBODisharmony : rankedCBODisharmonies) { - rankedCBODisharmony.setPriority(cboPriority++); - } - - stringBuilder.append( - ""); - - stringBuilder.append("
"); - writeGCBOGchartJs(rankedCBODisharmonies, cboPriority - 1); - stringBuilder.append(COUPLING_BETWEEN_OBJECT_CHART_LEGEND); - - stringBuilder.append("

Highly Coupled classes by the numbers: (Refactor starting with Priority 1)

"); - stringBuilder.append(""); - - // Content - stringBuilder.append(""); - for (String heading : cboTableHeadings) { - stringBuilder.append(""); - } - stringBuilder.append(""); - - stringBuilder.append(""); - for (RankedDisharmony rankedCboClassDisharmony : rankedCBODisharmonies) { - stringBuilder.append(""); - - String[] rankedCboClassDisharmonyData = { - rankedCboClassDisharmony.getFileName(), - rankedCboClassDisharmony.getPriority().toString(), - rankedCboClassDisharmony.getChangePronenessRank().toString(), - rankedCboClassDisharmony.getEffortRank().toString(), - formatter.format(rankedCboClassDisharmony.getMostRecentCommitTime()), - rankedCboClassDisharmony.getCommitCount().toString() - }; - - for (String rowData : rankedCboClassDisharmonyData) { - stringBuilder.append(""); - } - - stringBuilder.append(""); - } - - stringBuilder.append(""); - } - - stringBuilder.append("
").append(heading).append("
").append(rowData).append("
"); - stringBuilder.append(THE_END); - - log.debug(stringBuilder.toString()); - - writeReportToDisk(project, filename, stringBuilder); - log.info("Done! View the report at target/site/{}", filename); - } - - // TODO: Move to another class to allow use by Gradle plugin - void writeGodClassGchartJs(List rankedDisharmonies, int maxPriority) { - GraphDataGenerator graphDataGenerator = new GraphDataGenerator(); - String scriptStart = graphDataGenerator.getGodClassScriptStart(); - String bubbleChartData = graphDataGenerator.generateGodClassBubbleChartData(rankedDisharmonies, maxPriority); - String scriptEnd = graphDataGenerator.getGodClassScriptEnd(); - - String javascriptCode = scriptStart + bubbleChartData + scriptEnd; - - String reportOutputDirectory = project.getModel().getReporting().getOutputDirectory(); - File reportOutputDir = new File(reportOutputDirectory); - if (!reportOutputDir.exists()) { - reportOutputDir.mkdirs(); - } - String pathname = reportOutputDirectory + File.separator + "gchart.js"; - - File scriptFile = new File(pathname); - try { - scriptFile.createNewFile(); - } catch (IOException e) { - log.error("Failure creating God Class chart script file", e); - } - - try (BufferedWriter writer = new BufferedWriter(new FileWriter(scriptFile))) { - writer.write(javascriptCode); - } catch (IOException e) { - log.error("Error writing chart script file", e); - } - } - - void writeGCBOGchartJs(List rankedDisharmonies, int maxPriority) { - GraphDataGenerator graphDataGenerator = new GraphDataGenerator(); - String scriptStart = graphDataGenerator.getCBOScriptStart(); - String bubbleChartData = graphDataGenerator.generateCBOBubbleChartData(rankedDisharmonies, maxPriority); - String scriptEnd = graphDataGenerator.getCBOScriptEnd(); - - String javascriptCode = scriptStart + bubbleChartData + scriptEnd; - - String reportOutputDirectory = project.getModel().getReporting().getOutputDirectory(); - File reportOutputDir = new File(reportOutputDirectory); - if (!reportOutputDir.exists()) { - reportOutputDir.mkdirs(); - } - String pathname = reportOutputDirectory + File.separator + "gchart2.js"; - - File scriptFile = new File(pathname); - try { - scriptFile.createNewFile(); - } catch (IOException e) { - log.error("Failure creating CBO chart script file", e); - } - - try (BufferedWriter writer = new BufferedWriter(new FileWriter(scriptFile))) { - writer.write(javascriptCode); - } catch (IOException e) { - log.error("Error writing CBO chart script file", e); - } + log.info(outputDirectory.getPath()); + HtmlReport htmlReport = new HtmlReport(); + htmlReport.execute( + showDetails, + projectName, + projectVersion, + project.getModel() + .getReporting() + .getOutputDirectory() + .replace("${project.basedir}" + File.separator, ""), + project.getBasedir()); } } diff --git a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstRealMavenReport.java b/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstRealMavenReport.java index 25484961..66495b90 100644 --- a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstRealMavenReport.java +++ b/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstRealMavenReport.java @@ -126,7 +126,6 @@ public void executeReport(Locale locale) throws MavenReportException { /** * @See https://maven.apache.org/doxia/developers/sink.html#How_to_inject_javascript_code_into_HTML */ - SinkEventAttributeSet githubButtonJS = new SinkEventAttributeSet(); // githubButtonJS.addAttribute(SinkEventAttributes.TYPE, "text/javascript"); // githubButtonJS.addAttribute("async", ""); @@ -221,6 +220,7 @@ public void executeReport(Locale locale) throws MavenReportException { mainSink.text("Contratulations! " + projectName + " " + projectVersion + " has no God classes or highly coupled classes!"); mainSink.section1_(); + renderGitHubButtons(mainSink); mainSink.body_(); log.info("Done! No Disharmonies found!"); return; diff --git a/report/.gitignore b/report/.gitignore new file mode 100644 index 00000000..5ff6309b --- /dev/null +++ b/report/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/report/pom.xml b/report/pom.xml new file mode 100644 index 00000000..19e38d43 --- /dev/null +++ b/report/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + org.hjug.refactorfirst + refactor-first + 0.5.0-M1-SNAPSHOT + + + org.hjug.refactorfirst.report + report + + + + org.hjug.refactorfirst.graphdatagenerator + graph-data-generator + + + com.fasterxml.jackson.core + jackson-databind + + + + \ No newline at end of file diff --git a/report/src/main/java/org/hjug/refactorfirst/report/CsvReport.java b/report/src/main/java/org/hjug/refactorfirst/report/CsvReport.java new file mode 100644 index 00000000..29e99eba --- /dev/null +++ b/report/src/main/java/org/hjug/refactorfirst/report/CsvReport.java @@ -0,0 +1,223 @@ +package org.hjug.refactorfirst.report; + +import static org.hjug.refactorfirst.report.ReportWriter.writeReportToDisk; + +import java.io.File; +import java.nio.file.Paths; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; +import lombok.extern.slf4j.Slf4j; +import org.hjug.cbc.CostBenefitCalculator; +import org.hjug.cbc.RankedDisharmony; +import org.hjug.git.GitLogReader; + +@Slf4j +public class CsvReport { + + public void execute( + boolean showDetails, String projectName, String projectVersion, String outputDirectory, File baseDir) { + StringBuilder fileNameSB = new StringBuilder(); + String publishedDate = createFileDateTimeFormatter().format(Instant.now()); + + fileNameSB + .append(getOutputNamePrefix()) + .append("_P") + .append(projectName) + .append("_PV") + .append(projectVersion) + .append("_PD") + .append(publishedDate) + .append(".csv"); + String filename = fileNameSB.toString(); + + if (Objects.equals(projectName, "Maven Stub Project (No POM)")) { + projectName = new File(Paths.get("").toAbsolutePath().toString()).getName(); + } + + log.info("Generating {} for {} - {} date: {}", filename, projectName, projectVersion, publishedDate); + + StringBuilder contentBuilder = new StringBuilder(); + + // git management + GitLogReader gitLogReader = new GitLogReader(); + String projectBaseDir; + Optional optionalGitDir; + + if (baseDir != null) { + projectBaseDir = baseDir.getPath(); + optionalGitDir = Optional.ofNullable(gitLogReader.getGitDir(baseDir)); + } else { + projectBaseDir = Paths.get("").toAbsolutePath().toString(); + optionalGitDir = Optional.ofNullable(gitLogReader.getGitDir(new File(projectBaseDir))); + } + + File gitDir; + if (optionalGitDir.isPresent()) { + gitDir = optionalGitDir.get(); + } else { + log.info( + "Done! No Git repository found! Please initialize a Git repository and perform an initial commit."); + contentBuilder + .append("No Git repository found in project ") + .append(projectName) + .append(" ") + .append(projectVersion) + .append(". "); + contentBuilder.append("Please initialize a Git repository and perform an initial commit."); + writeReportToDisk(outputDirectory, filename, contentBuilder); + return; + } + + String parentOfGitDir = gitDir.getParentFile().getPath(); + log.info("Project Base Dir: {} ", projectBaseDir); + log.info("Parent of Git Dir: {}", parentOfGitDir); + + if (!projectBaseDir.equals(parentOfGitDir)) { + log.warn("Project Base Directory does not match Git Parent Directory"); + contentBuilder.append("Project Base Directory does not match Git Parent Directory. " + + "Please refer to the report at the root of the site directory."); + return; + } + + // actual calcualte + CostBenefitCalculator costBenefitCalculator = new CostBenefitCalculator(); + List rankedDisharmonies = + costBenefitCalculator.calculateGodClassCostBenefitValues(projectBaseDir); + + rankedDisharmonies.sort( + Comparator.comparing(RankedDisharmony::getRawPriority).reversed()); + + // perfect score: no god classes + if (rankedDisharmonies.isEmpty()) { + contentBuilder + .append("Congratulations! ") + .append(projectName) + .append(" ") + .append(projectVersion) + .append(" has no God classes!"); + log.info("Done! No God classes found!"); + + writeReportToDisk(outputDirectory, filename, contentBuilder); + return; + } + + // create Content + // header + final String[] tableHeadings = getHeaderList(showDetails); + addsRow(contentBuilder, tableHeadings); + contentBuilder.append("\n"); + // rows + for (RankedDisharmony rankedDisharmony : rankedDisharmonies) { + final String[] rankedDisharmonyData = getDataList(rankedDisharmony, showDetails); + + contentBuilder.append(projectVersion).append(","); + addsRow(contentBuilder, rankedDisharmonyData); + contentBuilder.append("eol" + "\n"); + } + + log.info(contentBuilder.toString()); + + writeReportToDisk(outputDirectory, filename, contentBuilder); + } + + private DateTimeFormatter createFileDateTimeFormatter() { + return DateTimeFormatter.ofPattern("yyyyMMddhhmm") + .withLocale(Locale.getDefault()) + .withZone(ZoneId.systemDefault()); + } + + private DateTimeFormatter createCsvDateTimeFormatter() { + return DateTimeFormatter.ISO_LOCAL_DATE_TIME + .withLocale(Locale.getDefault()) + .withZone(ZoneId.systemDefault()); + } + + private String[] getDataList(RankedDisharmony rankedDisharmony, boolean showDetails) { + String[] simpleRankedDisharmonyData = { + rankedDisharmony.getFileName(), + rankedDisharmony.getRawPriority().toString(), + rankedDisharmony.getChangePronenessRank().toString(), + rankedDisharmony.getEffortRank().toString(), + rankedDisharmony.getWmc().toString(), + createCsvDateTimeFormatter().format(rankedDisharmony.getMostRecentCommitTime()), + rankedDisharmony.getCommitCount().toString() + }; + + String[] detailedRankedDisharmonyData = { + rankedDisharmony.getFileName(), + rankedDisharmony.getRawPriority().toString(), + rankedDisharmony.getChangePronenessRank().toString(), + rankedDisharmony.getEffortRank().toString(), + rankedDisharmony.getWmc().toString(), + rankedDisharmony.getWmcRank().toString(), + rankedDisharmony.getAtfd().toString(), + rankedDisharmony.getAtfdRank().toString(), + rankedDisharmony.getTcc().toString(), + rankedDisharmony.getTccRank().toString(), + createCsvDateTimeFormatter().format(rankedDisharmony.getFirstCommitTime()), + createCsvDateTimeFormatter().format(rankedDisharmony.getMostRecentCommitTime()), + rankedDisharmony.getCommitCount().toString(), + rankedDisharmony.getPath() + }; + + return showDetails ? detailedRankedDisharmonyData : simpleRankedDisharmonyData; + } + + private String[] getHeaderList(boolean showDetails) { + + final String[] simpleTableHeadings = { + "Ver", + "Class", + "Priority", + "Change Proneness Rank", + "Effort Rank", + "Method Count", + "Most Recent Commit Date", + "Commit Count" + }; + + final String[] detailedTableHeadings = { + "Ver", + "Class", + "Priority", + "Change Proneness Rank", + "Effort Rank", + "WMC", + "WMC Rank", + "ATFD", + "ATFD Rank", + "TCC", + "TCC Rank", + "Date of First Commit", + "Most Recent Commit Date", + "Commit Count", + "Full Path" + }; + + return showDetails ? detailedTableHeadings : simpleTableHeadings; + } + + private void addsRow(StringBuilder contentBuilder, String[] rankedDisharmonyData) { + for (String rowData : rankedDisharmonyData) { + contentBuilder.append(rowData).append(","); + } + } + + public String getOutputNamePrefix() { + // This report will generate simple-report.html when invoked in a project with `mvn site` + return "RefFirst"; + } + + public String getName(Locale locale) { + // Name of the report when listed in the project-reports.html page of a project + return "Refactor First Report data"; + } + + public String getDescription(Locale locale) { + // Description of the report when listed in the project-reports.html page of a project + return "DRACO Ranks the disharmonies in a codebase. The classes that should be refactored first " + + " have the highest priority values."; + } +} diff --git a/report/src/main/java/org/hjug/refactorfirst/report/HtmlReport.java b/report/src/main/java/org/hjug/refactorfirst/report/HtmlReport.java new file mode 100644 index 00000000..59b7e96f --- /dev/null +++ b/report/src/main/java/org/hjug/refactorfirst/report/HtmlReport.java @@ -0,0 +1,463 @@ +package org.hjug.refactorfirst.report; + +import static org.hjug.refactorfirst.report.ReportWriter.writeReportToDisk; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Paths; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import lombok.extern.slf4j.Slf4j; +import org.hjug.cbc.CostBenefitCalculator; +import org.hjug.cbc.RankedDisharmony; +import org.hjug.gdg.GraphDataGenerator; +import org.hjug.git.GitLogReader; + +@Slf4j +public class HtmlReport { + + public static final String THE_BEGINNING = + "\n" + " \n" + + " \n" + + " \n" + + " "; + + public static final String GOD_CLASS_CHART_LEGEND = + "

God Class Chart Legend:

" + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
X-Axis: Effort to refactor to a non-God class
Y-Axis: Relative churn
Color: Priority of what to fix first
Circle size: Priority (Visual) of what to fix first
" + + "
"; + + public static final String COUPLING_BETWEEN_OBJECT_CHART_LEGEND = + "

Coupling Between Objects Chart Legend:

" + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
X-Axis: Number of objects the class is coupled to
Y-Axis: Relative churn
Color: Priority of what to fix first
Circle size: Priority (Visual) of what to fix first
" + + "
"; + + public static final String THE_END = "
\n" + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + " Copyright © 2002–2021The Apache Software Foundation.\n" + + ".
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + " \n" + + "\n"; + + public void execute( + boolean showDetails, String projectName, String projectVersion, String outputDirectory, File baseDir) { + + final String[] godClassSimpleTableHeadings = { + "Class", + "Priority", + "Change Proneness Rank", + "Effort Rank", + "Method Count", + "Most Recent Commit Date", + "Commit Count" + }; + + final String[] godClassDetailedTableHeadings = { + "Class", + "Priority", + "Raw Priority", + "Change Proneness Rank", + "Effort Rank", + "WMC", + "WMC Rank", + "ATFD", + "ATFD Rank", + "TCC", + "TCC Rank", + "Date of First Commit", + "Most Recent Commit Date", + "Commit Count", + "Full Path" + }; + + final String[] cboTableHeadings = { + "Class", "Priority", "Change Proneness Rank", "Coupling Count", "Most Recent Commit Date", "Commit Count" + }; + + final String[] godClassTableHeadings = + showDetails ? godClassDetailedTableHeadings : godClassSimpleTableHeadings; + + String filename = getOutputName() + ".html"; + + log.info("Generating {} for {} - {}", filename, projectName, projectVersion); + + DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) + .withLocale(Locale.getDefault()) + .withZone(ZoneId.systemDefault()); + + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(THE_BEGINNING); + + stringBuilder + .append("Refactor First Report for ") + .append(projectName) + .append(" ") + .append(projectVersion) + .append(" "); + + stringBuilder.append("\n" + + " \n" + + " \n" + + " \n" + + "" + + "" + + "" + + " \n" + + " \n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
"); + + stringBuilder + .append("Last Published: ") + .append(formatter.format(Instant.now())) + .append(""); + stringBuilder + .append(" Version: ") + .append(projectVersion) + .append(""); + + stringBuilder.append("
\n" + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
"); + + stringBuilder + .append("
\n" + "

RefactorFirst Report for ") + .append(projectName) + .append(" ") + .append(projectVersion) + .append("

\n"); + + GitLogReader gitLogReader = new GitLogReader(); + String projectBaseDir; + Optional optionalGitDir; + + if (baseDir != null) { + projectBaseDir = baseDir.getPath(); + optionalGitDir = Optional.ofNullable(gitLogReader.getGitDir(baseDir)); + } else { + projectBaseDir = Paths.get("").toAbsolutePath().toString(); + optionalGitDir = Optional.ofNullable(gitLogReader.getGitDir(new File(projectBaseDir))); + } + + File gitDir; + if (optionalGitDir.isPresent()) { + gitDir = optionalGitDir.get(); + } else { + log.info( + "Done! No Git repository found! Please initialize a Git repository and perform an initial commit."); + stringBuilder + .append("No Git repository found in project ") + .append(projectName) + .append(" ") + .append(projectVersion) + .append(". "); + stringBuilder.append("Please initialize a Git repository and perform an initial commit."); + stringBuilder.append(THE_END); + writeReportToDisk(outputDirectory, filename, stringBuilder); + return; + } + + String parentOfGitDir = gitDir.getParentFile().getPath(); + log.info("Project Base Dir: {} ", projectBaseDir); + log.info("Parent of Git Dir: {}", parentOfGitDir); + + if (!projectBaseDir.equals(parentOfGitDir)) { + log.warn("Project Base Directory does not match Git Parent Directory"); + stringBuilder.append("Project Base Directory does not match Git Parent Directory. " + + "Please refer to the report at the root of the site directory."); + stringBuilder.append(THE_END); + return; + } + + CostBenefitCalculator costBenefitCalculator = new CostBenefitCalculator(); + List rankedGodClassDisharmonies = + costBenefitCalculator.calculateGodClassCostBenefitValues(projectBaseDir); + + List rankedCBODisharmonies = + costBenefitCalculator.calculateCBOCostBenefitValues(projectBaseDir); + + if (rankedGodClassDisharmonies.isEmpty() && rankedCBODisharmonies.isEmpty()) { + stringBuilder + .append("Congratulations! ") + .append(projectName) + .append(" ") + .append(projectVersion) + .append(" has no God classes or highly coupled classes!"); + renderGithubButtons(stringBuilder); + log.info("Done! No Disharmonies found!"); + stringBuilder.append(THE_END); + writeReportToDisk(outputDirectory, filename, stringBuilder); + return; + } + + if (!rankedGodClassDisharmonies.isEmpty() && !rankedCBODisharmonies.isEmpty()) { + stringBuilder.append("God Classes"); + stringBuilder.append("
"); + stringBuilder.append("Highly Coupled Classes"); + } + + if (!rankedGodClassDisharmonies.isEmpty()) { + rankedGodClassDisharmonies.sort( + Comparator.comparing(RankedDisharmony::getRawPriority).reversed()); + + int godClassPriority = 1; + for (RankedDisharmony rankedGodClassDisharmony : rankedGodClassDisharmonies) { + rankedGodClassDisharmony.setPriority(godClassPriority++); + } + + stringBuilder.append(""); + + writeGodClassGchartJs(rankedGodClassDisharmonies, godClassPriority - 1, outputDirectory); + stringBuilder.append("
"); + renderGithubButtons(stringBuilder); + stringBuilder.append(GOD_CLASS_CHART_LEGEND); + + stringBuilder.append( + "

God classes by the numbers: (Refactor Starting with Priority 1)

"); + stringBuilder.append(""); + + // Content + stringBuilder.append(""); + for (String heading : godClassTableHeadings) { + stringBuilder.append(""); + } + stringBuilder.append(""); + + stringBuilder.append(""); + for (RankedDisharmony rankedGodClassDisharmony : rankedGodClassDisharmonies) { + stringBuilder.append(""); + + String[] simpleRankedGodClassDisharmonyData = { + rankedGodClassDisharmony.getFileName(), + rankedGodClassDisharmony.getPriority().toString(), + rankedGodClassDisharmony.getChangePronenessRank().toString(), + rankedGodClassDisharmony.getEffortRank().toString(), + rankedGodClassDisharmony.getWmc().toString(), + formatter.format(rankedGodClassDisharmony.getMostRecentCommitTime()), + rankedGodClassDisharmony.getCommitCount().toString() + }; + + String[] detailedRankedGodClassDisharmonyData = { + rankedGodClassDisharmony.getFileName(), + rankedGodClassDisharmony.getPriority().toString(), + rankedGodClassDisharmony.getRawPriority().toString(), + rankedGodClassDisharmony.getChangePronenessRank().toString(), + rankedGodClassDisharmony.getEffortRank().toString(), + rankedGodClassDisharmony.getWmc().toString(), + rankedGodClassDisharmony.getWmcRank().toString(), + rankedGodClassDisharmony.getAtfd().toString(), + rankedGodClassDisharmony.getAtfdRank().toString(), + rankedGodClassDisharmony.getTcc().toString(), + rankedGodClassDisharmony.getTccRank().toString(), + formatter.format(rankedGodClassDisharmony.getFirstCommitTime()), + formatter.format(rankedGodClassDisharmony.getMostRecentCommitTime()), + rankedGodClassDisharmony.getCommitCount().toString(), + rankedGodClassDisharmony.getPath() + }; + + final String[] rankedDisharmonyData = + showDetails ? detailedRankedGodClassDisharmonyData : simpleRankedGodClassDisharmonyData; + + for (String rowData : rankedDisharmonyData) { + stringBuilder.append(""); + } + + stringBuilder.append(""); + } + + stringBuilder.append(""); + stringBuilder.append("
").append(heading).append("
").append(rowData).append("
"); + } + + if (!rankedCBODisharmonies.isEmpty()) { + + stringBuilder.append("
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
"); + + rankedCBODisharmonies.sort( + Comparator.comparing(RankedDisharmony::getRawPriority).reversed()); + + int cboPriority = 1; + for (RankedDisharmony rankedCBODisharmony : rankedCBODisharmonies) { + rankedCBODisharmony.setPriority(cboPriority++); + } + + stringBuilder.append( + ""); + + stringBuilder.append("
"); + writeGCBOGchartJs(rankedCBODisharmonies, cboPriority - 1, outputDirectory); + renderGithubButtons(stringBuilder); + stringBuilder.append(COUPLING_BETWEEN_OBJECT_CHART_LEGEND); + + stringBuilder.append("

Highly Coupled classes by the numbers: (Refactor starting with Priority 1)

"); + stringBuilder.append(""); + + // Content + stringBuilder.append(""); + for (String heading : cboTableHeadings) { + stringBuilder.append(""); + } + stringBuilder.append(""); + + stringBuilder.append(""); + for (RankedDisharmony rankedCboClassDisharmony : rankedCBODisharmonies) { + stringBuilder.append(""); + + String[] rankedCboClassDisharmonyData = { + rankedCboClassDisharmony.getFileName(), + rankedCboClassDisharmony.getPriority().toString(), + rankedCboClassDisharmony.getChangePronenessRank().toString(), + rankedCboClassDisharmony.getEffortRank().toString(), + formatter.format(rankedCboClassDisharmony.getMostRecentCommitTime()), + rankedCboClassDisharmony.getCommitCount().toString() + }; + + for (String rowData : rankedCboClassDisharmonyData) { + stringBuilder.append(""); + } + + stringBuilder.append(""); + } + + stringBuilder.append(""); + } + + stringBuilder.append("
").append(heading).append("
").append(rowData).append("
"); + stringBuilder.append(THE_END); + + log.debug(stringBuilder.toString()); + + writeReportToDisk(outputDirectory, filename, stringBuilder); + log.info("Done! View the report at target/site/{}", filename); + } + + void renderGithubButtons(StringBuilder stringBuilder) { + stringBuilder.append("
"); + stringBuilder.append("Show RefactorFirst some ❤️"); + stringBuilder.append("
"); + stringBuilder.append( + "Star"); + stringBuilder.append( + "Fork"); + stringBuilder.append( + "Watch"); + stringBuilder.append( + "Issue"); + stringBuilder.append( + "Sponsor"); + stringBuilder.append("
"); + } + + // TODO: Move to another class to allow use by Gradle plugin + void writeGodClassGchartJs( + List rankedDisharmonies, int maxPriority, String reportOutputDirectory) { + GraphDataGenerator graphDataGenerator = new GraphDataGenerator(); + String scriptStart = graphDataGenerator.getGodClassScriptStart(); + String bubbleChartData = graphDataGenerator.generateGodClassBubbleChartData(rankedDisharmonies, maxPriority); + String scriptEnd = graphDataGenerator.getGodClassScriptEnd(); + + String javascriptCode = scriptStart + bubbleChartData + scriptEnd; + + File reportOutputDir = new File(reportOutputDirectory); + if (!reportOutputDir.exists()) { + reportOutputDir.mkdirs(); + } + String pathname = reportOutputDirectory + File.separator + "gchart.js"; + + File scriptFile = new File(pathname); + try { + scriptFile.createNewFile(); + } catch (IOException e) { + log.error("Failure creating God Class chart script file", e); + } + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(scriptFile))) { + writer.write(javascriptCode); + } catch (IOException e) { + log.error("Error writing chart script file", e); + } + } + + void writeGCBOGchartJs(List rankedDisharmonies, int maxPriority, String reportOutputDirectory) { + GraphDataGenerator graphDataGenerator = new GraphDataGenerator(); + String scriptStart = graphDataGenerator.getCBOScriptStart(); + String bubbleChartData = graphDataGenerator.generateCBOBubbleChartData(rankedDisharmonies, maxPriority); + String scriptEnd = graphDataGenerator.getCBOScriptEnd(); + + String javascriptCode = scriptStart + bubbleChartData + scriptEnd; + + File reportOutputDir = new File(reportOutputDirectory); + if (!reportOutputDir.exists()) { + reportOutputDir.mkdirs(); + } + String pathname = reportOutputDirectory + File.separator + "gchart2.js"; + + File scriptFile = new File(pathname); + try { + scriptFile.createNewFile(); + } catch (IOException e) { + log.error("Failure creating CBO chart script file", e); + } + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(scriptFile))) { + writer.write(javascriptCode); + } catch (IOException e) { + log.error("Error writing CBO chart script file", e); + } + } + + public String getOutputName() { + // This report will generate simple-report.html when invoked in a project with `mvn site` + return "refactor-first-report"; + } + + public String getName(Locale locale) { + // Name of the report when listed in the project-reports.html page of a project + return "Refactor First Report"; + } + + public String getDescription(Locale locale) { + // Description of the report when listed in the project-reports.html page of a project + return "Ranks the disharmonies in a codebase. The classes that should be refactored first " + + " have the highest priority values."; + } +} diff --git a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/ReportWriter.java b/report/src/main/java/org/hjug/refactorfirst/report/ReportWriter.java similarity index 73% rename from refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/ReportWriter.java rename to report/src/main/java/org/hjug/refactorfirst/report/ReportWriter.java index 69ecf273..bd186fbb 100644 --- a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/ReportWriter.java +++ b/report/src/main/java/org/hjug/refactorfirst/report/ReportWriter.java @@ -1,4 +1,4 @@ -package org.hjug.mavenreport; +package org.hjug.refactorfirst.report; import java.io.BufferedWriter; import java.io.File; @@ -6,16 +6,12 @@ import java.nio.charset.Charset; import java.nio.file.Files; import lombok.extern.slf4j.Slf4j; -import org.apache.maven.project.MavenProject; @Slf4j public class ReportWriter { + public static void writeReportToDisk( - final MavenProject project, final String filename, final StringBuilder stringBuilder) { - final String reportOutputDirectory = project.getModel() - .getReporting() - .getOutputDirectory() - .replace("${project.basedir}" + File.separator, ""); + final String reportOutputDirectory, final String filename, final StringBuilder stringBuilder) { final File reportOutputDir = new File(reportOutputDirectory); if (!reportOutputDir.exists()) { diff --git a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstJsonReport/JsonReport.java b/report/src/main/java/org/hjug/refactorfirst/report/json/JsonReport.java similarity index 78% rename from refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstJsonReport/JsonReport.java rename to report/src/main/java/org/hjug/refactorfirst/report/json/JsonReport.java index ab544888..0bfb519b 100644 --- a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstJsonReport/JsonReport.java +++ b/report/src/main/java/org/hjug/refactorfirst/report/json/JsonReport.java @@ -1,4 +1,4 @@ -package org.hjug.mavenreport.RefactorFirstJsonReport; +package org.hjug.refactorfirst.report.json; import java.util.List; import lombok.Builder; diff --git a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstJsonReport/JsonReportDisharmonyEntry.java b/report/src/main/java/org/hjug/refactorfirst/report/json/JsonReportDisharmonyEntry.java similarity index 96% rename from refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstJsonReport/JsonReportDisharmonyEntry.java rename to report/src/main/java/org/hjug/refactorfirst/report/json/JsonReportDisharmonyEntry.java index 59ccfbf4..18f17f9f 100644 --- a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstJsonReport/JsonReportDisharmonyEntry.java +++ b/report/src/main/java/org/hjug/refactorfirst/report/json/JsonReportDisharmonyEntry.java @@ -1,4 +1,4 @@ -package org.hjug.mavenreport.RefactorFirstJsonReport; +package org.hjug.refactorfirst.report.json; import java.time.ZoneId; import java.time.format.DateTimeFormatter; diff --git a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstJsonReport/RefactorFirstMavenJsonReport.java b/report/src/main/java/org/hjug/refactorfirst/report/json/JsonReportExecutor.java similarity index 62% rename from refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstJsonReport/RefactorFirstMavenJsonReport.java rename to report/src/main/java/org/hjug/refactorfirst/report/json/JsonReportExecutor.java index 05874365..ea431572 100644 --- a/refactor-first-maven-plugin/src/main/java/org/hjug/mavenreport/RefactorFirstJsonReport/RefactorFirstMavenJsonReport.java +++ b/report/src/main/java/org/hjug/refactorfirst/report/json/JsonReportExecutor.java @@ -1,6 +1,6 @@ -package org.hjug.mavenreport.RefactorFirstJsonReport; +package org.hjug.refactorfirst.report.json; -import static org.hjug.mavenreport.ReportWriter.writeReportToDisk; +import static org.hjug.refactorfirst.report.ReportWriter.writeReportToDisk; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -11,36 +11,19 @@ import java.util.List; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.plugins.annotations.ResolutionScope; -import org.apache.maven.project.MavenProject; import org.hjug.cbc.CostBenefitCalculator; import org.hjug.cbc.RankedDisharmony; @Slf4j -@Mojo( - name = "jsonreport", - defaultPhase = LifecyclePhase.SITE, - requiresDependencyResolution = ResolutionScope.RUNTIME, - requiresProject = false, - threadSafe = true, - inheritByDefault = false) -public class RefactorFirstMavenJsonReport extends AbstractMojo { +public class JsonReportExecutor { + private static final String FILE_NAME = "refactor-first-data.json"; private static final ObjectMapper MAPPER = new ObjectMapper(); - @Parameter(readonly = true, defaultValue = "${project}") - private MavenProject project; - - @Override - public void execute() { + public void execute(File baseDir, String outputDirectory) { String projectBaseDir; - File baseDir = project.getBasedir(); if (baseDir != null) { projectBaseDir = baseDir.getPath(); } else { @@ -60,7 +43,7 @@ public void execute() { try { final String reportJson = MAPPER.writeValueAsString(report); - writeReportToDisk(project, FILE_NAME, new StringBuilder(reportJson)); + writeReportToDisk(outputDirectory, FILE_NAME, new StringBuilder(reportJson)); } catch (final JsonProcessingException jsonProcessingException) { final String errorMessage = "Could not generate a json report: " + jsonProcessingException; @@ -69,13 +52,13 @@ public void execute() { .errors(new ArrayList<>(Collections.singletonList(errorMessage))) .build(); - writeErrorReport(errorReport); + writeErrorReport(errorReport, outputDirectory); } } - private void writeErrorReport(final JsonReport errorReport) { + private void writeErrorReport(final JsonReport errorReport, String outputDirectory) { try { - writeReportToDisk(project, FILE_NAME, new StringBuilder(MAPPER.writeValueAsString(errorReport))); + writeReportToDisk(outputDirectory, FILE_NAME, new StringBuilder(MAPPER.writeValueAsString(errorReport))); } catch (final JsonProcessingException jsonProcessingException) { log.error("failed to write error report: ", jsonProcessingException); } diff --git a/refactor-first-maven-plugin/src/test/java/org/hjug/mavenreport/RefactorFirstMavenReportTest.java b/report/src/test/java/org/hjug/refactorfirst/report/HtmlReportTest.java similarity index 85% rename from refactor-first-maven-plugin/src/test/java/org/hjug/mavenreport/RefactorFirstMavenReportTest.java rename to report/src/test/java/org/hjug/refactorfirst/report/HtmlReportTest.java index 1bfaa1c6..e66080fe 100644 --- a/refactor-first-maven-plugin/src/test/java/org/hjug/mavenreport/RefactorFirstMavenReportTest.java +++ b/report/src/test/java/org/hjug/refactorfirst/report/HtmlReportTest.java @@ -1,13 +1,13 @@ -package org.hjug.mavenreport; +package org.hjug.refactorfirst.report; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Locale; import org.junit.jupiter.api.Test; -public class RefactorFirstMavenReportTest { +public class HtmlReportTest { - private RefactorFirstMavenReport mavenReport = new RefactorFirstMavenReport(); + private HtmlReport mavenReport = new HtmlReport(); @Test void testGetOutputName() { diff --git a/test-resources/pom.xml b/test-resources/pom.xml index 2a25abbb..49f0c22c 100644 --- a/test-resources/pom.xml +++ b/test-resources/pom.xml @@ -5,7 +5,7 @@ org.hjug.refactorfirst refactor-first - 0.4.1-SNAPSHOT + 0.5.0-M1-SNAPSHOT org.hjug.refactorfirst.testresources