diff --git a/src/main/java/rife/bld/extension/PmdOperation.java b/src/main/java/rife/bld/extension/PmdOperation.java index a8ea894..2125be1 100644 --- a/src/main/java/rife/bld/extension/PmdOperation.java +++ b/src/main/java/rife/bld/extension/PmdOperation.java @@ -49,7 +49,6 @@ public class PmdOperation extends AbstractOperation { /** * The cache location. - * */ Path cache; /** @@ -64,6 +63,10 @@ public class PmdOperation extends AbstractOperation { * The fail on violation toggle. */ boolean failOnViolation; + /** + * The forced language. + */ + LanguageVersion forcedLanguageVersion; /** * The path of the ignore file */ @@ -81,9 +84,13 @@ public class PmdOperation extends AbstractOperation { */ URI inputUri; /** - * The language version. + * The default language version(s). */ - LanguageVersion languageVersion; + List languageVersions; + /** + * The relative roots paths. + */ + List relativizeRoots = new ArrayList<>(); /** * The path to the report page. */ @@ -168,6 +175,16 @@ public class PmdOperation extends AbstractOperation { return this; } + /** + * Add several paths to shorten paths that are output in the report. + * + * @see #addRelativizeRoot(Path...) + */ + public PmdOperation addRelativizeRoot(Path... relativeRoot) { + this.relativizeRoots.addAll(List.of(relativeRoot)); + return this; + } + /** * Adds new rule set paths. *

@@ -207,6 +224,14 @@ public class PmdOperation extends AbstractOperation { return this; } + /** + * Set the default language to be used for all input files. + */ + public PmdOperation defaultLanguage(LanguageVersion... languageVersion) { + this.languageVersions.addAll(List.of(languageVersion)); + return this; + } + /** *

Specifies the character set encoding of the source code files. The default is {@code UTF-8}.

* @@ -243,8 +268,8 @@ public class PmdOperation extends AbstractOperation { /** * Forces a language to be used for all input files, irrespective of file names. */ - public PmdOperation forceLanguage(LanguageVersion languageVersion) { - this.languageVersion = languageVersion; + public PmdOperation forceVersion(LanguageVersion languageVersion) { + this.forcedLanguageVersion = languageVersion; return this; } @@ -269,29 +294,41 @@ public class PmdOperation extends AbstractOperation { */ 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 (languageVersions != null) { + config.setDefaultLanguageVersions(languageVersions); } + + if (forcedLanguageVersion != null) { + config.setForceLanguageVersion(forcedLanguageVersion); + } + 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) { @@ -301,6 +338,7 @@ public class PmdOperation extends AbstractOperation { config.setReportFile(reportFile); } + config.addRelativizeRoots(relativizeRoots); config.setReportFormat(reportFormat); config.setRuleSets(ruleSets); config.setShowSuppressedViolations(showSuppressed); @@ -313,6 +351,9 @@ public class PmdOperation extends AbstractOperation { /** * Sets the to source files, or directories containing source files to analyze. + * Previously set paths will be disregarded. + * + * @see #addInputPath(Path...) */ public PmdOperation inputPaths(Path... inputPath) { inputPaths.clear(); @@ -362,6 +403,17 @@ public class PmdOperation extends AbstractOperation { return numErrors; } + /** + * Sets several paths to shorten paths that are output in the report. Previous relative paths will be disregarded. + * + * @see #addRelativizeRoot(Path...) + */ + public PmdOperation relativizeRoots(Path... relativeRoot) { + this.relativizeRoots.clear(); + this.relativizeRoots.addAll(List.of(relativeRoot)); + return this; + } + /** * Sets the output format of the analysis report. The default is {@code text}. */ @@ -371,7 +423,7 @@ public class PmdOperation extends AbstractOperation { } /** - * Sets the rule set path(s). + * Sets the rule set path(s), disregarding any previously set paths. *

* The built-in rule set paths are: *

    @@ -385,6 +437,8 @@ public class PmdOperation extends AbstractOperation { *
  • {@code category/java/performance.xml}
  • *
  • {@code category/java/security.xml}
  • *
+ * + * @see #addRuleSet(String...) */ public PmdOperation ruleSets(String... ruleSet) { ruleSets.clear(); diff --git a/src/test/java/rife/bld/extension/PmdOperationTest.java b/src/test/java/rife/bld/extension/PmdOperationTest.java index 1dea825..d5e2729 100644 --- a/src/test/java/rife/bld/extension/PmdOperationTest.java +++ b/src/test/java/rife/bld/extension/PmdOperationTest.java @@ -16,11 +16,15 @@ package rife.bld.extension; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -31,113 +35,223 @@ import static org.assertj.core.api.Assertions.assertThatCode; * @author Erik C. Thauvin * @since 1.0 */ -public class PmdOperationTest { - public static final String COMMAND_NAME = "pmd"; - public static final String TEST = "test"; - static final PmdOperation pmdOperation = new PmdOperation(); +class PmdOperationTest { + static final String CODE_STYLE = "category/java/codestyle.xml"; + static final Path CODE_STYLE_SAMPLE = Path.of("src/test/resources/java/CodeStyle.java"); + static final int CODING_STYLE_ERRORS = 16; + static final String COMMAND_NAME = "pmd"; + static final String ERROR_PRONE = "category/java/errorprone.xml"; + static final int ERROR_PRONE_ERRORS = 8; + static final Path ERROR_PRONE_SAMPLE = Path.of("src/test/resources/java/ErrorProne.java"); + static final String TEST = "test"; + static final String CATEGORY_FOO = "category/foo.xml"; - @BeforeEach - void initializeOperation() { - pmdOperation.inputPaths = List.of(Paths.get("src/main")); + PmdOperation newPmdOperation() { + final PmdOperation pmdOperation = new PmdOperation(); + pmdOperation.inputPaths(Path.of("src/main"), Path.of("src/test")); pmdOperation.reportFile = Paths.get("build", COMMAND_NAME, "pmd-test-report.txt"); pmdOperation.cache = Paths.get("build", COMMAND_NAME, "pmd-cache"); pmdOperation.failOnViolation = false; + return pmdOperation; } @Test - void testConfigFile() { - var pmd = pmdOperation.ruleSets("src/test/resources/pmd.xml"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("no errors").isEqualTo(0); + void testAddInputPaths() { + var pmd = newPmdOperation().ruleSets(PmdOperation.RULE_SET_DEFAULT, CODE_STYLE).inputPaths(ERROR_PRONE_SAMPLE) + .addInputPath(CODE_STYLE_SAMPLE); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(42); } @Test - void testEmptyRuleSets() { - var pmd = pmdOperation.ruleSets(""); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("no errors").isEqualTo(0); + void testCache() { + var cache = Path.of("build/pmd/temp-cache"); + var pmd = newPmdOperation().ruleSets(CODE_STYLE).inputPaths(CODE_STYLE_SAMPLE).cache(cache); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(CODING_STYLE_ERRORS); + var f = cache.toFile(); + assertThat(f.exists()).as("file exits").isTrue(); + assertThat(f.delete()).as("delete file").isTrue(); } @Test - void testJavaQuickStart() { - var pmd = pmdOperation.ruleSets("rulesets/java/quickstart.xml"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("no errors").isEqualTo(0); + void testDebug() { + var pmd = newPmdOperation().ruleSets(CATEGORY_FOO).debug(true); + var config = pmd.initConfiguration(COMMAND_NAME); + assertThat(config.isDebug()).isTrue(); } @Test - void testJavaErrorProne() { - var pmd = pmdOperation.ruleSets("category/java/errorprone.xml"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("many errors").isGreaterThan(0); - } - - @Test - void testJavaCodeStyleAndErrorProne() { - var pmd = pmdOperation.addRuleSet("category/java/codestyle.xml", "category/java/errorprone.xml"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("many errors").isGreaterThan(0); - } - - @Test - void testJavaCodeStyle() { - var pmd = pmdOperation.ruleSets("category/java/codestyle.xml"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("many errors").isGreaterThan(0); - } - - @Test - void testJavaDesign() { - var pmd = pmdOperation.ruleSets("category/java/design.xml"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("many errors").isGreaterThan(0); - } - - @Test - void testJavaDocumentation() { - var pmd = pmdOperation.ruleSets("category/java/documentation.xml"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("many errors").isGreaterThan(0); - } - - @Test - void testJavaBestPractices() { - var pmd = pmdOperation.ruleSets("category/java/bestpractices.xml"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("no errors").isEqualTo(0); - } - - @Test - void testJavaMultiThreading() { - var pmd = pmdOperation.ruleSets("category/java/multithreading"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("no errors").isEqualTo(0); - } - - @Test - void testJavaPerformance() { - var pmd = pmdOperation.ruleSets("category/java/performance.xml"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("no errors").isEqualTo(0); - } - - @Test - void testJavaSecurity() { - var pmd = pmdOperation.ruleSets("category/java/security.xml"); - assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) - .as("no errors").isEqualTo(0); - } - - @Test - void testPmdOperation() { - assertThat(pmdOperation.performPmdAnalysis(TEST, pmdOperation.initConfiguration(COMMAND_NAME))) - .as("no errors").isEqualTo(0); + void testEncoding() { + var pmd = newPmdOperation().ruleSets(CATEGORY_FOO).encoding("UTF-16"); + var config = pmd.initConfiguration(COMMAND_NAME); + assertThat(config.getSourceEncoding()).isEqualTo(StandardCharsets.UTF_16); } @Test void testFailOnValidation() { - assertThatCode(() -> pmdOperation.ruleSets("category/java/documentation.xml").failOnViolation(true) - .performPmdAnalysis(TEST, pmdOperation.initConfiguration(COMMAND_NAME)) + var pmd = newPmdOperation().ruleSets("category/java/documentation.xml") + .inputPaths(Path.of("src/test/resources/java/Documentation.java")); + assertThatCode(() -> pmd.failOnViolation(true).performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)) ).isInstanceOf(RuntimeException.class).hasMessageContaining('[' + TEST + ']'); } + + @Test + void testIgnoreFile() { + var pmd = newPmdOperation().ruleSets(ERROR_PRONE, CODE_STYLE).inputPaths(ERROR_PRONE_SAMPLE, CODE_STYLE_SAMPLE) + .ignoreFile(Path.of("src/test/resources/ignore.txt")); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0); + } + + @Test + void testIncrementalAnalysis() { + var pmd = newPmdOperation().ruleSets(CATEGORY_FOO).incrementalAnalysis(true); + var config = pmd.initConfiguration(COMMAND_NAME); + assertThat(config.isIgnoreIncrementalAnalysis()).isFalse(); + } + + @Test + void testJavaBestPractices() { + var pmd = newPmdOperation().ruleSets("category/java/bestpractices.xml") + .inputPaths(Path.of("src/test/resources/java/BestPractices.java")); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(11); + } + + @Test + void testJavaCodeStyle() { + var pmd = newPmdOperation().ruleSets(CODE_STYLE).inputPaths(CODE_STYLE_SAMPLE); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(CODING_STYLE_ERRORS); + } + + @Test + void testJavaCodeStyleAndErrorProne() { + var pmd = newPmdOperation().ruleSets(CODE_STYLE).inputPaths(CODE_STYLE_SAMPLE); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) + .as("code style").isEqualTo(CODING_STYLE_ERRORS); + pmd = pmd.addRuleSet(ERROR_PRONE).addInputPath(ERROR_PRONE_SAMPLE); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) + .as("code style + error prone").isEqualTo(40); + } + + @Test + void testJavaDesign() { + var pmd = newPmdOperation().ruleSets("category/java/design.xml") + .inputPaths(Path.of("src/test/resources/java/Design.java")) + .cache(Path.of("build/pmd/design-cache")); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(4); + } + + @Test + void testJavaDocumentation() { + var pmd = newPmdOperation().ruleSets("category/java/documentation.xml") + .inputPaths(Path.of("src/test/resources/java/Documentation.java")); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(4); + } + + @Test + void testJavaErrorProne() { + var pmd = newPmdOperation().ruleSets(ERROR_PRONE).inputPaths(ERROR_PRONE_SAMPLE); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(ERROR_PRONE_ERRORS); + } + + @Test + void testJavaMultiThreading() { + var pmd = newPmdOperation().ruleSets("category/java/multithreading.xml") + .inputPaths(Path.of("src/test/resources/java/MultiThreading.java")); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(4); + } + + @Test + void testJavaPerformance() { + var pmd = newPmdOperation().ruleSets("category/java/performance.xml") + .inputPaths(Path.of("src/test/resources/java/Performance.java")); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(9); + } + + @Test + void testJavaQuickStart() { + var pmd = newPmdOperation().ruleSets("rulesets/java/quickstart.xml") + .inputPaths(Path.of("src/test/resources/java")); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(35); + } + + @Test + void testJavaSecurity() { + var pmd = newPmdOperation().ruleSets("category/java/security.xml") + .inputPaths(Path.of("src/test/resources/java/Security.java")); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0); + } + + @Test + void testMainOperation() { + var pmd = newPmdOperation().inputPaths(Path.of("src/main")) + .performPmdAnalysis(TEST, newPmdOperation().initConfiguration(COMMAND_NAME)); + assertThat(pmd).isEqualTo(0); + } + + @Test + void testRelativizeRoots() { + var foo = Path.of("foo/bar"); + var bar = Path.of("bar/foo"); + + var pmd = newPmdOperation().ruleSets(CATEGORY_FOO).relativizeRoots(foo).addRelativizeRoot(bar); + var config = pmd.initConfiguration(COMMAND_NAME); + assertThat(config.getRelativizeRoots()).contains(foo).contains(bar); + } + + @Test + void testReportFormat() throws IOException { + var pmd = newPmdOperation().ruleSets(ERROR_PRONE).reportFormat("xml").inputPaths(ERROR_PRONE_SAMPLE); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(ERROR_PRONE_ERRORS); + try (var br = Files.newBufferedReader(pmd.reportFile)) { + assertThat(br.readLine()).as("xml report").startsWith(""); + } + } + + @Test + void testRuleSetsConfigFile() { + var pmd = newPmdOperation().ruleSets("src/test/resources/pmd.xml") + .ignoreFile(Path.of("src/test/resources/ignore-all.txt")); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0); + } + + @Test + void testRuleSetsEmpty() { + var pmd = newPmdOperation().ruleSets(""); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0); + } + + @Test + void testShowSuppressed() { + var pmd = newPmdOperation().ruleSets(ERROR_PRONE).showSuppressed(true); + var config = pmd.initConfiguration(COMMAND_NAME); + assertThat(config.isShowSuppressedViolations()).isTrue(); + } + + @Test + void testSuppressedMarker() { + var pmd = newPmdOperation().ruleSets(ERROR_PRONE).suppressedMarker(TEST); + var config = pmd.initConfiguration(COMMAND_NAME); + assertThat(config.getSuppressMarker()).isEqualTo(TEST); + } + + @Test + void testThreads() { + var pmd = newPmdOperation().ruleSets(ERROR_PRONE).threads(5); + var config = pmd.initConfiguration(COMMAND_NAME); + assertThat(config.getThreads()).isEqualTo(5); + } + + @Test + void testUri() throws URISyntaxException { + var uri = new URI("https://example.com"); + var pmd = newPmdOperation().ruleSets(ERROR_PRONE).uri(uri); + var config = pmd.initConfiguration(COMMAND_NAME); + assertThat(config.getUri()).isEqualTo(uri); + } + + @Test + void testXml() { + var pmd = newPmdOperation().inputPaths(Path.of("src/test/resources/pmd.xml")) + .ruleSets("src/test/resources/xml/basic.xml"); + assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0); + } } diff --git a/src/test/resources/ignore-all.txt b/src/test/resources/ignore-all.txt new file mode 100644 index 0000000..e1073ef --- /dev/null +++ b/src/test/resources/ignore-all.txt @@ -0,0 +1 @@ +src/test/resources/java/ \ No newline at end of file diff --git a/src/test/resources/ignore.txt b/src/test/resources/ignore.txt new file mode 100644 index 0000000..4fca46e --- /dev/null +++ b/src/test/resources/ignore.txt @@ -0,0 +1,2 @@ +src/test/resources/java/ErrorProne.java +src/test/resources/java/CodeStyle.java \ No newline at end of file diff --git a/src/test/resources/java/BestPractices.java b/src/test/resources/java/BestPractices.java new file mode 100644 index 0000000..a7de345 --- /dev/null +++ b/src/test/resources/java/BestPractices.java @@ -0,0 +1,50 @@ +/* + * 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 java;/** + * BestPractices class + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public class BestPractices { + private final String ip = "127.0.0.1"; // not recommended + private StringBuffer buffer; // potential memory leak as an instance variable; + private String[] x; + + void bar(int a) { + switch (a) { + case 1: // do something + break; + default: // the default case should be last, by convention + break; + case 2: + break; + } + } + + public void foo(String[] param) { + // Don't do this, make a copy of the array at least + this.x = param; + } + + private void greet(String name) { + name = name.trim(); + System.out.println("Hello " + name); + } + + +} diff --git a/src/test/resources/java/CodeStyle.java b/src/test/resources/java/CodeStyle.java new file mode 100644 index 0000000..98d1525 --- /dev/null +++ b/src/test/resources/java/CodeStyle.java @@ -0,0 +1,46 @@ +/* + * 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 java; + +/** + * CodeStyle class + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public final class CodeStyle { + final int FinalField = 1; + private int x; + private int y; // class cannot be subclassed, so is y really private or package visible? + + // missing constructor + + boolean bar(int x, int y) { + try { + x = y; + } catch (IllegalArgumentException e) { + throw e; + } catch (IllegalStateException e) { // Can be collapsed into the previous block + throw e; + } + + if (true) ; // empty if statement + if (true) { // empty as well + } + return x == y; + } +} diff --git a/src/test/resources/java/Design.java b/src/test/resources/java/Design.java new file mode 100644 index 0000000..42068de --- /dev/null +++ b/src/test/resources/java/Design.java @@ -0,0 +1,40 @@ +/* + * 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 java;/** + * Design class + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public class Design { + String field; + int otherField; + + void foo() { + throw new NullPointerException(); + } + + public void bar(int x, int y, int z) { + if (x > y) { + if (y > z) { + if (z == x) { + // !! too deep + } + } + } + } +} diff --git a/src/test/resources/java/Documentation.java b/src/test/resources/java/Documentation.java new file mode 100644 index 0000000..2c90f8a --- /dev/null +++ b/src/test/resources/java/Documentation.java @@ -0,0 +1,28 @@ +/* + * 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 java;/** + * @author Erik C. Thauvin + * @since 1.0 + */ +public class Documentation { + public Documentation() { + // This constructor is intentionally empty. Nothing special is needed here. + } + + public void doSomething() { + } +} diff --git a/src/test/resources/java/ErrorProne.java b/src/test/resources/java/ErrorProne.java new file mode 100644 index 0000000..0b042d8 --- /dev/null +++ b/src/test/resources/java/ErrorProne.java @@ -0,0 +1,52 @@ +/* + * 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 java;/** + * ErrorProne class + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public class ErrorProne { + static int x; + + public ErrorProne(int y) { + x = y; // unsafe + + // unusual use of branching statement in a loop + for (int i = 0; i < 10; i++) { + if (i * i <= 25) { + continue; + } + break; + } + } + + void foo() { + try { + // do something + } catch (Throwable th) { // should not catch Throwable + th.printStackTrace(); + } + } + + void bar() { + try { + // do something + } catch (NullPointerException npe) { + } + } +} diff --git a/src/test/resources/java/MultiThreading.java b/src/test/resources/java/MultiThreading.java new file mode 100644 index 0000000..6dfc1fc --- /dev/null +++ b/src/test/resources/java/MultiThreading.java @@ -0,0 +1,50 @@ +/* + * 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 java;import java.text.SimpleDateFormat; + +/** + * MultiThreading class + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public class MultiThreading { + private static final SimpleDateFormat sdf = new SimpleDateFormat(); + /*volatile */ Object baz = null; // fix for Java5 and later: volatile + private volatile String var1; // not suggested + + void bar() { + sdf.format("bar"); // poor, no thread-safety + } + + void foo() { + synchronized (sdf) { // preferred + sdf.format("foo"); + } + } + + Object obj() { + if (baz == null) { // baz may be non-null yet not fully created + synchronized (this) { + if (baz == null) { + baz = new Object(); + } + } + } + return baz; + } +} diff --git a/src/test/resources/java/Performance.java b/src/test/resources/java/Performance.java new file mode 100644 index 0000000..82b9a0b --- /dev/null +++ b/src/test/resources/java/Performance.java @@ -0,0 +1,55 @@ +/* + * 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 java;/** + * Performance class + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public class Performance { + public static void main(String[] as) { + for (int i = 0; i < 10; i++) { + Performance p = new Performance(); // Avoid this whenever you can it's really expensive + } + } + + + private boolean checkTrimEmpty(String str) { + for (int i = 0; i < str.length(); i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + void foo() { + StringBuffer sb = new StringBuffer(); + sb.append("a"); // avoid this + + String foo = " "; + StringBuffer buf = new StringBuffer(); + buf.append("Hello"); // poor + buf.append(foo); + buf.append("World"); + + buf.append("Hello").append(" ").append("World"); + + StringBuffer sbuf = new StringBuffer("tmp = " + System.getProperty("java.io.tmpdir")); +// poor + } +} diff --git a/src/test/resources/java/Security.java b/src/test/resources/java/Security.java new file mode 100644 index 0000000..a083b2c --- /dev/null +++ b/src/test/resources/java/Security.java @@ -0,0 +1,34 @@ +/* + * 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 java;import java.security.SecureRandom; + +/** + * Security class + * + * @author Erik C. Thauvin + * @since 1.0 + */ +public class Security { + void bad() { + byte[] iv = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, }; + } + + void alsoBad() { + byte[] iv = "secret iv in here".getBytes(); + } + +} diff --git a/src/test/resources/xml/basic.xml b/src/test/resources/xml/basic.xml new file mode 100644 index 0000000..3527b42 --- /dev/null +++ b/src/test/resources/xml/basic.xml @@ -0,0 +1,42 @@ + + + + + The Basic ruleset contains a collection of good practices which should be followed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file