Initial commit
This commit is contained in:
commit
ef8b6df387
27 changed files with 952 additions and 0 deletions
34
src/bld/java/rife/bld/extension/PmdOperationBuild.java
Normal file
34
src/bld/java/rife/bld/extension/PmdOperationBuild.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
package rife.bld.extension;
|
||||
|
||||
import rife.bld.Project;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static rife.bld.dependencies.Repository.MAVEN_CENTRAL;
|
||||
import static rife.bld.dependencies.Repository.RIFE2_RELEASES;
|
||||
import static rife.bld.dependencies.Scope.*;
|
||||
|
||||
public class PmdOperationBuild extends Project {
|
||||
public PmdOperationBuild() {
|
||||
pkg = "rife.bld.extension";
|
||||
name = "PmdOperation";
|
||||
mainClass = "rife.bld.extension.PmdOperationMain";
|
||||
version = version(0, 9, 0, "SNAPSHOT");
|
||||
|
||||
downloadSources = true;
|
||||
repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES);
|
||||
scope(compile)
|
||||
.include(dependency("com.uwyn.rife2", "rife2", version(1, 5, 19)))
|
||||
.include(dependency("net.sourceforge.pmd:pmd-java:6.55.0"));
|
||||
scope(runtime)
|
||||
.include(dependency("net.sourceforge.pmd:pmd:6.55.0"));
|
||||
scope(test)
|
||||
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 9, 2)))
|
||||
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 9, 2)))
|
||||
.include(dependency("org.assertj:assertj-joda-time:2.2.0"));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new PmdOperationBuild().start(args);
|
||||
}
|
||||
}
|
293
src/main/java/rife/bld/extension/PmdOperation.java
Normal file
293
src/main/java/rife/bld/extension/PmdOperation.java
Normal file
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package rife.bld.extension;
|
||||
|
||||
import net.sourceforge.pmd.PMDConfiguration;
|
||||
import net.sourceforge.pmd.PmdAnalysis;
|
||||
import net.sourceforge.pmd.RulePriority;
|
||||
import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
import rife.bld.Project;
|
||||
import rife.bld.operations.AbstractOperation;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class PmdOperation extends AbstractOperation<PmdOperation> {
|
||||
private static final Logger LOGGER = Logger.getLogger(PmdOperation.class.getName());
|
||||
private static final String PMD_DIR = "pmd";
|
||||
public static final String RULESET_DEFAULT = "rulesets/java/quickstart.xml";
|
||||
Path cache;
|
||||
boolean debug;
|
||||
String encoding = "UTF-8";
|
||||
boolean failOnViolation;
|
||||
Path ignoreFile;
|
||||
boolean incrementalAnalysis = true;
|
||||
List<Path> inputPaths = new ArrayList<>();
|
||||
URI inputUri;
|
||||
LanguageVersion languageVersion;
|
||||
Path reportFile;
|
||||
String reportFormat = "text";
|
||||
RulePriority rulePriority = RulePriority.LOW;
|
||||
List<String> ruleSets = new ArrayList<>();
|
||||
boolean showSuppressed;
|
||||
String suppressedMarker = "NOPMD";
|
||||
int threads = 1;
|
||||
private Project project;
|
||||
|
||||
public PmdOperation() {
|
||||
super();
|
||||
}
|
||||
|
||||
public PmdOperation(Project project) {
|
||||
super();
|
||||
this.project = project;
|
||||
|
||||
inputPaths.add(project.srcMainDirectory().toPath());
|
||||
inputPaths.add(project.srcTestDirectory().toPath());
|
||||
ruleSets.add(RULESET_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the path to a source file, or directory containing source files to analyze.
|
||||
*
|
||||
* @see #inputPaths(Path...)
|
||||
*/
|
||||
public PmdOperation addInputPath(Path inputPath) {
|
||||
inputPaths.add(inputPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new rule set path.
|
||||
*
|
||||
* @see #ruleSets(String...)
|
||||
*/
|
||||
public PmdOperation addRuleSet(String ruleSet) {
|
||||
ruleSets.add(ruleSet);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location of the cache file for incremental analysis.
|
||||
*/
|
||||
public PmdOperation cache(Path cache) {
|
||||
this.cache = cache;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables debug logging mode.
|
||||
*/
|
||||
public PmdOperation debug(boolean debug) {
|
||||
this.debug = debug;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Specifies the character set encoding of the source code files. The default is {@code UTF-8}.</p>
|
||||
*
|
||||
* <p>The valid values are the standard character sets of {@link java.nio.charset.Charset Charset}.</p>
|
||||
*/
|
||||
public PmdOperation encoding(String encoding) {
|
||||
this.encoding = encoding;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the PMD code analysis operation.
|
||||
*
|
||||
* @throws Exception when an exception occurs during the execution
|
||||
*/
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (project == null) {
|
||||
throw new IllegalArgumentException("ERROR: project required.");
|
||||
}
|
||||
|
||||
var commandName = project.getCurrentCommandName();
|
||||
performPmdAnalysis(commandName, initConfiguration(commandName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the build will continue on warnings.
|
||||
*/
|
||||
public PmdOperation failOnViolation(boolean failOnViolation) {
|
||||
this.failOnViolation = failOnViolation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a language to be used for all input files, irrespective of file names.
|
||||
*/
|
||||
public PmdOperation forceLanguage(LanguageVersion languageVersion) {
|
||||
this.languageVersion = languageVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the file containing a list of files to ignore, one path per line.
|
||||
*/
|
||||
public PmdOperation ignoreFile(Path ignoreFile) {
|
||||
this.ignoreFile = ignoreFile;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables incremental analysis.
|
||||
*/
|
||||
public PmdOperation incrementalAnalysis(boolean incrementalAnalysis) {
|
||||
this.incrementalAnalysis = incrementalAnalysis;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PMDConfiguration initConfiguration(String commandName) {
|
||||
PMDConfiguration config = new PMDConfiguration();
|
||||
if (cache == null && project != null && incrementalAnalysis) {
|
||||
config.setAnalysisCacheLocation(
|
||||
Paths.get(project.buildDirectory().getPath(), PMD_DIR, PMD_DIR + "-cache").toFile().getAbsolutePath());
|
||||
} else if (cache != null) {
|
||||
config.setAnalysisCacheLocation(cache.toFile().getAbsolutePath());
|
||||
}
|
||||
config.setDebug(debug);
|
||||
config.setFailOnViolation(failOnViolation);
|
||||
if (languageVersion != null) {
|
||||
config.setForceLanguageVersion(languageVersion);
|
||||
}
|
||||
if (ignoreFile != null) {
|
||||
config.setIgnoreFilePath(ignoreFile);
|
||||
}
|
||||
config.setIgnoreIncrementalAnalysis(!incrementalAnalysis);
|
||||
if (inputPaths.isEmpty()) {
|
||||
throw new IllegalArgumentException(commandName + ": InputPaths required.");
|
||||
} else {
|
||||
config.setInputPathList(inputPaths);
|
||||
}
|
||||
if (inputUri != null) {
|
||||
config.setInputUri(inputUri);
|
||||
}
|
||||
config.setMinimumPriority(rulePriority);
|
||||
|
||||
if (project != null) {
|
||||
config.setReportFile(Objects.requireNonNullElseGet(reportFile,
|
||||
() -> Paths.get(project.buildDirectory().getPath(), PMD_DIR, PMD_DIR + "-report." + reportFormat)));
|
||||
} else {
|
||||
config.setReportFile(reportFile);
|
||||
}
|
||||
|
||||
config.setReportFormat(reportFormat);
|
||||
config.setRuleSets(ruleSets);
|
||||
config.setShowSuppressedViolations(showSuppressed);
|
||||
config.setSourceEncoding(encoding);
|
||||
config.setSuppressMarker(suppressedMarker);
|
||||
config.setThreads(threads);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the to source files, or directories containing source files to analyze.
|
||||
*/
|
||||
public PmdOperation inputPaths(Path... inputPath) {
|
||||
inputPaths.clear();
|
||||
inputPaths.addAll(List.of(inputPath));
|
||||
return this;
|
||||
}
|
||||
|
||||
public int performPmdAnalysis(String commandName, PMDConfiguration config) throws RuntimeException {
|
||||
var pmd = PmdAnalysis.create(config);
|
||||
var report = pmd.performAnalysisAndCollectReport();
|
||||
if (LOGGER.isLoggable(Level.INFO)) {
|
||||
LOGGER.info(String.format("[%s] ruleSets%s", commandName, ruleSets));
|
||||
}
|
||||
var numErrors = report.getViolations().size();
|
||||
if (numErrors > 0) {
|
||||
var msg = String.format(
|
||||
"[%s] %d rule violations were found. See the report at: %s", commandName, numErrors,
|
||||
config.getReportFilePath().toUri());
|
||||
for (var v : report.getViolations()) {
|
||||
if (LOGGER.isLoggable(Level.WARNING)) {
|
||||
LOGGER.warning(String.format("[%s] %s:%d\n\t%s (%s)\n\t\t--> %s", commandName,
|
||||
Paths.get(v.getFilename()).toUri(), v.getBeginLine(), v.getRule().getName(), v.getRule().getExternalInfoUrl(), v.getDescription()));
|
||||
}
|
||||
}
|
||||
if (config.isFailOnViolation()) {
|
||||
throw new RuntimeException(msg); // NOPMD
|
||||
} else {
|
||||
if (LOGGER.isLoggable(Level.WARNING)) {
|
||||
LOGGER.warning(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
return numErrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the output format of the analysis report. The default is {@code text}.
|
||||
*/
|
||||
public PmdOperation reportFormat(String reportFormat) {
|
||||
this.reportFormat = reportFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rule set path(s).
|
||||
*/
|
||||
public PmdOperation ruleSets(String... ruleSet) {
|
||||
ruleSets.clear();
|
||||
ruleSets.addAll(Arrays.asList(ruleSet));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables adding the suppressed rule violations to the report.
|
||||
*/
|
||||
public PmdOperation showSuppressed(boolean showSuppressed) {
|
||||
this.showSuppressed = showSuppressed;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the comment token that marks lines which should be ignored. The default is {@code NOPMD}.
|
||||
*/
|
||||
public PmdOperation suppressedMarker(String suppressedMarker) {
|
||||
this.suppressedMarker = suppressedMarker;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of threads to be used. The default is {code 1}.
|
||||
*/
|
||||
public PmdOperation threads(int threads) {
|
||||
this.threads = threads;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the input URI to process for source code objects.
|
||||
*/
|
||||
public PmdOperation uri(URI inputUri) {
|
||||
this.inputUri = inputUri;
|
||||
return this;
|
||||
}
|
||||
}
|
64
src/test/java/rife/bld/extension/PmdOperationTest.java
Normal file
64
src/test/java/rife/bld/extension/PmdOperationTest.java
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package rife.bld.extension;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/**
|
||||
* PmdOperationTest class
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
|
||||
* @since 1.0
|
||||
*/
|
||||
public class PmdOperationTest {
|
||||
static final PmdOperation PMD_OPERATION = new PmdOperation();
|
||||
|
||||
@BeforeAll
|
||||
public static void initializeOperation() {
|
||||
PMD_OPERATION.inputPaths = List.of(Paths.get("src/main"));
|
||||
PMD_OPERATION.reportFile = Paths.get("build", "pmd", "pmd-test-report.txt");
|
||||
PMD_OPERATION.cache = Paths.get("build", "pmd", "pmd-cache");
|
||||
PMD_OPERATION.failOnViolation = false;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfigFile() {
|
||||
var pmd = PMD_OPERATION.ruleSets("src/test/resources/pmd.xml");
|
||||
assertThat(pmd.performPmdAnalysis("test", pmd.initConfiguration("pmd")))
|
||||
.as("no errors").isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyRuleSets() {
|
||||
var pmd = PMD_OPERATION.ruleSets("");
|
||||
assertThat(pmd.performPmdAnalysis("test", pmd.initConfiguration("pmd")))
|
||||
.as("no errors").isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPmdOperation() {
|
||||
assertThat(PMD_OPERATION.performPmdAnalysis("test", PMD_OPERATION.initConfiguration("pmd")))
|
||||
.as("no errors").isEqualTo(0);
|
||||
}
|
||||
}
|
112
src/test/resources/pmd.xml
Normal file
112
src/test/resources/pmd.xml
Normal file
|
@ -0,0 +1,112 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset name="test"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
|
||||
<description>Erik's Ruleset</description>
|
||||
<!-- BEST PRACTICES -->
|
||||
<rule ref="category/java/bestpractices.xml">
|
||||
<exclude name="AvoidPrintStackTrace"/>
|
||||
<exclude name="JUnit4TestShouldUseTestAnnotation"/>
|
||||
<exclude name="JUnitTestContainsTooManyAsserts"/>
|
||||
</rule>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/MissingOverride">
|
||||
<properties>
|
||||
<property name="violationSuppressXPath"
|
||||
value="//MethodDeclaration[@Name='hashCode' or @Name='equals' or @Name='toString']"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
|
||||
<!-- CODE STYLE -->
|
||||
<rule ref="category/java/codestyle.xml">
|
||||
<exclude name="AtLeastOneConstructor"/>
|
||||
<exclude name="ClassNamingConventions"/>
|
||||
<exclude name="CommentDefaultAccessModifier"/>
|
||||
<exclude name="ConfusingTernary"/>
|
||||
<exclude name="DefaultPackage"/>
|
||||
<exclude name="FieldNamingConventions"/>
|
||||
<exclude name="LocalVariableCouldBeFinal"/>
|
||||
<exclude name="LongVariable"/>
|
||||
<exclude name="MethodArgumentCouldBeFinal"/>
|
||||
<exclude name="OnlyOneReturn"/>
|
||||
<exclude name="PackageCase"/>
|
||||
<exclude name="ShortClassName"/>
|
||||
<exclude name="ShortMethodName"/>
|
||||
<exclude name="ShortVariable"/>
|
||||
<exclude name="UseUnderscoresInNumericLiterals"/>
|
||||
<exclude name="UselessParentheses"/>
|
||||
</rule>
|
||||
|
||||
<!-- DESIGN -->
|
||||
<rule ref="category/java/design.xml">
|
||||
<exclude name="AvoidCatchingGenericException"/>
|
||||
<exclude name="AvoidDeeplyNestedIfStmts"/>
|
||||
<exclude name="AvoidUncheckedExceptionsInSignatures"/>
|
||||
<exclude name="CognitiveComplexity"/>
|
||||
<exclude name="CyclomaticComplexity"/>
|
||||
<exclude name="ExcessiveClassLength"/>
|
||||
<exclude name="ExcessiveMethodLength"/>
|
||||
<exclude name="ExcessiveParameterList"/>
|
||||
<exclude name="ExcessivePublicCount"/>
|
||||
<exclude name="GodClass"/>
|
||||
<exclude name="LawOfDemeter"/>
|
||||
<exclude name="LoosePackageCoupling"/>
|
||||
<exclude name="NPathComplexity"/>
|
||||
<exclude name="NcssCount"/>
|
||||
<exclude name="TooManyFields"/>
|
||||
<exclude name="TooManyMethods"/>
|
||||
<exclude name="UseObjectForClearerAPI"/>
|
||||
</rule>
|
||||
|
||||
<!-- DOCUMENTATION -->
|
||||
<rule ref="category/java/documentation.xml">
|
||||
<exclude name="CommentRequired"/>
|
||||
<exclude name="CommentSize"/>
|
||||
</rule>
|
||||
|
||||
<!-- ERROR PRONE -->
|
||||
<rule ref="category/java/errorprone.xml">
|
||||
<exclude name="AssignmentInOperand"/>
|
||||
<exclude name="AvoidCatchingNPE"/>
|
||||
<exclude name="AvoidDuplicateLiterals"/>
|
||||
<exclude name="AvoidFieldNameMatchingMethodName"/>
|
||||
<exclude name="AvoidFieldNameMatchingTypeName"/>
|
||||
<exclude name="AvoidLiteralsInIfCondition"/>
|
||||
<exclude name="EmptyCatchBlock"/>
|
||||
<exclude name="NullAssignment"/>
|
||||
</rule>
|
||||
|
||||
<rule ref="category/java/errorprone.xml/AssignmentInOperand">
|
||||
<properties>
|
||||
<property name="allowWhile" value="true"/>
|
||||
<property name="allowFor" value="true"/>
|
||||
<property name="allowIf" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals">
|
||||
<properties>
|
||||
<property name="skipAnnotations" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="category/java/errorprone.xml/EmptyCatchBlock">
|
||||
<properties>
|
||||
<property name="allowExceptionNameRegex">
|
||||
<value>^ignore$</value>
|
||||
</property>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<!-- MULTITHREADING -->
|
||||
<rule ref="category/java/multithreading.xml">
|
||||
</rule>
|
||||
|
||||
<!-- PERFORMANCE -->
|
||||
<rule ref="category/java/performance.xml">
|
||||
</rule>
|
||||
|
||||
<!-- SECURITY -->
|
||||
<rule ref="category/java/security.xml">
|
||||
</rule>
|
||||
</ruleset>
|
Loading…
Add table
Add a link
Reference in a new issue