diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 56ec52f9ab..a9ce1bfda6 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -49,6 +49,7 @@ For the changes, see [PMD Designer Changelog](https://github.com/pmd/pmd-designe * core * [#2014](https://github.com/pmd/pmd/issues/2014): \[core] Making add(SourceCode sourceCode) public for alternative file systems + * [#2020](https://github.com/pmd/pmd/issues/2020): \[core] Wrong deprecation warnings for unused XPath attributes * [#2036](https://github.com/pmd/pmd/issues/2036): \[core] Wrong include/exclude patterns are silently ignored * [#2067](https://github.com/pmd/pmd/issues/2067): \[core] Build issue on Windows * [#2068](https://github.com/pmd/pmd/pull/2068): \[core] Rule loader should use the same resources loader for the ruleset diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java index f37ca7e215..85cca22010 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java @@ -6,6 +6,8 @@ package net.sourceforge.pmd.lang.ast.xpath; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -36,7 +38,7 @@ public class Attribute { private final Node parent; private final String name; private Method method; - private Object value; + private List value; private String stringValue; /** Creates a new attribute belonging to the given node using its accessor. */ @@ -50,7 +52,7 @@ public class Attribute { public Attribute(Node parent, String name, String value) { this.parent = parent; this.name = name; - this.value = value; + this.value = Collections.singletonList(value); this.stringValue = value; } @@ -71,20 +73,20 @@ public class Attribute { } public Object getValue() { - if (value != null) { // TODO if the method returned null we'll call it again... - return value; + if (value != null) { + return value.get(0); } if (method.isAnnotationPresent(Deprecated.class) && LOG.isLoggable(Level.WARNING) && DETECTED_DEPRECATED_ATTRIBUTES.putIfAbsent(getLoggableAttributeName(), Boolean.TRUE) == null) { - // this message needs to be kept in sync with PMDCoverageTest + // this message needs to be kept in sync with PMDCoverageTest / BinaryDistributionIT LOG.warning("Use of deprecated attribute '" + getLoggableAttributeName() + "' in XPath query"); } // this lazy loading reduces calls to Method.invoke() by about 90% try { - value = method.invoke(parent, EMPTY_OBJ_ARRAY); - return value; + value = Collections.singletonList(method.invoke(parent, EMPTY_OBJ_ARRAY)); + return value.get(0); } catch (IllegalAccessException | InvocationTargetException iae) { iae.printStackTrace(); } @@ -95,10 +97,8 @@ public class Attribute { if (stringValue != null) { return stringValue; } - Object v = this.value; - if (this.value == null) { - v = getValue(); - } + Object v = getValue(); + stringValue = v == null ? "" : String.valueOf(v); return stringValue; } diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AbstractBinaryDistributionTest.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AbstractBinaryDistributionTest.java new file mode 100644 index 0000000000..692d40378a --- /dev/null +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AbstractBinaryDistributionTest.java @@ -0,0 +1,44 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.it; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.commons.io.FileUtils; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import net.sourceforge.pmd.PMDVersion; + +public abstract class AbstractBinaryDistributionTest { + + protected static File getBinaryDistribution() { + return new File(".", "target/pmd-bin-" + PMDVersion.VERSION + ".zip"); + } + + /** + * The temporary directory, to which the binary distribution will be extracted. + * It will be deleted again after the test. + */ + protected static Path tempDir; + + @BeforeClass + public static void setupTempDirectory() throws Exception { + tempDir = Files.createTempDirectory("pmd-it-test-"); + if (getBinaryDistribution().exists()) { + ZipFileExtractor.extractZipFile(getBinaryDistribution().toPath(), tempDir); + } + } + + @AfterClass + public static void cleanupTempDirectory() throws IOException { + if (tempDir != null && tempDir.toFile().exists()) { + FileUtils.forceDelete(tempDir.toFile()); + } + } +} diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AllRulesIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AllRulesIT.java new file mode 100644 index 0000000000..4819106502 --- /dev/null +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AllRulesIT.java @@ -0,0 +1,46 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.it; + +import java.io.File; +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class AllRulesIT extends AbstractBinaryDistributionTest { + + @Parameter + public String language; + + @Parameters + public static Iterable languagesToTest() { + // note: scala and wsdl have no rules + return Arrays.asList("java", "apex", "javascript", "jsp", "plsql", "pom", "visualforce", "velocitytemplate", "xml", "xsl"); + } + + @Test + public void runRuleTests() throws Exception { + String srcDir = new File(".", "src/test/resources/sample-source/" + language + "/").getAbsolutePath(); + + ExecutionResult result = PMDExecutor.runPMDRules(tempDir, srcDir, "src/test/resources/rulesets/all-" + + language + ".xml"); + assertDefaultExecutionResult(result); + } + + private static void assertDefaultExecutionResult(ExecutionResult result) { + result.assertExecutionResult(4, ""); + + result.assertNoError("Exception applying rule"); + result.assertNoError("Ruleset not found"); + result.assertNoError("Use of deprecated attribute"); + result.assertNoErrorInReport("Error while processing"); + result.assertNoErrorInReport("Error while parsing"); + } +} diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java index 23d8e90829..8974f28c8d 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java @@ -9,47 +9,17 @@ import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import org.apache.commons.io.FileUtils; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; import net.sourceforge.pmd.PMDVersion; -public class BinaryDistributionIT { - - private static File getBinaryDistribution() { - return new File(".", "target/pmd-bin-" + PMDVersion.VERSION + ".zip"); - } - - /** - * The temporary directory, to which the binary distribution will be extracted. - * It will be deleted again after the test. - */ - private static Path tempDir; - - @BeforeClass - public static void setupTempDirectory() throws Exception { - tempDir = Files.createTempDirectory("pmd-it-test-"); - if (getBinaryDistribution().exists()) { - ZipFileExtractor.extractZipFile(getBinaryDistribution().toPath(), tempDir); - } - } - - @AfterClass - public static void cleanupTempDirectory() throws IOException { - if (tempDir != null && tempDir.toFile().exists()) { - FileUtils.forceDelete(tempDir.toFile()); - } - } +public class BinaryDistributionIT extends AbstractBinaryDistributionTest { @Test public void testFileExistence() { @@ -90,7 +60,7 @@ public class BinaryDistributionIT { @Test public void runPMD() throws Exception { - String srcDir = new File(".", "src/test/resources/sample-source/").getAbsolutePath(); + String srcDir = new File(".", "src/test/resources/sample-source/java/").getAbsolutePath(); ExecutionResult result; @@ -99,7 +69,7 @@ public class BinaryDistributionIT { result.assertExecutionResult(0, "apex, ecmascript, java, jsp, plsql, pom, scala, text, vf, vm, wsdl, xml, xsl"); result = PMDExecutor.runPMDRules(tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml"); - result.assertExecutionResult(4, "JumbledIncrementer.java:8:"); + result.assertExecutionResult(4, "", "JumbledIncrementer.java:8:"); result = PMDExecutor.runPMDRules(tempDir, srcDir, "rulesets/java/quickstart.xml"); result.assertExecutionResult(4, ""); diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/CpdExecutor.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/CpdExecutor.java index 42e80430ac..b7c25f023e 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/CpdExecutor.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/CpdExecutor.java @@ -34,7 +34,7 @@ public class CpdExecutor { String output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); int result = process.waitFor(); - return new ExecutionResult(result, output); + return new ExecutionResult(result, output, null, null); } private static ExecutionResult runCpdWindows(Path tempDir, String ... arguments) throws Exception { @@ -46,7 +46,7 @@ public class CpdExecutor { String output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); int result = process.waitFor(); - return new ExecutionResult(result, output); + return new ExecutionResult(result, output, null, null); } /** diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/ExecutionResult.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/ExecutionResult.java index 4e7c8134f1..04efb3971b 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/ExecutionResult.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/ExecutionResult.java @@ -5,7 +5,9 @@ package net.sourceforge.pmd.it; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import net.sourceforge.pmd.PMD; @@ -18,10 +20,14 @@ import net.sourceforge.pmd.PMD; public class ExecutionResult { private final int exitCode; private final String output; + private final String errorOutput; + private final String report; - ExecutionResult(int theExitCode, String theOutput) { + ExecutionResult(int theExitCode, String theOutput, String theErrorOutput, String theReport) { this.exitCode = theExitCode; this.output = theOutput; + this.errorOutput = theErrorOutput; + this.report = theReport; } @Override @@ -30,7 +36,9 @@ public class ExecutionResult { sb.append("ExecutionResult:") .append(PMD.EOL) .append(" exit code: ").append(exitCode).append(PMD.EOL) - .append(" output:").append(PMD.EOL).append(output).append(PMD.EOL); + .append(" output:").append(PMD.EOL).append(output).append(PMD.EOL) + .append(" errorOutput:").append(PMD.EOL).append(errorOutput).append(PMD.EOL) + .append(" report:").append(PMD.EOL).append(report).append(PMD.EOL); return sb.toString(); } @@ -42,10 +50,80 @@ public class ExecutionResult { * @param expectedOutput the output to search for */ public void assertExecutionResult(int expectedExitCode, String expectedOutput) { - assertEquals("Command exited with wrong code", expectedExitCode, exitCode); + assertExecutionResult(expectedExitCode, expectedOutput, null); + } + + /** + * Asserts that the command exited with the expected exit code and that the given expected + * output is contained in the actual command output and the given expected report is in the + * generated report. + * + * @param expectedExitCode the exit code, e.g. 0 if no rule violations are expected, or 4 if violations are found + * @param expectedOutput the output to search for + * @param expectedReport the string to search for tin the report + */ + public void assertExecutionResult(int expectedExitCode, String expectedOutput, String expectedReport) { + assertEquals("Command exited with wrong code.\nComplete result:\n\n" + this, expectedExitCode, exitCode); assertNotNull("No output found", output); - if (!output.contains(expectedOutput)) { - fail("Expected output '" + expectedOutput + "' not present.\nComplete output:\n\n" + output); + if (expectedOutput != null && !expectedOutput.isEmpty()) { + if (!output.contains(expectedOutput)) { + fail("Expected output '" + expectedOutput + "' not present.\nComplete result:\n\n" + this); + } + } else { + assertTrue("The output should have been empty.\nComplete result:\n\n" + this, output.isEmpty()); + } + if (expectedReport != null && !expectedReport.isEmpty()) { + assertTrue("Expected report '" + expectedReport + "'.\nComplete result:\n\n" + this, + report.contains(expectedReport)); + } + } + + /** + * Asserts that the given error message is not in the error output. + * @param errorMessage the error message to search for + */ + public void assertNoError(String errorMessage) { + assertFalse("Found error message: " + errorMessage + ".\nComplete result:\n\n" + this, + errorOutput.contains(errorMessage)); + } + + /** + * Asserts that the given error message is not in the report. + * @param errorMessage the error message to search for + */ + public void assertNoErrorInReport(String errorMessage) { + assertFalse("Found error message in report: " + errorMessage + ".\nComplete result:\n\n" + this, + report.contains(errorMessage)); + } + + static class Builder { + private int exitCode; + private String output; + private String errorOutput; + private String report; + + Builder withExitCode(int exitCode) { + this.exitCode = exitCode; + return this; + } + + Builder withOutput(String output) { + this.output = output; + return this; + } + + Builder withErrorOutput(String errorOutput) { + this.errorOutput = errorOutput; + return this; + } + + Builder withReport(String report) { + this.report = report; + return this; + } + + ExecutionResult build() { + return new ExecutionResult(exitCode, output, errorOutput, report); } } } diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java index 1e127a1f0f..05bd0a9755 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java @@ -4,9 +4,13 @@ package net.sourceforge.pmd.it; +import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.SystemUtils; @@ -24,33 +28,65 @@ public class PMDExecutor { private static final String RULESET_FLAG = "-R"; private static final String FORMAT_FLAG = "-f"; private static final String FORMATTER = "text"; + private static final String REPORTFILE_FLAG = "-r"; private PMDExecutor() { // this is a helper class only } - private static ExecutionResult runPMDUnix(Path tempDir, String ... arguments) throws Exception { + private static ExecutionResult runPMDUnix(Path tempDir, Path reportFile, String ... arguments) throws Exception { String cmd = tempDir.resolve(PMD_BIN_PREFIX + PMDVersion.VERSION + "/bin/run.sh").toAbsolutePath().toString(); - ProcessBuilder pb = new ProcessBuilder(cmd, "pmd"); - pb.command().addAll(Arrays.asList(arguments)); - pb.redirectErrorStream(true); - Process process = pb.start(); - String output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); - - int result = process.waitFor(); - return new ExecutionResult(result, output); + return runPMD(cmd, Arrays.asList(arguments), reportFile); } - private static ExecutionResult runPMDWindows(Path tempDir, String ... arguments) throws Exception { + private static ExecutionResult runPMDWindows(Path tempDir, Path reportFile, String ... arguments) throws Exception { String cmd = tempDir.resolve(PMD_BIN_PREFIX + PMDVersion.VERSION + "/bin/pmd.bat").toAbsolutePath().toString(); - ProcessBuilder pb = new ProcessBuilder(cmd); - pb.command().addAll(Arrays.asList(arguments)); - pb.redirectErrorStream(true); - Process process = pb.start(); - String output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); + return runPMD(cmd, Arrays.asList(arguments), reportFile); + } - int result = process.waitFor(); - return new ExecutionResult(result, output); + private static ExecutionResult runPMD(String cmd, List arguments, Path reportFile) throws Exception { + ProcessBuilder pb = new ProcessBuilder(cmd, "pmd"); + pb.command().addAll(arguments); + pb.redirectErrorStream(false); + final Process process = pb.start(); + final ExecutionResult.Builder result = new ExecutionResult.Builder(); + + Thread outputReader = new Thread(new Runnable() { + @Override + public void run() { + String output; + try { + output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); + result.withOutput(output); + } catch (IOException e) { + result.withOutput("Exception occurred: " + e.toString()); + } + } + }); + outputReader.start(); + Thread errorReader = new Thread(new Runnable() { + @Override + public void run() { + String error; + try { + error = IOUtils.toString(process.getErrorStream(), StandardCharsets.UTF_8); + result.withErrorOutput(error); + } catch (IOException e) { + result.withErrorOutput("Exception occurred: " + e.toString()); + } + } + }); + errorReader.start(); + + int exitCode = process.waitFor(); + outputReader.join(TimeUnit.MINUTES.toMillis(5)); + errorReader.join(TimeUnit.MINUTES.toMillis(5)); + + String report = null; + if (reportFile != null) { + report = IOUtils.toString(reportFile.toUri(), StandardCharsets.UTF_8); + } + return result.withExitCode(exitCode).withReport(report).build(); } /** @@ -63,10 +99,15 @@ public class PMDExecutor { * @throws Exception if the execution fails for any reason (executable not found, ...) */ public static ExecutionResult runPMDRules(Path tempDir, String sourceDirectory, String ruleset) throws Exception { + Path reportFile = Files.createTempFile("pmd-it-report", "txt"); + reportFile.toFile().deleteOnExit(); + if (SystemUtils.IS_OS_WINDOWS) { - return runPMDWindows(tempDir, SOURCE_DIRECTORY_FLAG, sourceDirectory, RULESET_FLAG, ruleset, FORMAT_FLAG, FORMATTER); + return runPMDWindows(tempDir, reportFile, SOURCE_DIRECTORY_FLAG, sourceDirectory, RULESET_FLAG, ruleset, + FORMAT_FLAG, FORMATTER, REPORTFILE_FLAG, reportFile.toAbsolutePath().toString()); } else { - return runPMDUnix(tempDir, SOURCE_DIRECTORY_FLAG, sourceDirectory, RULESET_FLAG, ruleset, FORMAT_FLAG, FORMATTER); + return runPMDUnix(tempDir, reportFile, SOURCE_DIRECTORY_FLAG, sourceDirectory, RULESET_FLAG, ruleset, + FORMAT_FLAG, FORMATTER, REPORTFILE_FLAG, reportFile.toAbsolutePath().toString()); } } @@ -79,9 +120,9 @@ public class PMDExecutor { */ public static ExecutionResult runPMD(Path tempDir, String ... arguments) throws Exception { if (SystemUtils.IS_OS_WINDOWS) { - return runPMDWindows(tempDir, arguments); + return runPMDWindows(tempDir, null, arguments); } else { - return runPMDUnix(tempDir, arguments); + return runPMDUnix(tempDir, null, arguments); } } } diff --git a/pmd-dist/src/test/resources/rulesets/all-apex.xml b/pmd-dist/src/test/resources/rulesets/all-apex.xml new file mode 100644 index 0000000000..92fe0705e8 --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/all-apex.xml @@ -0,0 +1,18 @@ + + + + Every Apex Rule in PMD + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/rulesets/all-java.xml b/pmd-dist/src/test/resources/rulesets/all-java.xml new file mode 100644 index 0000000000..a091ea021b --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/all-java.xml @@ -0,0 +1,18 @@ + + + + Every Java Rule in PMD + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/rulesets/all-javascript.xml b/pmd-dist/src/test/resources/rulesets/all-javascript.xml new file mode 100644 index 0000000000..db9e425e76 --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/all-javascript.xml @@ -0,0 +1,18 @@ + + + + Every JavaScript Rule in PMD + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/rulesets/all-jsp.xml b/pmd-dist/src/test/resources/rulesets/all-jsp.xml new file mode 100644 index 0000000000..9b48bd0577 --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/all-jsp.xml @@ -0,0 +1,18 @@ + + + + Every JSP Rule in PMD + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/rulesets/all-plsql.xml b/pmd-dist/src/test/resources/rulesets/all-plsql.xml new file mode 100644 index 0000000000..ba43c139be --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/all-plsql.xml @@ -0,0 +1,18 @@ + + + + Every PLSQL Rule in PMD + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/rulesets/all-pom.xml b/pmd-dist/src/test/resources/rulesets/all-pom.xml new file mode 100644 index 0000000000..fe65645d55 --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/all-pom.xml @@ -0,0 +1,18 @@ + + + + Every Maven POM Rule in PMD + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/rulesets/all-velocitytemplate.xml b/pmd-dist/src/test/resources/rulesets/all-velocitytemplate.xml new file mode 100644 index 0000000000..7918dde8b7 --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/all-velocitytemplate.xml @@ -0,0 +1,18 @@ + + + + Every Velocity Template Language Rule in PMD + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/rulesets/all-visualforce.xml b/pmd-dist/src/test/resources/rulesets/all-visualforce.xml new file mode 100644 index 0000000000..ff68553962 --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/all-visualforce.xml @@ -0,0 +1,18 @@ + + + + Every Visualforce Rule in PMD + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/rulesets/all-xml.xml b/pmd-dist/src/test/resources/rulesets/all-xml.xml new file mode 100644 index 0000000000..6b230c138e --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/all-xml.xml @@ -0,0 +1,18 @@ + + + + Every XML Rule in PMD + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/rulesets/all-xsl.xml b/pmd-dist/src/test/resources/rulesets/all-xsl.xml new file mode 100644 index 0000000000..ced819fdce --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/all-xsl.xml @@ -0,0 +1,18 @@ + + + + Every XSL Rule in PMD + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/sample-source/apex/TableGridController.cls b/pmd-dist/src/test/resources/sample-source/apex/TableGridController.cls new file mode 100644 index 0000000000..a84b10ef72 --- /dev/null +++ b/pmd-dist/src/test/resources/sample-source/apex/TableGridController.cls @@ -0,0 +1,371 @@ +/* +Copyright (c) 2013 Up2Go International LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * DOCUMENTATION MISSING //TODO + * + * @author Robert Soesemann (robert.soesemann@up2go.com) + */ +public with sharing class TableGridController { + + // COMPONENT ATTRIBUTES + public String typeParam { get; set; } + public String fieldsParam { get; set; } + public String filterParam { get; set; } + public Boolean sortDescParam { get; set; } + public Integer pageSizeParam { get; set; } + public String modeParam { get; set; } + public String sortByParam { get; set; } + public List selectedIds { get; set; } + + public String gridIdParam { get; set; } + public Boolean saveSettingsParam { + get { + return (saveSettingsParam == null) ? false : saveSettingsParam; + } + set; + } + + public String lookupFieldValue { get; set; } + + + // MEMBER VARIABLES + public SoqlQuery soqlQuery { get; set; } + public RowManager rowManager { get; set; } + public Boolean isInitialized { get; private set; } + public String previousSortField {get; set;} + public String objectLabel { get; private set; } + public String objectLabelPlural { get; private set; } + public Boolean noneSelected { get; private set; } + public Boolean allSelected { get; set; } + public String parentFrameUrl { get; set; } + public String currentMode { get; set; } + public String currentPageUrl { + get { + PageReference currentPage = ApexPages.currentPage(); + currentPage.getParameters().remove('AJAXREQUEST'); + return currentPage.getUrl(); + } + private set; + } + public Boolean isPageLoaded { get; private set; } + + private TableGridState__c settings; + + + // COMPONENT INIT JOEL DIETZ STYLE + + public void getInit() { + isPageLoaded = false; + + typeParam = TableGridUtils.normalize(typeParam); + gridIdParam = TableGridUtils.normalize(gridIdParam); + + try { + // Try to load user's saved settings for this grid instance + settings = getSettings(); + + // Init from saved settings if it exists + if(settings.Id != null) { + String fieldNames = settings.txtl_FieldNames__c; + soqlQuery = new SoqlQuery(typeParam, fieldNames); + + // Always use the components initial filter + if(filterParam != null) { + soqlQuery.filter(filterParam); + } + // Recreate additional filter statements + String filterStatements = settings.txtl_FilterStatements__c; + + if(filterStatements != null) { + for(String statement : filterStatements.split(';')) { + String[] fragments = statement.split(','); + + try { + FilterStatement newStatement = new FilterStatement(typeParam, fragments[0], fragments[1], fragments[2]) ; + soqlQuery.filterStatements.add( newStatement ); + } + catch(Exception initException) { + //Just do not add Filter + } + } + } + // Order By + if(settings.txt_SortBy__c != null) { + soqlQuery.orderBy(settings.txt_SortBy__c, settings.chk_SortDescending__c == null ? false : settings.chk_SortDescending__c); + } + + currentMode = settings.txt_Mode__c; + } + // Otherwise init from component attributes + else { + fieldsParam = TableGridUtils.normalize(fieldsParam); + + soqlQuery = new SoqlQuery(typeParam, fieldsParam); + if(filterParam != null) { + soqlQuery.filter(filterParam); + } + if(sortByParam != null) { + soqlQuery.orderBy(sortByParam, sortDescParam == null ? false : sortDescParam); + } + currentMode = modeParam; + } + objectLabel = SchemaCache.objectDescribe(typeParam).getLabel(); + objectLabelPlural = SchemaCache.objectDescribe(typeParam).getLabelPlural(); + noneSelected = true; + + // Add lookup filter as editable statement + if(currentMode == 'singleselect' && lookupFieldValue != null) { + FilterStatement newStatement = new FilterStatement(typeParam, 'Name', 'contains', lookupFieldValue) ; + + // If Statement does not already exists + Boolean alreadyExists = false; + for(FilterStatement fs : soqlQuery.filterStatements) { + if(fs.hashcode.equals(newStatement.hashcode)) { + alreadyExists = true; + break; + } + } + // Add it to SOQLQuery + if(!alreadyExists) { + soqlQuery.filterStatements.add( newStatement ); + } + } + } + catch(Exception ex) { + showMessage(ApexPages.Severity.FATAL, 'TableGrid Initialization Error: ' + ex.getMessage() + ' (Please contact your administrator)'); + return; + } + + // Create RowManager from query + rowManager = new RowManager(soqlQuery, settings); + + previousSortField = soqlQuery.sortFieldName; + + // Mark initialisation as successful + isInitialized = true; + + allSelected = false; + } + + + // ACTIONS + + public void doSort() { + // Flip sort direction if sort field is unchanged + if(soqlQuery.sortFieldName == previousSortField) { + soqlQuery.sortDescending = !soqlQuery.sortDescending; + } + else { + soqlQuery.sortDescending = true; + } + + // Refetch rows + rowManager.fetchRows(soqlQuery); + previousSortField = soqlQuery.sortFieldName; + + // Save settings + saveSettings(); + } + + public PageReference doEditNew() { + String typePrefix = SchemaCache.objectDescribe(typeParam).getKeyPrefix(); + PageReference page = new PageReference('/' + typePrefix + '/e'); + + page.getParameters().put('retURL', parentFrameUrl); + page.getParameters().put('cancelURL', parentFrameUrl); + page.getParameters().put('saveURL', parentFrameUrl); + + return page; + } + + public void doSaveSelected() { + try { + List toUpdate = new List(); + + for(SObjectRow row: rowManager.rows) { + if(row.isSelected) { + toUpdate.add(row.delegate); + } + } + if(!toUpdate.isEmpty()) { + update toUpdate; + showMessage(ApexPages.Severity.INFO, 'Successfully updated changed records'); + } + } + catch(Exception ex){ + showMessage(ApexPages.Severity.ERROR, ex.getMessage()); + } + + doRefresh(); + } + + + public void doDeleteSelected() { + try { + List toDelete = new List(); + + for(SObjectRow row: rowManager.rows) { + if(row.isSelected) { + toDelete.add(row.delegate); + } + } + delete toDelete; + } + catch(Exception ex){ + showMessage(ApexPages.Severity.ERROR, ex.getMessage()); + } + + doRefresh(); + } + + public void doRefresh() { + // Update field names and refetchs rows + soqlQuery.updateFieldNames(); + rowManager.fetchRows(soqlQuery); + + saveSettings(); + } + + public void doLoadDefaults() { + if(settings.Id != null) { + delete settings; + } + getInit(); + } + + public void doChangeMode() { + // Mode set already by apex:param that called this action + + saveSettings(); + } + + + public void doHandleSelection() { + noneSelected = true; + if(selectedIds!=null) + selectedIds.clear(); + else + selectedIds = new List(); + + for(SObjectRow row : rowManager.rows) { + if(row.isSelected) { + noneSelected = false; + + if(currentMode=='select') { + selectedIds.add(row.delegate.Id); + } + } + } + } + + + /** + * Save settings override into users custom settings + */ + private void saveSettings() { + if(saveSettingsParam == true) { + // SELECT + settings.txtl_FieldNames__c = soqlQuery.fieldNames; + + // WHERE + String serializedStatements = ''; + Iterator iter = soqlQuery.filterStatements.iterator(); + while(iter.hasNext()) { + serializedStatements += iter.next().asStringSetting(); + serializedStatements += iter.hasNext() ? ';' : ''; + } + settings.txtl_FilterStatements__c = serializedStatements; + + // ORDER BY + settings.txt_SortBy__c = previousSortField; + settings.chk_SortDescending__c = soqlQuery.sortDescending; + + // PAGINATION + settings.num_PageSize__c = rowManager.pageSize; + settings.num_PageNumber__c = rowManager.pageNumber; + + // MODE + settings.txt_Mode__c = currentMode; + + upsert settings; + } + } + + /** + * Returns existing (in the db) or new Settings object for this tablegrid + */ + private TableGridState__c getSettings() { + TableGridState__c settings; + String currentPage; + String uniqueGridId; + + // Set instance and page id dependant on calling context + if(Test.isRunningTest()) { + currentPage = 'calledWithoutPage'; + uniqueGridId = 'uniqueGridId'; + } + else { + String wholeUrl = ApexPages.currentPage().getUrl(); + Integer firstPos = wholeUrl.lastIndexOf('/'); + Integer lastPos = wholeUrl.indexOf('?', firstPos); + if(lastPos == -1) { + lastPos = wholeUrl.length(); + } + currentPage = wholeUrl.substring(firstPos, lastPos); + uniqueGridId = gridIdParam; + } + + settings = new TableGridState__c(lkp_User__c = UserInfo.getUserId(), + txt_PageUrl__c = currentPage, + txt_GridId__c = gridIdParam, + num_PageSize__c = pageSizeParam); + + if(saveSettingsParam == true) { + // Check if settings are saved in the database + List fromDatabase = [SELECT txtl_FieldNames__c, txtl_FilterStatements__c, txt_SortBy__c, chk_SortDescending__c, num_PageSize__c, num_PageNumber__c, txt_Mode__c + FROM TableGridState__c + WHERE txt_GridId__c = :uniqueGridId + AND txt_PageUrl__c = :currentPage + AND lkp_User__c = :UserInfo.getUserId() + LIMIT 1]; + + if(fromDatabase != null && !fromDatabase.isEmpty()) { + settings = fromDatabase[0]; + } + } + + return settings; + } + + + private void showMessage(ApexPages.Severity severity, String messageText) { + ApexPages.Message message = new ApexPages.Message(severity, messageText); + ApexPages.addMessage(message); + } +} diff --git a/pmd-dist/src/test/resources/sample-source/JumbledIncrementer.java b/pmd-dist/src/test/resources/sample-source/java/JumbledIncrementer.java similarity index 100% rename from pmd-dist/src/test/resources/sample-source/JumbledIncrementer.java rename to pmd-dist/src/test/resources/sample-source/java/JumbledIncrementer.java diff --git a/pmd-dist/src/test/resources/sample-source/javascript/SampleCode.js b/pmd-dist/src/test/resources/sample-source/javascript/SampleCode.js new file mode 100644 index 0000000000..fc36006b11 --- /dev/null +++ b/pmd-dist/src/test/resources/sample-source/javascript/SampleCode.js @@ -0,0 +1,7 @@ +(function() { + if (true) { + x=2; + } else { + x=4; + } +})(); diff --git a/pmd-dist/src/test/resources/sample-source/jsp/SampleCode.jsp b/pmd-dist/src/test/resources/sample-source/jsp/SampleCode.jsp new file mode 100644 index 0000000000..1a1c2508ce --- /dev/null +++ b/pmd-dist/src/test/resources/sample-source/jsp/SampleCode.jsp @@ -0,0 +1,12 @@ +<%@ page errorPage="error.jsp" %> +<%@ page import="com.foo.AClass"%> +<%@ page import="com.foo.MyClass"%> + + + + + xx + text + + + diff --git a/pmd-dist/src/test/resources/sample-source/plsql/SampleCode.pls b/pmd-dist/src/test/resources/sample-source/plsql/SampleCode.pls new file mode 100644 index 0000000000..137703c569 --- /dev/null +++ b/pmd-dist/src/test/resources/sample-source/plsql/SampleCode.pls @@ -0,0 +1,14 @@ +-- +-- Example 2-25 Assigning Value to Variable with SELECT INTO Statement +-- From: https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/plsql-language-fundamentals.html#GUID-664BFFEA-063A-48B6-A65B-95225EDDED59 +-- +DECLARE + bonus NUMBER(8,2); +BEGIN + SELECT salary * 0.10 INTO bonus + FROM employees + WHERE employee_id = 100; +END; + +DBMS_OUTPUT.PUT_LINE('bonus = ' || TO_CHAR(bonus)); +/ \ No newline at end of file diff --git a/pmd-dist/src/test/resources/sample-source/pom/pom.xml.pom b/pmd-dist/src/test/resources/sample-source/pom/pom.xml.pom new file mode 100644 index 0000000000..291a0ad3e8 --- /dev/null +++ b/pmd-dist/src/test/resources/sample-source/pom/pom.xml.pom @@ -0,0 +1,17 @@ + + 4.0.0 + net.sourceforge.pmd + xml-pom + 1.0.0-SNAPSHOT + + + + org.jboss.arquillian + arquillian-bom + ${arquillian.version} + bom + import + + + + diff --git a/pmd-dist/src/test/resources/sample-source/velocitytemplate/helloworld.vm b/pmd-dist/src/test/resources/sample-source/velocitytemplate/helloworld.vm new file mode 100644 index 0000000000..e632d74a8d --- /dev/null +++ b/pmd-dist/src/test/resources/sample-source/velocitytemplate/helloworld.vm @@ -0,0 +1,8 @@ + + + #set( $foo = "Velocity" ) + Hello $foo World! + + + + diff --git a/pmd-dist/src/test/resources/sample-source/visualforce/SampleCode.page b/pmd-dist/src/test/resources/sample-source/visualforce/SampleCode.page new file mode 100644 index 0000000000..4bb5f988c9 --- /dev/null +++ b/pmd-dist/src/test/resources/sample-source/visualforce/SampleCode.page @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pmd-dist/src/test/resources/sample-source/xml/samplecode.xml b/pmd-dist/src/test/resources/sample-source/xml/samplecode.xml new file mode 100644 index 0000000000..7db6211863 --- /dev/null +++ b/pmd-dist/src/test/resources/sample-source/xml/samplecode.xml @@ -0,0 +1,35 @@ + + + app + + + Application_Module_Web_ContextRoot + ikb.adf.kreda.abw.webapp-Abnahmetest-ohne-SSO + + + + ikb.adf.kreda.abw.ear + ear + + application + META-INF/application.xml + + Application_Module_Web_ContextRoot + /application/module/web/[context-root="ikb.adf.kreda.abw.webapp-Local-ohne-SSO"] + replace + + + + + + + + + + + diff --git a/pmd-dist/src/test/resources/sample-source/xsl/samplecode.xslt b/pmd-dist/src/test/resources/sample-source/xsl/samplecode.xslt new file mode 100644 index 0000000000..b4e1e7db8e --- /dev/null +++ b/pmd-dist/src/test/resources/sample-source/xsl/samplecode.xslt @@ -0,0 +1,4 @@ + + + diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index cb7728910f..43c3f47c9e 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -316,13 +316,13 @@ prefix for these methods. @@ -1647,7 +1647,7 @@ Method names that are very short are not helpful to the reader. diff --git a/pmd-java/src/main/resources/category/java/design.xml b/pmd-java/src/main/resources/category/java/design.xml index b65b33eb9a..84299f5da7 100644 --- a/pmd-java/src/main/resources/category/java/design.xml +++ b/pmd-java/src/main/resources/category/java/design.xml @@ -1557,11 +1557,11 @@ complexity and find a way to have more fine grained objects. something like this: not ( ( - starts-with(@Image,'get') + starts-with(@Name,'get') or - starts-with(@Image,'set') + starts-with(@Name,'set') or - starts-with(@Image,'is') + starts-with(@Name,'is') ) and ( ( @@ -1571,18 +1571,18 @@ complexity and find a way to have more fine grained objects. ) <= 3 ) ) - This will avoid discarding 'real' method... + This will avoid discarding 'real' methods... --> $maxmethods ] diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 6e99ee509e..9c4d46287c 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -694,16 +694,16 @@ public String bar(String string) { @@ -937,8 +937,8 @@ Note: This is only possible with Java 1.5 or higher. @@ -978,8 +978,8 @@ The method clone() should throw a CloneNotSupportedException. @@ -1849,7 +1849,7 @@ If the finalize() is implemented, its last action should be to call super.finali 0]] +//MethodDeclaration[@Name='finalize'][@Arity > 0] ]]> @@ -1962,9 +1961,7 @@ Note that Oracle has declared Object.finalize() as deprecated since JDK 9. @@ -2126,11 +2123,11 @@ Some JUnit framework methods are easy to misspell.