diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 56a5a13ccb..738f691819 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: false contact_links: + - name: Question + url: https://github.com/pmd/pmd/discussions?discussions_q=category%3AQ%26A + about: Feel free to ask any question about PMD and its usage - name: PMD Designer Issues url: https://github.com/pmd/pmd-designer/issues about: Issues about the rule designer diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 5af12acfa1..0000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: Question -about: Feel free to ask any question about PMD and its usage -title: '' -labels: 'a:question' -assignees: '' - ---- - - - -**Description:** - diff --git a/README.md b/README.md index a819536f6d..038b529c54 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ![PMD Logo](https://raw.githubusercontent.com/pmd/pmd/pmd/7.0.x/docs/images/logo/pmd-logo-300px.png) [![Join the chat at https://gitter.im/pmd/pmd](https://badges.gitter.im/pmd/pmd.svg)](https://gitter.im/pmd/pmd?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://github.com/pmd/pmd/workflows/.github/workflows/pushes.yml/badge.svg?branch=master)](https://github.com/pmd/pmd/actions) +[![Build Status](https://github.com/pmd/pmd/workflows/Pushes/badge.svg?branch=master)](https://github.com/pmd/pmd/actions) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.sourceforge.pmd/pmd/badge.svg)](https://maven-badges.herokuapp.com/maven-central/net.sourceforge.pmd/pmd) [![Reproducible Builds](https://img.shields.io/badge/Reproducible_Builds-ok-green?labelColor=blue)](https://github.com/jvm-repo-rebuild/reproducible-central#net.sourceforge.pmd:pmd) [![Coverage Status](https://coveralls.io/repos/github/pmd/pmd/badge.svg)](https://coveralls.io/github/pmd/pmd) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 71819cd021..806ef68272 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -58,6 +58,23 @@ Thanks to Jeff Bartolotta and Roopa Mohan for contributing this! #### Deprecated API + +##### Around RuleSet parsing + +* {% jdoc core::RuleSetFactory %} and {% jdoc core::RuleSetFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetLoader %}. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* Some static creation methods have been added to {% jdoc core::RuleSet %} for simple cases, eg {% jdoc core::RuleSet#forSingleRule(core::Rule) %}. These replace some counterparts in {% jdoc core::RuleSetFactory %} +* Since {% jdoc core::RuleSets %} is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. +* {% jdoc core::RuleSetReferenceId %}, {% jdoc core::RuleSetReference %}, {% jdoc core::RuleSetFactoryCompatibility %} are deprecated. They are most likely not relevant outside of the implementation of pmd-core. + +##### Around the `PMD` class + +Many classes around PMD's entry point ({% jdoc core::PMD %}) have been deprecated as internal, including: +* The contents of the packages {% jdoc_package core::cli %}, {% jdoc_package core::processor %} +* {% jdoc core::SourceCodeProcessor %} +* The constructors of {% jdoc core::PMD %} (the class will be made a utility class) + +##### Miscellaneous + * {% jdoc !!java::lang.java.ast.ASTPackageDeclaration#getPackageNameImage() %}, {% jdoc !!java::lang.java.ast.ASTTypeParameter#getParameterName() %} and the corresponding XPath attributes. In both cases they're replaced with a new method `getName`, @@ -89,5 +106,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr * [#2936](https://github.com/pmd/pmd/pull/2936): \[java] (doc) Fix typo: "an accessor" not "a" - [Igor Moreno](https://github.com/igormoreno) * [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: fix issue where ignoring using directives could not be disabled - [Maikel Steneker](https://github.com/maikelsteneker) * [#2945](https://github.com/pmd/pmd/pull/2945): \[cs] Add option to ignore sequences of literals - [Maikel Steneker](https://github.com/maikelsteneker) +* [#2962](https://github.com/pmd/pmd/pull/2962): \[cpp] Add support for C++ 14 binary literals - [Maikel Steneker](https://github.com/maikelsteneker) {% endtocmaker %} diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java index 11ef33e0d1..e8f53a6d68 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java @@ -14,16 +14,15 @@ import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; -import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.RuleSetLoader; public class DefaultRulesetTest { @Rule public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); - private RuleSetFactory factory = RulesetsFactoryUtils.createFactory(RulePriority.LOW, true, false); + private RuleSetFactory factory = new RuleSetLoader().enableCompatibility(false).toFactory(); @Test public void loadDefaultRuleset() throws Exception { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java index 32644b1bec..a7836e1a31 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -11,16 +11,18 @@ import java.io.Writer; import java.net.URISyntaxException; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TextTimingReportRenderer; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; @@ -44,7 +46,6 @@ import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.util.ClasspathClassLoader; import net.sourceforge.pmd.util.FileUtil; import net.sourceforge.pmd.util.IOUtil; -import net.sourceforge.pmd.util.ResourceLoader; import net.sourceforge.pmd.util.database.DBMSMetadata; import net.sourceforge.pmd.util.database.DBURI; import net.sourceforge.pmd.util.database.SourceObject; @@ -88,7 +89,10 @@ public class PMD { /** * Create a PMD instance using a default Configuration. Changes to the * configuration may be required. + * + * @deprecated Just use the static methods, and maintain your {@link PMDConfiguration} separately. */ + @Deprecated public PMD() { this(new PMDConfiguration()); } @@ -96,9 +100,11 @@ public class PMD { /** * Create a PMD instance using the specified Configuration. * - * @param configuration - * The runtime Configuration of PMD to use. + * @param configuration The runtime Configuration of PMD to use. + * + * @deprecated Just use the static methods, and maintain your {@link PMDConfiguration} separately. */ + @Deprecated public PMD(PMDConfiguration configuration) { this.configuration = configuration; this.rulesetsFileProcessor = new SourceCodeProcessor(configuration); @@ -114,7 +120,10 @@ public class PMD { * @throws PMDException * if the URI couldn't be parsed * @see DBURI + * + * @deprecated Will be hidden as part of the parsing of {@link PMD#getApplicableFiles(PMDConfiguration, Set)} */ + @Deprecated public static List getURIDataSources(String uriString) throws PMDException { List dataSources = new ArrayList<>(); @@ -160,7 +169,11 @@ public class PMD { * @param configuration * the given configuration * @return the pre-configured parser + * + * @deprecated This is internal */ + @Deprecated + @InternalApi public static Parser parserFor(LanguageVersion languageVersion, PMDConfiguration configuration) { // TODO Handle Rules having different parser options. @@ -178,7 +191,10 @@ public class PMD { * * @return The configuration. * @see PMDConfiguration + * + * @deprecated Don't create a PMD instance just to create a {@link PMDConfiguration} */ + @Deprecated public PMDConfiguration getConfiguration() { return configuration; } @@ -187,7 +203,9 @@ public class PMD { * Gets the source code processor. * * @return SourceCodeProcessor + * @deprecated Source code processor is internal */ + @Deprecated public SourceCodeProcessor getSourceCodeProcessor() { return rulesetsFileProcessor; } @@ -201,10 +219,8 @@ public class PMD { public static int doPMD(final PMDConfiguration configuration) { // Load the RuleSets - final RuleSetFactory ruleSetFactory = - RulesetsFactoryUtils.getRulesetFactory(configuration, new ResourceLoader()); - final RuleSets ruleSets = - RulesetsFactoryUtils.getRuleSetsWithBenchmark(configuration.getRuleSets(), ruleSetFactory); + final RuleSetFactory ruleSetFactory = RuleSetLoader.fromPmdConfig(configuration).toFactory(); + final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSetsWithBenchmark(configuration.getRuleSets(), ruleSetFactory); if (ruleSets == null) { return PMDCommandLineInterface.NO_ERRORS_STATUS; } @@ -222,24 +238,15 @@ public class PMD { renderer.start(); } - final RuleContext ctx = new RuleContext(); - final AtomicInteger violations = new AtomicInteger(0); - ctx.getReport().addListener(new ThreadSafeReportListener() { - @Override - public void ruleViolationAdded(final RuleViolation ruleViolation) { - violations.getAndIncrement(); - } - - }); - + Report report; try (TimedOperation to = TimeTracker.startOperation(TimedOperationCategory.FILE_PROCESSING)) { - processFiles(configuration, ruleSetFactory, files, ctx, renderers); + report = processFiles(configuration, Arrays.asList(ruleSets.getAllRuleSets()), files, renderers); } try (TimedOperation rto = TimeTracker.startOperation(TimedOperationCategory.REPORTING)) { renderer.end(); renderer.flush(); - return violations.get(); + return report.getViolations().size(); } } catch (final Exception e) { String message = e.getMessage(); @@ -271,7 +278,10 @@ public class PMD { * @param sourceCodeFile * the source code file * @return the rule context + * + * @deprecated Not useful */ + @Deprecated public static RuleContext newRuleContext(String sourceCodeFilename, File sourceCodeFile) { RuleContext context = new RuleContext(); @@ -294,19 +304,60 @@ public class PMD { * RuleContext * @param renderers * List of {@link Renderer}s + * + * @deprecated Use {@link #processFiles(PMDConfiguration, List, Collection, List)} + * so as not to depend on {@link RuleSetFactory}. Note that this sorts the list of data sources in-place, + * which won't be fixed */ + @Deprecated public static void processFiles(final PMDConfiguration configuration, final RuleSetFactory ruleSetFactory, - final List files, final RuleContext ctx, final List renderers) { + final List files, final RuleContext ctx, final List renderers) { + // Note that this duplicates the other routine, because the old behavior was + // that we parsed rulesets (a second time) inside the processor execution. + // To not mess up error handling, we keep this behavior. + encourageToUseIncrementalAnalysis(configuration); sortFiles(configuration, files); // Make sure the cache is listening for analysis results ctx.getReport().addListener(configuration.getAnalysisCache()); - final RuleSetFactory silentFactory = new RuleSetFactory(ruleSetFactory, false); + final RuleSetFactory silentFactory = ruleSetFactory.toLoader().warnDeprecated(false).toFactory(); newFileProcessor(configuration).processFiles(silentFactory, files, ctx, renderers); configuration.getAnalysisCache().persist(); } + /** + * Run PMD using the given configuration. This replaces the other overload. + * + * @param configuration Configuration for the run. Note that the files, + * and rulesets, are ignored, as they are supplied + * as parameters + * @param rulesets Parsed rulesets + * @param files Files to process, will be closed by this method. + * @param renderers Renderers that render the report + * + * @return Report in which violations are accumulated + * + * @throws RuntimeException If processing fails + */ + public static Report processFiles(final PMDConfiguration configuration, + final List rulesets, + final Collection files, + final List renderers) { + encourageToUseIncrementalAnalysis(configuration); + Report report = new Report(); + report.addListener(configuration.getAnalysisCache()); + + List sortedFiles = new ArrayList<>(files); + sortFiles(configuration, sortedFiles); + + RuleContext ctx = new RuleContext(); + ctx.setReport(report); + newFileProcessor(configuration).processFiles(new RuleSets(rulesets), sortedFiles, ctx, renderers); + configuration.getAnalysisCache().persist(); + return report; + } + private static void sortFiles(final PMDConfiguration configuration, final List files) { if (configuration.isStressTest()) { // randomize processing order @@ -439,7 +490,7 @@ public class PMD { } /** - * Entry to invoke PMD as command line tool + * Entry to invoke PMD as command line tool. Note that this will invoke {@link System#exit(int)}. * * @param args * command line arguments @@ -449,7 +500,8 @@ public class PMD { } /** - * Parses the command line arguments and executes PMD. + * Parses the command line arguments and executes PMD. Returns the + * exit code without exiting the VM. * * @param args * command line arguments diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDException.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDException.java index 17265d7366..61927fa18a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDException.java @@ -13,6 +13,7 @@ package net.sourceforge.pmd; * @version $Revision$, $Date$ * @since August 30, 2002 */ +@Deprecated public class PMDException extends Exception { private static final long serialVersionUID = 6938647389367956874L; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Report.java b/pmd-core/src/main/java/net/sourceforge/pmd/Report.java index c24a3d642b..ae72b12c6e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/Report.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/Report.java @@ -27,6 +27,7 @@ public class Report { private final List suppressedRuleViolations = new ArrayList<>(); private final List errors = new ArrayList<>(); private final List configErrors = new ArrayList<>(); + private final Object lock = new Object(); /** * Creates a new, initialized, empty report for the given file name. @@ -218,18 +219,23 @@ public class Report { * summary over all violations is needed as PMD creates one report per file * by default. * - * @param r - * the report to be merged into this. + *

This is synchronized on an internal lock (note that other mutation + * operations are not synchronized, todo for pmd 7). + * + * @param r the report to be merged into this. + * * @see AbstractAccumulatingRenderer */ public void merge(Report r) { - errors.addAll(r.errors); - configErrors.addAll(r.configErrors); - suppressedRuleViolations.addAll(r.suppressedRuleViolations); + synchronized (lock) { + errors.addAll(r.errors); + configErrors.addAll(r.configErrors); + suppressedRuleViolations.addAll(r.suppressedRuleViolations); - for (RuleViolation violation : r.getViolations()) { - int index = Collections.binarySearch(violations, violation, RuleViolation.DEFAULT_COMPARATOR); - violations.add(index < 0 ? -index - 1 : index, violation); + for (RuleViolation violation : r.getViolations()) { + int index = Collections.binarySearch(violations, violation, RuleViolation.DEFAULT_COMPARATOR); + violations.add(index < 0 ? -index - 1 : index, violation); + } } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java index 034441ef13..06a3469b99 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java @@ -25,6 +25,7 @@ import net.sourceforge.pmd.internal.util.PredicateUtil; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.RuleReference; +import net.sourceforge.pmd.lang.rule.XPathRule; /** * This class represents a collection of rules along with some optional filter @@ -91,6 +92,89 @@ public class RuleSet implements ChecksumAware { filter = rs.filter; // filters are immutable, can be shared } + /** + * Creates a new ruleset containing a single rule. The ruleset will + * have default description, name, and null file name. + * + * @param rule The rule being created + * + * @return The newly created RuleSet + */ + public static RuleSet forSingleRule(final Rule rule) { + final long checksum; + if (rule instanceof XPathRule) { + checksum = ((XPathRule) rule).getXPathExpression().hashCode(); + } else { + // TODO : Is this good enough? all properties' values + rule name + checksum = rule.getPropertiesByPropertyDescriptor().values().hashCode() * 31 + rule.getName().hashCode(); + } + + final RuleSetBuilder builder = + new RuleSetBuilder(checksum) + .withName(rule.getName()) + .withDescription("RuleSet for " + rule.getName()); + builder.addRule(rule); + return builder.build(); + } + + + /** + * Creates a new ruleset with the given metadata such as name, description, + * fileName, exclude/include patterns are used. The rules are taken from the given + * collection. + * + *

Note: The rule instances are shared between the collection + * and the new ruleset (copy-by-reference). This might lead to concurrency issues, + * if the rules of the collection are also referenced by other rulesets and used + * in different threads. + *

+ * + * @param name the name of the ruleset + * @param description the description + * @param fileName the filename + * @param excludePatterns list of exclude patterns + * @param includePatterns list of include patterns, that override the exclude patterns + * @param rules the collection with the rules to add to the new ruleset + * + * @return the new ruleset + * + * @throws NullPointerException If any parameter is null, or the collections contain null elements + */ + public static RuleSet create(String name, + String description, + String fileName, + Collection excludePatterns, + Collection includePatterns, + Iterable rules) { + RuleSetBuilder builder = new RuleSetBuilder(0L); // TODO: checksum missing + builder.withName(name) + .withDescription(description) + .withFileName(fileName) + .replaceFileExclusions(excludePatterns) + .replaceFileInclusions(includePatterns); + for (Rule rule : rules) { + builder.addRule(rule); + } + return builder.build(); + } + + /** + * Creates a copy of the given ruleset. All properties like name, description, fileName + * and exclude/include patterns are copied. + * + *

Note: The rule instances are shared between the original + * and the new ruleset (copy-by-reference). This might lead to concurrency issues, + * if the original ruleset and the new ruleset are used in different threads. + *

+ * + * @param original the original rule set to copy from + * + * @return the copy + */ + public static RuleSet copy(RuleSet original) { + return new RuleSet(original); + } + /* package */ static class RuleSetBuilder { public String description; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index f934f33b58..08f2c1bcf7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -13,7 +13,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -34,21 +33,19 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import net.sourceforge.pmd.RuleSet.RuleSetBuilder; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.RuleReference; -import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.rules.RuleFactory; import net.sourceforge.pmd.util.ResourceLoader; /** * RuleSetFactory is responsible for creating RuleSet instances from XML - * content. By default Rules will be loaded using the {@link RulePriority#LOW} priority, - * with Rule deprecation warnings off; - * the ruleset compatibility filter is active, too (see {@link RuleSetFactoryCompatibility}); - * if the ruleset contains rule references (e.g. for renamed or moved rules), these - * are ignored by default. + * content. See {@link RuleSetLoader} for configuration options and + * their defaults. + * + * @deprecated Use a {@link RuleSetLoader} instead. This will be hidden in PMD 7 + * (it's the implementation, while {@link RuleSetLoader} is the API). */ +@Deprecated public class RuleSetFactory { private static final Logger LOG = Logger.getLogger(RuleSetFactory.class.getName()); @@ -66,7 +63,7 @@ public class RuleSetFactory { private final Map parsedRulesets = new HashMap<>(); /** - * @deprecated Use {@link RulesetsFactoryUtils#defaultFactory()} + * @deprecated Use a {@link RuleSetLoader} to build a new factory */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory() { @@ -74,8 +71,7 @@ public class RuleSetFactory { } /** - * @deprecated Use {@link RulesetsFactoryUtils#createFactory(ClassLoader, RulePriority, boolean, boolean)} - * or {@link RulesetsFactoryUtils#createFactory(RulePriority, boolean, boolean)} + * @deprecated Use a {@link RuleSetLoader} to build a new factory */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory(final ClassLoader classLoader, final RulePriority minimumPriority, @@ -84,8 +80,7 @@ public class RuleSetFactory { } /** - * @deprecated Use {@link RulesetsFactoryUtils#createFactory(ClassLoader, RulePriority, boolean, boolean)} - * or {@link RulesetsFactoryUtils#createFactory(RulePriority, boolean, boolean)} + * @deprecated Use a {@link RuleSetLoader} to build a new factory */ @Deprecated // to be hidden with PMD 7.0.0. public RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority, @@ -110,16 +105,18 @@ public class RuleSetFactory { /** * Constructor copying all configuration from another factory. * - * @param factory - * The factory whose configuration to copy. - * @param warnDeprecated - * Whether deprecation warnings are to be produced by this - * factory. + * @param factory The factory whose configuration to copy. + * @param warnDeprecated Whether deprecation warnings are to be produced by this + * factory + * + * @deprecated Use {@link #toLoader()} to rebuild a factory from a configuration */ + @Deprecated public RuleSetFactory(final RuleSetFactory factory, final boolean warnDeprecated) { this(factory.resourceLoader, factory.minimumPriority, warnDeprecated, factory.compatibilityFilter != null); } + /** * Gets the compatibility filter in order to adjust it, e.g. add additional * filters. @@ -137,28 +134,12 @@ public class RuleSetFactory { * @return An Iterator of RuleSet objects. * * @throws RuleSetNotFoundException if the ruleset file could not be found + * + * @deprecated Use {@link RuleSetLoader#getStandardRuleSets()} */ + @Deprecated public Iterator getRegisteredRuleSets() throws RuleSetNotFoundException { - String rulesetsProperties = null; - List ruleSetReferenceIds = new ArrayList<>(); - for (Language language : LanguageRegistry.getLanguages()) { - Properties props = new Properties(); - rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties"; - try (InputStream inputStream = resourceLoader.loadClassPathResourceAsStreamOrThrow(rulesetsProperties)) { - props.load(inputStream); - String rulesetFilenames = props.getProperty("rulesets.filenames"); - if (rulesetFilenames != null) { - ruleSetReferenceIds.addAll(RuleSetReferenceId.parse(rulesetFilenames)); - } - } catch (RuleSetNotFoundException e) { - LOG.warning("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); - } catch (IOException ioe) { - throw new RuntimeException("Couldn't find " + rulesetsProperties - + "; please ensure that the directory is on the classpath. The current classpath is: " - + System.getProperty("java.class.path")); - } - } - return createRuleSets(ruleSetReferenceIds).getRuleSetsIterator(); + return toLoader().getStandardRuleSets().iterator(); } /** @@ -173,7 +154,11 @@ public class RuleSetFactory { * @return The new RuleSets. * @throws RuleSetNotFoundException * if unable to find a resource. + * + * @deprecated Use {@link RuleSetLoader#loadFromResource(String)}, + * but note that that method does not split on commas */ + @Deprecated public RuleSets createRuleSets(String referenceString) throws RuleSetNotFoundException { return createRuleSets(RuleSetReferenceId.parse(referenceString)); } @@ -187,7 +172,10 @@ public class RuleSetFactory { * @return The new RuleSets. * @throws RuleSetNotFoundException * if unable to find a resource. + * + * @deprecated Will not be replaced */ + @Deprecated public RuleSets createRuleSets(List ruleSetReferenceIds) throws RuleSetNotFoundException { List ruleSets = new ArrayList<>(); for (RuleSetReferenceId ruleSetReferenceId : ruleSetReferenceIds) { @@ -209,7 +197,10 @@ public class RuleSetFactory { * @return A new RuleSet. * @throws RuleSetNotFoundException * if unable to find a resource. + * + * @deprecated Use {@link RuleSetLoader#loadFromResource(String)} and discard the rest of the list. */ + @Deprecated public RuleSet createRuleSet(String referenceString) throws RuleSetNotFoundException { List references = RuleSetReferenceId.parse(referenceString); if (references.isEmpty()) { @@ -228,7 +219,10 @@ public class RuleSetFactory { * @return A new RuleSet. * @throws RuleSetNotFoundException * if unable to find a resource. + * + * @deprecated Will not be replaced */ + @Deprecated public RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { return createRuleSet(ruleSetReferenceId, includeDeprecatedRuleReferences); } @@ -249,7 +243,10 @@ public class RuleSetFactory { * * @param original the original rule set to copy from * @return the copy + * + * @deprecated Use {@link RuleSet#copy(RuleSet)} */ + @Deprecated public RuleSet createRuleSetCopy(RuleSet original) { RuleSetBuilder builder = new RuleSetBuilder(original); return builder.build(); @@ -273,23 +270,17 @@ public class RuleSetFactory { * @param includePatterns list of include patterns, if any is not a valid regular expression, it will be ignored * @param rules the collection with the rules to add to the new ruleset * @return the new ruleset + * + * @deprecated Use {@link RuleSet#create(String, String, String, Collection, Collection, Iterable)} */ + @Deprecated public RuleSet createNewRuleSet(String name, String description, String fileName, Collection excludePatterns, Collection includePatterns, Collection rules) { - RuleSetBuilder builder = new RuleSetBuilder(0L); // TODO: checksum missing - builder.withName(name) - .withDescription(description) - .withFileName(fileName) - .replaceFileExclusions(toPatterns(excludePatterns)) - .replaceFileInclusions(toPatterns(includePatterns)); - for (Rule rule : rules) { - builder.addRule(rule); - } - return builder.build(); + return RuleSet.create(name, description, fileName, toPatterns(excludePatterns), toPatterns(includePatterns), rules); } private Collection toPatterns(Collection sources) { @@ -307,24 +298,15 @@ public class RuleSetFactory { /** * Creates a new RuleSet containing a single rule. * - * @param rule - * The rule being created + * @param rule The rule being created + * * @return The newly created RuleSet + * + * @deprecated Use {@link RuleSet#forSingleRule(Rule)} */ - public RuleSet createSingleRuleRuleSet(final Rule rule) { // TODO make static? - final long checksum; - if (rule instanceof XPathRule) { - checksum = ((XPathRule) rule).getXPathExpression().hashCode(); - } else { - // TODO : Is this good enough? all properties' values + rule name - checksum = rule.getPropertiesByPropertyDescriptor().values().hashCode() * 31 + rule.getName().hashCode(); - } - - final RuleSetBuilder builder = new RuleSetBuilder(checksum) - .withName(rule.getName()) - .withDescription("RuleSet for " + rule.getName()); - builder.addRule(rule); - return builder.build(); + @Deprecated + public RuleSet createSingleRuleRuleSet(final Rule rule) { + return RuleSet.forSingleRule(rule); } /** @@ -347,7 +329,8 @@ public class RuleSetFactory { private Rule createRule(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) throws RuleSetNotFoundException { if (ruleSetReferenceId.isAllRules()) { - throw new IllegalArgumentException("Cannot parse a single Rule from an all Rule RuleSet reference: <" + ruleSetReferenceId + ">."); + throw new IllegalArgumentException( + "Cannot parse a single Rule from an all Rule RuleSet reference: <" + ruleSetReferenceId + ">."); } RuleSet ruleSet; // java8: computeIfAbsent @@ -437,9 +420,6 @@ public class RuleSetFactory { ruleSetBuilder.filterRulesByPriority(minimumPriority); return ruleSetBuilder.build(); - } catch (ReflectiveOperationException ex) { - ex.printStackTrace(); - throw new RuntimeException("Couldn't find the class " + ex.getMessage(), ex); } catch (ParserConfigurationException | IOException | SAXException ex) { ex.printStackTrace(); throw new RuntimeException("Couldn't read the ruleset " + ruleSetReferenceId + ": " + ex.getMessage(), ex); @@ -511,7 +491,7 @@ public class RuleSetFactory { */ private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Node ruleNode, boolean withDeprecatedRuleReferences, Set rulesetReferences) - throws ClassNotFoundException, InstantiationException, IllegalAccessException, RuleSetNotFoundException { + throws RuleSetNotFoundException { Element ruleElement = (Element) ruleNode; String ref = ruleElement.getAttribute("ref"); if (ref.endsWith("xml")) { @@ -556,7 +536,7 @@ public class RuleSetFactory { // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset - RuleSetFactory ruleSetFactory = new RuleSetFactory(resourceLoader, RulePriority.LOW, false, this.compatibilityFilter != null); + RuleSetFactory ruleSetFactory = toLoader().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).toFactory(); RuleSet otherRuleSet = ruleSetFactory.createRuleSet(RuleSetReferenceId.parse(ref).get(0)); List potentialRules = new ArrayList<>(); int countDeprecated = 0; @@ -619,7 +599,7 @@ public class RuleSetFactory { * Must be a rule element node. */ private void parseSingleRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode) throws ClassNotFoundException, InstantiationException, IllegalAccessException { + Node ruleNode) { Element ruleElement = (Element) ruleNode; // Stop if we're looking for a particular Rule, and this element is not @@ -671,7 +651,7 @@ public class RuleSetFactory { // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset - RuleSetFactory ruleSetFactory = new RuleSetFactory(resourceLoader, RulePriority.LOW, false, this.compatibilityFilter != null); + RuleSetFactory ruleSetFactory = toLoader().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).toFactory(); boolean isSameRuleSet = false; RuleSetReferenceId otherRuleSetReferenceId = RuleSetReferenceId.parse(ref).get(0); @@ -824,4 +804,19 @@ public class RuleSetFactory { return false; } } + + + /** + * Create a new {@link RuleSetLoader} with the same config as this + * factory. This is a transitional API. + */ + public RuleSetLoader toLoader() { + return new RuleSetLoader().loadResourcesWith(resourceLoader) + .filterAbovePriority(minimumPriority) + .warnDeprecated(warnDeprecated) + .enableCompatibility(compatibilityFilter != null) + .includeDeprecatedRuleReferences(includeDeprecatedRuleReferences); + } + + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java index cd933218fd..6ed1d189a4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java @@ -18,13 +18,20 @@ import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; +import net.sourceforge.pmd.annotation.InternalApi; + /** * Provides a simple filter mechanism to avoid failing to parse an old ruleset, * which references rules, that have either been removed from PMD already or * renamed or moved to another ruleset. * * @see issue 1360 + * + * @deprecated Use {@link RuleSetLoader#enableCompatibility(boolean)} to enable this feature. + * This implementation is internal API. */ +@InternalApi +@Deprecated public class RuleSetFactoryCompatibility { private static final Logger LOG = Logger.getLogger(RuleSetFactoryCompatibility.class.getName()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java new file mode 100644 index 0000000000..a59321e146 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java @@ -0,0 +1,32 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import net.sourceforge.pmd.annotation.InternalApi; + +/** + * An exception that is thrown when something wrong occurs while + * {@linkplain RuleSetLoader loading rulesets}. This may be because the + * XML is not well-formed, does not respect the ruleset schema, is + * not a valid ruleset or is otherwise unparsable. + * + *

In the new {@link RuleSetLoader} API, this is thrown instead of + * {@link RuleSetNotFoundException}. + */ +public final class RuleSetLoadException extends RuntimeException { + + /** Constructors are internal. */ + @InternalApi + public RuleSetLoadException(String message, Throwable cause) { + super(message, cause); + } + + /** Constructors are internal. */ + @InternalApi + public RuleSetLoadException(String message) { + super(message); + } + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java new file mode 100644 index 0000000000..f5631f7aec --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -0,0 +1,225 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.util.CollectionUtil; +import net.sourceforge.pmd.util.ResourceLoader; + +/** + * Configurable object to load rulesets from XML resources. + * This can be configured using a fluent API, see eg {@link #warnDeprecated(boolean)}. + * To create a new ruleset, use {@link #loadFromResource(String)} + * or some such overload. + */ +public final class RuleSetLoader { + + private static final Logger LOG = Logger.getLogger(RuleSetLoader.class.getName()); + + private ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader()); + private RulePriority minimumPriority = RulePriority.LOW; + private boolean warnDeprecated = true; + private boolean enableCompatibility = true; + private boolean includeDeprecatedRuleReferences = false; + + /** + * Specify that the given classloader should be used to resolve + * paths to external ruleset references. The default uses PMD's + * own classpath. + */ + public RuleSetLoader loadResourcesWith(ClassLoader classLoader) { + this.resourceLoader = new ResourceLoader(classLoader); + return this; + } + + // internal + RuleSetLoader loadResourcesWith(ResourceLoader loader) { + this.resourceLoader = loader; + return this; + } + + /** + * Filter loaded rules to only those that match or are above + * the given priority. The default is {@link RulePriority#LOW}, + * ie, no filtering occurs. + * + * @return This instance, modified + */ + public RuleSetLoader filterAbovePriority(RulePriority minimumPriority) { + this.minimumPriority = minimumPriority; + return this; + } + + /** + * Log a warning when referencing a deprecated rule. + * This is enabled by default. + * + * @return This instance, modified + */ + public RuleSetLoader warnDeprecated(boolean warn) { + this.warnDeprecated = warn; + return this; + } + + /** + * Enable translating old rule references to newer ones, if they have + * been moved or renamed. This is enabled by default, if disabled, + * unresolved references will not be translated and will produce an + * error. + * + * @return This instance, modified + */ + public RuleSetLoader enableCompatibility(boolean enable) { + this.enableCompatibility = enable; + return this; + } + + /** + * Follow deprecated rule references. By default this is off, + * and those references will be ignored (with a warning depending + * on {@link #enableCompatibility(boolean)}). + * + * @return This instance, modified + */ + public RuleSetLoader includeDeprecatedRuleReferences(boolean enable) { + this.includeDeprecatedRuleReferences = enable; + return this; + } + + /** + * Create a new rule set factory, if you have to (that class is deprecated). + * That factory will use the configuration that was set using the setters of this. + * + * @deprecated {@link RuleSetFactory} is deprecated, replace its usages + * with usages of this class, or of static factory methods of {@link RuleSet} + */ + @Deprecated + public RuleSetFactory toFactory() { + return new RuleSetFactory( + this.resourceLoader, + this.minimumPriority, + this.warnDeprecated, + this.enableCompatibility, + this.includeDeprecatedRuleReferences + ); + } + + + /** + * Parses and returns a ruleset from its location. The location may + * be a file system path, or a resource path (see {@link #loadResourcesWith(ClassLoader)}). + * + *

This replaces {@link RuleSetFactory#createRuleSet(String)}, + * but does not split commas. + * + * @param rulesetPath A reference to a single ruleset + * + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found) + */ + public RuleSet loadFromResource(String rulesetPath) { + return loadFromResource(new RuleSetReferenceId(rulesetPath)); + } + + /** + * Parses several resources into a list of rulesets. + * + * @param paths Paths + * + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found), + * for any of the parameters + * @throws NullPointerException If the parameter, or any component is null + */ + public List loadFromResources(Collection paths) { + List ruleSets = new ArrayList<>(paths.size()); + for (String path : paths) { + ruleSets.add(loadFromResource(path)); + } + return ruleSets; + } + + /** + * Parses several resources into a list of rulesets. + * + * @param first First path + * @param rest Paths + * + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found), + * for any of the parameters + * @throws NullPointerException If the parameter, or any component is null + */ + public List loadFromResources(String first, String... rest) { + return loadFromResources(CollectionUtil.listOf(first, rest)); + } + + // package private + RuleSet loadFromResource(RuleSetReferenceId ruleSetReferenceId) { + try { + return toFactory().createRuleSet(ruleSetReferenceId); + } catch (Exception e) { + throw new RuleSetLoadException("Cannot parse " + ruleSetReferenceId, e); + } + } + + + /** + * Configure a new ruleset factory builder according to the parameters + * of the given PMD configuration. + */ + public static RuleSetLoader fromPmdConfig(PMDConfiguration configuration) { + return new RuleSetLoader().filterAbovePriority(configuration.getMinimumPriority()) + .enableCompatibility(configuration.isRuleSetFactoryCompatibilityEnabled()); + } + + + /** + * Returns an Iterator of RuleSet objects loaded from descriptions from the + * "categories.properties" resource for each language. This + * uses the classpath of the resource loader ({@link #loadResourcesWith(ClassLoader)}). + * + * @return A list of all category rulesets + * + * @throws RuleSetLoadException If a standard ruleset cannot be loaded. + * This is a corner case, that probably should not be caught by clients. + * The standard rulesets are well-formed, at least in stock PMD distributions. + * + */ + public List getStandardRuleSets() { + String rulesetsProperties; + List ruleSetReferenceIds = new ArrayList<>(); + for (Language language : LanguageRegistry.getLanguages()) { + Properties props = new Properties(); + rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties"; + try (InputStream inputStream = resourceLoader.loadClassPathResourceAsStreamOrThrow(rulesetsProperties)) { + props.load(inputStream); + String rulesetFilenames = props.getProperty("rulesets.filenames"); + if (rulesetFilenames != null) { + ruleSetReferenceIds.addAll(RuleSetReferenceId.parse(rulesetFilenames)); + } + } catch (RuleSetNotFoundException e) { + LOG.warning("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); + } catch (IOException ioe) { + throw new RuntimeException("Couldn't find " + rulesetsProperties + + "; please ensure that the directory is on the classpath. The current classpath is: " + + System.getProperty("java.class.path")); + } + } + + List ruleSets = new ArrayList<>(); + for (RuleSetReferenceId id : ruleSetReferenceIds) { + ruleSets.add(loadFromResource(id)); // may throw + } + return ruleSets; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java index 0ad22f4c68..4e3acad5d4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java @@ -4,10 +4,20 @@ package net.sourceforge.pmd; +/** + * @deprecated This is now only thrown by deprecated apis. {@link RuleSetLoader} + * throws {@link RuleSetLoadException} instead + */ +@Deprecated public class RuleSetNotFoundException extends Exception { + private static final long serialVersionUID = -4617033110919250810L; public RuleSetNotFoundException(String msg) { super(msg); } + + public RuleSetNotFoundException(String msg, Throwable cause) { + super(msg, cause); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java index e7098568e1..cdc6e5f77c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java @@ -8,9 +8,15 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; +import net.sourceforge.pmd.annotation.InternalApi; + /** * This class represents a reference to RuleSet. + * + * @deprecated This is part of the internals of the {@link RuleSetLoader}. */ +@Deprecated +@InternalApi public class RuleSetReference { private final String ruleSetFileName; private final boolean allRules; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java index e5ee6c47cc..66dd5e47c9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java @@ -14,6 +14,7 @@ import java.util.List; import org.apache.commons.lang3.StringUtils; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.util.ResourceLoader; /** @@ -78,7 +79,11 @@ import net.sourceforge.pmd.util.ResourceLoader; * * * + * + * @deprecated This is part of the internals of the {@link RuleSetLoader}. */ +@Deprecated +@InternalApi public class RuleSetReferenceId { private final boolean external; private final String ruleSetFileName; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java index f27776f7d8..6b2c5d146a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java @@ -74,6 +74,11 @@ public class RuleSets { return ruleSets.toArray(new RuleSet[0]); } + // internal + List getRuleSetsInternal() { + return ruleSets; + } + public Iterator getRuleSetsIterator() { return ruleSets.iterator(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java index 69a1ff066c..135736f8a6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java @@ -13,6 +13,10 @@ import net.sourceforge.pmd.benchmark.TimedOperation; import net.sourceforge.pmd.benchmark.TimedOperationCategory; import net.sourceforge.pmd.util.ResourceLoader; +/** + * @deprecated Use a {@link RuleSetLoader} instead + */ +@Deprecated public final class RulesetsFactoryUtils { private static final Logger LOG = Logger.getLogger(RulesetsFactoryUtils.class.getName()); @@ -76,7 +80,7 @@ public final class RulesetsFactoryUtils { } /** - * @deprecated Use {@link #createFactory(PMDConfiguration)} or {@link #createFactory(PMDConfiguration, ClassLoader)} + * @deprecated Use a {@link RuleSetLoader} */ @InternalApi @Deprecated @@ -96,7 +100,10 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration, ClassLoader) + * + * @deprecated Use {@link RuleSetLoader#fromPmdConfig(PMDConfiguration)} */ + @Deprecated public static RuleSetFactory createFactory(final PMDConfiguration configuration) { return createFactory(configuration, RulesetsFactoryUtils.class.getClassLoader()); } @@ -107,7 +114,7 @@ public final class RulesetsFactoryUtils { * * @return A ruleset factory * - * @see #createFactory(PMDConfiguration, ClassLoader) + * @see RuleSetLoader */ public static RuleSetFactory defaultFactory() { return new RuleSetFactory(); @@ -124,7 +131,10 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration) + * + * @deprecated Use a {@link RuleSetLoader} */ + @Deprecated public static RuleSetFactory createFactory(final PMDConfiguration configuration, ClassLoader classLoader) { return createFactory(classLoader, configuration.getMinimumPriority(), @@ -145,7 +155,10 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration) + * + * @deprecated Use a {@link RuleSetLoader} */ + @Deprecated public static RuleSetFactory createFactory(ClassLoader classLoader, RulePriority minimumPriority, boolean warnDeprecated, @@ -166,11 +179,13 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration) + * + * @deprecated Use a {@link RuleSetLoader} */ + @Deprecated public static RuleSetFactory createFactory(RulePriority minimumPriority, boolean warnDeprecated, boolean enableCompatibility) { - return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility); } @@ -190,14 +205,16 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration) + * @deprecated Use a {@link RuleSetLoader} */ + @Deprecated public static RuleSetFactory createFactory(RulePriority minimumPriority, boolean warnDeprecated, boolean enableCompatibility, boolean includeDeprecatedRuleReferences) { return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility, - includeDeprecatedRuleReferences); + includeDeprecatedRuleReferences); } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java index 4dcd6f983b..b7750a9dd1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java @@ -10,6 +10,7 @@ import java.io.InputStreamReader; import java.io.Reader; import java.util.Collections; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; import net.sourceforge.pmd.benchmark.TimedOperationCategory; @@ -21,6 +22,11 @@ import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.RootNode; +/** + * Source code processor is internal. + */ +@Deprecated +@InternalApi public class SourceCodeProcessor { private final PMDConfiguration configuration; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java index 7466d6d794..c59ccd52e1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java @@ -9,11 +9,12 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ContextedRuntimeException; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; @@ -25,11 +26,9 @@ import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.ant.Formatter; @@ -42,7 +41,6 @@ import net.sourceforge.pmd.renderers.AbstractRenderer; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.util.ClasspathClassLoader; import net.sourceforge.pmd.util.IOUtil; -import net.sourceforge.pmd.util.ResourceLoader; import net.sourceforge.pmd.util.datasource.DataSource; import net.sourceforge.pmd.util.datasource.FileDataSource; import net.sourceforge.pmd.util.log.AntLogHandler; @@ -106,22 +104,20 @@ public class PMDTaskImpl { setupClassLoader(); // Setup RuleSetFactory and validate RuleSets - final ResourceLoader rl = setupResourceLoader(); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration, rl); + RuleSetLoader rulesetLoader = RuleSetLoader.fromPmdConfig(configuration) + .loadResourcesWith(setupResourceLoader()); - try { - // This is just used to validate and display rules. Each thread will create its own ruleset - String ruleSets = configuration.getRuleSets(); - if (StringUtils.isNotBlank(ruleSets)) { - // Substitute env variables/properties - configuration.setRuleSets(project.replaceProperties(ruleSets)); - } - RuleSets rules = ruleSetFactory.createRuleSets(configuration.getRuleSets()); - logRulesUsed(rules); - } catch (RuleSetNotFoundException e) { - throw new BuildException(e.getMessage(), e); + // This is just used to validate and display rules. Each thread will create its own ruleset + String ruleSetString = configuration.getRuleSets(); + if (StringUtils.isNotBlank(ruleSetString)) { + // Substitute env variables/properties + configuration.setRuleSets(project.replaceProperties(ruleSetString)); } + final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), rulesetLoader.toFactory()); + List rulesetList = Arrays.asList(ruleSets.getAllRuleSets()); + logRulesUsed(ruleSets); + if (configuration.getSuppressMarker() != null) { project.log("Setting suppress marker to be " + configuration.getSuppressMarker(), Project.MSG_VERBOSE); } @@ -137,9 +133,8 @@ public class PMDTaskImpl { // TODO Do we really need all this in a loop over each FileSet? Seems // like a lot of redundancy - RuleContext ctx = new RuleContext(); Report errorReport = new Report(); - final AtomicInteger reportSize = new AtomicInteger(); + int problemCount = 0; final String separator = System.getProperty("file.separator"); for (FileSet fs : filesets) { @@ -172,10 +167,7 @@ public class PMDTaskImpl { @Override public void renderFileReport(Report r) { - int size = r.getViolations().size(); - if (size > 0) { - reportSize.addAndGet(size); - } + // Nothing to do } @Override @@ -196,13 +188,19 @@ public class PMDTaskImpl { renderers.add(renderer); } try { - PMD.processFiles(configuration, ruleSetFactory, files, ctx, renderers); + Report report = PMD.processFiles(configuration, rulesetList, files, renderers); + problemCount += report.getViolations().size(); + } catch (ContextedRuntimeException e) { + if (e.getFirstContextValue("filename") instanceof String) { + handleError((String) e.getFirstContextValue("filename"), errorReport, e); + } else { + handleError("(unknown file)", errorReport, e); + } } catch (RuntimeException pmde) { - handleError(ctx, errorReport, pmde); + handleError("(unknown file)", errorReport, pmde); } } - int problemCount = reportSize.get(); project.log(problemCount + " problems found", Project.MSG_VERBOSE); for (Formatter formatter : formatters) { @@ -219,7 +217,7 @@ public class PMDTaskImpl { } } - private ResourceLoader setupResourceLoader() { + private ClassLoader setupResourceLoader() { if (classpath == null) { classpath = new Path(project); } @@ -236,11 +234,11 @@ public class PMDTaskImpl { // are loaded twice // and exist in multiple class loaders final boolean parentFirst = true; - return new ResourceLoader(new AntClassLoader(Thread.currentThread().getContextClassLoader(), - project, classpath, parentFirst)); + return new AntClassLoader(Thread.currentThread().getContextClassLoader(), + project, classpath, parentFirst); } - private void handleError(RuleContext ctx, Report errorReport, RuntimeException pmde) { + private void handleError(String filename, Report errorReport, RuntimeException pmde) { pmde.printStackTrace(); project.log(pmde.toString(), Project.MSG_VERBOSE); @@ -263,7 +261,7 @@ public class PMDTaskImpl { if (failOnError) { throw new BuildException(pmde); } - errorReport.addError(new Report.ProcessingError(pmde, String.valueOf(ctx.getSourceCodeFile()))); + errorReport.addError(new Report.ProcessingError(pmde, filename)); } private void setupClassLoader() { @@ -282,6 +280,10 @@ public class PMDTaskImpl { final ScopedLogHandlersManager logManager = new ScopedLogHandlersManager(antLogHandler.getAntLogLevel(), antLogHandler); try { doTask(); + } catch (BuildException e) { + throw e; + } catch (Exception other) { + throw new BuildException(other); } finally { logManager.close(); // only close the classloader, if it is ours. Otherwise we end up with class not found diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java index f1282ffdaa..e717973250 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java @@ -9,6 +9,7 @@ import java.util.stream.Collectors; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -21,7 +22,10 @@ import com.beust.jcommander.ParameterException; /** * @author Romain Pelisse <belaran@gmail.com> * + * @deprecated Internal API. Use {@link PMD#run(String[])} or {@link PMD#main(String[])} */ +@Deprecated +@InternalApi public final class PMDCommandLineInterface { public static final String PROG_NAME = "pmd"; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java index 3877315616..0f37b74d06 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java @@ -11,8 +11,10 @@ import java.util.Properties; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.RulePriority; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; @@ -23,6 +25,11 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; import com.beust.jcommander.validators.PositiveInteger; +/** + * @deprecated Internal API. Use {@link PMD#run(String[])} or {@link PMD#main(String[])} + */ +@Deprecated +@InternalApi public class PMDParameters { @Parameter(names = { "-rulesets", "-R" }, description = "Comma separated list of ruleset names to use.", diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java index 07552dd3e8..5bcf538f21 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java @@ -12,6 +12,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.exception.ContextedRuntimeException; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report; @@ -21,6 +22,7 @@ import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.SourceCodeProcessor; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; import net.sourceforge.pmd.benchmark.TimedOperationCategory; @@ -30,7 +32,10 @@ import net.sourceforge.pmd.util.datasource.DataSource; /** * @author Romain Pelisse <belaran@gmail.com> * + * @deprecated Is internal API */ +@Deprecated +@InternalApi public abstract class AbstractPMDProcessor { private static final Logger LOG = Logger.getLogger(AbstractPMDProcessor.class.getName()); @@ -75,13 +80,15 @@ public abstract class AbstractPMDProcessor { */ protected RuleSets createRuleSets(RuleSetFactory factory, Report report) { final RuleSets rs = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), factory); + reportBrokenRules(report, rs); + return rs; + } + public static void reportBrokenRules(Report report, RuleSets rs) { final Set brokenRules = removeBrokenRules(rs); for (final Rule rule : brokenRules) { report.addConfigError(new Report.ConfigurationError(rule, rule.dysfunctionReason())); } - - return rs; } /** @@ -91,7 +98,7 @@ public abstract class AbstractPMDProcessor { * @param ruleSets RuleSets to prune of broken rules. * @return Set */ - private Set removeBrokenRules(final RuleSets ruleSets) { + private static Set removeBrokenRules(final RuleSets ruleSets) { final Set brokenRules = new HashSet<>(); ruleSets.removeDysfunctionalRules(brokenRules); @@ -105,28 +112,37 @@ public abstract class AbstractPMDProcessor { return brokenRules; } + @Deprecated + public void processFiles(RuleSetFactory ruleSetFactory, List files, RuleContext ctx, + List renderers) { + RuleSets rs = createRuleSets(ruleSetFactory, ctx.getReport()); + processFiles(rs, files, ctx, renderers); + } + @SuppressWarnings("PMD.CloseResource") // the data sources must only be closed after the threads are finished // this is done manually without a try-with-resources - public void processFiles(RuleSetFactory ruleSetFactory, List files, RuleContext ctx, - List renderers) { + public void processFiles(RuleSets rulesets, List files, RuleContext ctx, List renderers) { try { - final RuleSets rs = createRuleSets(ruleSetFactory, ctx.getReport()); - configuration.getAnalysisCache().checkValidity(rs, configuration.getClassLoader()); + reportBrokenRules(ctx.getReport(), rulesets); + + // render base report first - general errors + renderReports(renderers, ctx.getReport()); + + configuration.getAnalysisCache().checkValidity(rulesets, configuration.getClassLoader()); final SourceCodeProcessor processor = new SourceCodeProcessor(configuration); for (final DataSource dataSource : files) { // this is the real, canonical and absolute filename (not shortened) String realFileName = dataSource.getNiceFileName(false, null); - runAnalysis(new PmdRunnable(dataSource, realFileName, renderers, ctx, rs, processor)); + runAnalysis(new PmdRunnable(dataSource, realFileName, renderers, ctx, rulesets, processor)); } - // render base report first - general errors - renderReports(renderers, ctx.getReport()); - // then add analysis results per file collectReports(renderers); + } catch (RuntimeException e) { + throw new ContextedRuntimeException(e).addContextValue("filename", String.valueOf(ctx.getSourceCodeFile())); } finally { // in case we analyzed files within Zip Files/Jars, we need to close them after // the analysis is finished diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/MonoThreadProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/MonoThreadProcessor.java index 6acf61a32c..f2776574e4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/MonoThreadProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/MonoThreadProcessor.java @@ -9,12 +9,15 @@ import java.util.List; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.renderers.Renderer; /** * @author Romain Pelisse <belaran@gmail.com> - * + * @deprecated Is internal API */ +@Deprecated +@InternalApi public final class MonoThreadProcessor extends AbstractPMDProcessor { private final List reports = new ArrayList<>(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/MultiThreadProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/MultiThreadProcessor.java index f398a90db8..8745f99bf0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/MultiThreadProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/MultiThreadProcessor.java @@ -13,12 +13,16 @@ import java.util.concurrent.Executors; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.renderers.Renderer; /** * @author Romain Pelisse <belaran@gmail.com> + * @deprecated Is internal API */ +@Deprecated +@InternalApi public class MultiThreadProcessor extends AbstractPMDProcessor { private final ExecutorService executor; private final CompletionService completionService; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java index d3ddeb6230..da91fbd95a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java @@ -17,10 +17,17 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.SourceCodeProcessor; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.util.datasource.DataSource; +/** + * + * @deprecated Is internal API + */ +@Deprecated +@InternalApi public class PmdRunnable implements Callable { private static final Logger LOG = Logger.getLogger(PmdRunnable.class.getName()); @@ -86,6 +93,9 @@ public class PmdRunnable implements Callable { TimeTracker.finishThread(); + // merge the sub-report into the global report (thread-safe) + ruleContext.getReport().merge(report); + return report; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdThreadFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdThreadFactory.java index 51c84b3063..e767b8e79b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdThreadFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdThreadFactory.java @@ -7,6 +7,14 @@ package net.sourceforge.pmd.processor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; +import net.sourceforge.pmd.annotation.InternalApi; + +/** + * + * @deprecated Is internal API + */ +@Deprecated +@InternalApi public class PmdThreadFactory implements ThreadFactory { private final AtomicInteger counter = new AtomicInteger(); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 209dea08f5..52e52f4ff5 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -495,40 +495,40 @@ public class RuleSetFactoryTest { @Test public void testReferencePriority() throws RuleSetNotFoundException { - ResourceLoader rl = new ResourceLoader(); - RuleSetFactory rsf = new RuleSetFactory(rl, RulePriority.LOW, false, true); + RuleSetLoader config = new RuleSetLoader().warnDeprecated(false).enableCompatibility(true); + RuleSetFactory rsf = config.filterAbovePriority(RulePriority.LOW).toFactory(); RuleSet ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); assertEquals("Number of Rules", 3, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("MockRuleName")); assertNotNull(ruleSet.getRuleByName("MockRuleNameRef")); assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - rsf = new RuleSetFactory(rl, RulePriority.MEDIUM_HIGH, false, true); + rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH).toFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); assertEquals("Number of Rules", 2, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("MockRuleNameRef")); assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - rsf = new RuleSetFactory(rl, RulePriority.HIGH, false, true); + rsf = config.filterAbovePriority(RulePriority.HIGH).toFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); assertEquals("Number of Rules", 1, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - rsf = new RuleSetFactory(rl, RulePriority.LOW, false, true); + rsf = config.filterAbovePriority(RulePriority.LOW).toFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); assertEquals("Number of Rules", 3, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleName")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRef")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); - rsf = new RuleSetFactory(rl, RulePriority.MEDIUM_HIGH, false, true); + rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH).toFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); assertEquals("Number of Rules", 2, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRef")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); - rsf = new RuleSetFactory(rl, RulePriority.HIGH, false, true); + rsf = config.filterAbovePriority(RulePriority.HIGH).toFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); assertEquals("Number of Rules", 1, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); @@ -536,7 +536,7 @@ public class RuleSetFactoryTest { @Test public void testOverridePriorityLoadWithMinimum() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactory(new ResourceLoader(), RulePriority.MEDIUM_LOW, true, true); + RuleSetFactory rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true).toFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); // only one rule should remain, since we filter out the other rule by minimum priority assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -557,13 +557,13 @@ public class RuleSetFactoryTest { @Test public void testExcludeWithMinimumPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.HIGH, true, true); + RuleSetFactory rsf = new RuleSetLoader().filterAbovePriority(RulePriority.HIGH).toFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // no rules should be loaded assertEquals("Number of Rules", 0, ruleset.getRules().size()); // now, load with default minimum priority - rsf = RulesetsFactoryUtils.defaultFactory(); + rsf = new RuleSetLoader().filterAbovePriority(RulePriority.LOW).toFactory(); ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // only one rule, we have excluded one... assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -592,10 +592,9 @@ public class RuleSetFactoryTest { @Test public void testSetPriority() throws RuleSetNotFoundException { - ResourceLoader rl = new ResourceLoader(); - RuleSetFactory rsf = new RuleSetFactory(rl, RulePriority.MEDIUM_HIGH, false, true); + RuleSetFactory rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).toFactory(); assertEquals(0, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); - rsf = new RuleSetFactory(rl, RulePriority.MEDIUM_LOW, false, true); + rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).toFactory(); assertEquals(1, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); } @@ -774,7 +773,7 @@ public class RuleSetFactoryTest { + " Ruleset which references a empty ruleset\n" + "\n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetFactory(new ResourceLoader(), RulePriority.LOW, true, true); + RuleSetFactory ruleSetFactory = new RuleSetLoader().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).toFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet(ref); assertEquals(0, ruleset.getRules().size()); @@ -1279,7 +1278,7 @@ public class RuleSetFactoryTest { } private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.LOW, true, false); + RuleSetFactory rsf = new RuleSetLoader().warnDeprecated(true).enableCompatibility(false).toFactory(); return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java index 22376095cb..374baa6da5 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java @@ -77,21 +77,21 @@ public class RuleSetTest { @Test public void testGetRuleByName() { MockRule mock = new MockRule("name", "desc", "msg", "rulesetname"); - RuleSet rs = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(mock); + RuleSet rs = RuleSet.forSingleRule(mock); assertEquals("unable to fetch rule by name", mock, rs.getRuleByName("name")); } @Test public void testGetRuleByName2() { MockRule mock = new MockRule("name", "desc", "msg", "rulesetname"); - RuleSet rs = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(mock); + RuleSet rs = RuleSet.forSingleRule(mock); assertNull("the rule FooRule must not be found!", rs.getRuleByName("FooRule")); } @Test public void testRuleList() { MockRule rule = new MockRule("name", "desc", "msg", "rulesetname"); - RuleSet ruleset = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet ruleset = RuleSet.forSingleRule(rule); assertEquals("Size of RuleSet isn't one.", 1, ruleset.size()); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java index d488f30fcc..6f6e1643dc 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java @@ -81,7 +81,7 @@ public class RuleSetWriterTest { ruleRef.setRuleSetReference(ruleSetReference); ruleRef.setName("Foo"); // override the name - RuleSet ruleSet = ruleSetFactory.createSingleRuleRuleSet(ruleRef); + RuleSet ruleSet = RuleSet.forSingleRule(ruleRef); writer.write(ruleSet); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java index bb3e26cb67..cfeb29973d 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.ant; import static org.junit.Assert.fail; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -71,7 +70,7 @@ public class PMDTaskTest { } @Test - public void testWithShortFilenames() throws FileNotFoundException, IOException { + public void testWithShortFilenames() throws IOException { buildRule.executeTarget("testWithShortFilenames"); try (InputStream in = new FileInputStream("target/pmd-ant-test.txt")) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index c6276c5ddc..3995a91c1f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -32,7 +32,6 @@ import org.junit.rules.ExpectedException; import net.sourceforge.pmd.FooRule; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; @@ -59,7 +58,7 @@ public class PropertyDescriptorTest { FooRule rule = new FooRule(); rule.definePropertyDescriptor(intProperty); rule.setProperty(intProperty, 1000); - RuleSet ruleSet = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet ruleSet = RuleSet.forSingleRule(rule); List dysfunctional = new ArrayList<>(); ruleSet.removeDysfunctionalRules(dysfunctional); @@ -80,7 +79,7 @@ public class PropertyDescriptorTest { FooRule rule = new FooRule(); rule.definePropertyDescriptor(descriptor); rule.setProperty(descriptor, Collections.singletonList(1000d)); // not in range - RuleSet ruleSet = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet ruleSet = RuleSet.forSingleRule(rule); List dysfunctional = new ArrayList<>(); ruleSet.removeDysfunctionalRules(dysfunctional); diff --git a/pmd-cpp/etc/grammar/Cpp.jj b/pmd-cpp/etc/grammar/Cpp.jj index 450f772c41..95640555eb 100644 --- a/pmd-cpp/etc/grammar/Cpp.jj +++ b/pmd-cpp/etc/grammar/Cpp.jj @@ -193,12 +193,14 @@ TOKEN : TOKEN: { - < #DECIMALDIGIT: ["0"-"9"] > + < #BINARYDIGIT: ["0"-"1"] > | < #OCTALDIGIT: ["0"-"7"] > +| < #DECIMALDIGIT: ["0"-"9"] > | < #HEXDIGIT: ["a"-"f", "A"-"F", "0"-"9"] > | < #INT_SUFFIX: ["u", "U", "l", "L"] | "uL" | "Ul" | "UL" | "ul" | "lu" | "Lu" | "lU" | "LU" > | < ZERO: "0" > +| < BINARY_INT_LITERAL: "0" ["b", "B"] ("'" | )+ ()? > | < OCTAL_INT_LITERAL: "0" ("'" | )+ ()? > | < DECIMAL_INT_LITERAL: ["1"-"9"] ("'" | )* ()? > | < HEXADECIMAL_INT_LITERAL: "0" ["x", "X"] ("'" | )+ ()? > diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp index 9f0d38b06f..cbae7336ba 100644 --- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp @@ -37,4 +37,7 @@ auto hex_literal = 0x0F00'abcd'6f3d; auto silly_example = 1'0'0'000'00; + // boolean literals + int b1 = 0B001101; // C++ 14 binary literal + int b2 = 0b000001; // C++ 14 binary literal } \ No newline at end of file diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt index 3a0185425f..79afe0b26c 100644 --- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt @@ -118,6 +118,18 @@ L38 [=] 24 25 [1'0'0'000'00] 26 38 [;] 38 39 -L40 +L41 + [int] 5 8 + [b1] 9 11 + [=] 12 13 + [0B001101] 14 22 + [;] 22 23 +L42 + [int] 5 8 + [b2] 9 11 + [=] 12 13 + [0b000001] 14 22 + [;] 22 23 +L43 [}] 1 2 EOF diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java index 2256a21d18..1658262fb3 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java @@ -13,24 +13,20 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; -import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.RuleSetLoader; public final class GenerateRuleDocsCmd { private GenerateRuleDocsCmd() { // Utility class } - public static void main(String[] args) throws RuleSetNotFoundException { + public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("One argument is required: The base directory of the module pmd-doc."); System.exit(1); @@ -41,8 +37,7 @@ public final class GenerateRuleDocsCmd { System.out.println("Generating docs into " + output); // important: use a RuleSetFactory that includes all rules, e.g. deprecated rule references - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.createFactory(RulePriority.LOW, false, false, true); - Iterator registeredRuleSets = ruleSetFactory.getRegisteredRuleSets(); + List registeredRuleSets = new RuleSetLoader().getStandardRuleSets(); List additionalRulesets = findAdditionalRulesets(output); RuleDocGenerator generator = new RuleDocGenerator(new DefaultFileWriter(), output); diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java index f62400b1a9..6f7c7e60ce 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -17,7 +17,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -29,6 +28,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; @@ -36,9 +36,8 @@ import org.apache.commons.text.StringEscapeUtils; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.RuleSetLoadException; +import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.XPathRule; @@ -91,21 +90,16 @@ public class RuleDocGenerator { } } - public void generate(Iterator registeredRulesets, List additionalRulesets) { + public void generate(List registeredRulesets, List additionalRulesets) throws IOException { Map> sortedRulesets; Map> sortedAdditionalRulesets; - try { - sortedRulesets = sortRulesets(registeredRulesets); - sortedAdditionalRulesets = sortRulesets(resolveAdditionalRulesets(additionalRulesets)); - determineRuleClassSourceFiles(sortedRulesets); - generateLanguageIndex(sortedRulesets, sortedAdditionalRulesets); - generateRuleSetIndex(sortedRulesets); + sortedRulesets = sortRulesets(registeredRulesets); + sortedAdditionalRulesets = sortRulesets(resolveAdditionalRulesets(additionalRulesets)); + determineRuleClassSourceFiles(sortedRulesets); + generateLanguageIndex(sortedRulesets, sortedAdditionalRulesets); + generateRuleSetIndex(sortedRulesets); - generateSidebar(sortedRulesets); - - } catch (RuleSetNotFoundException | IOException e) { - throw new RuntimeException(e); - } + generateSidebar(sortedRulesets); } private void generateSidebar(Map> sortedRulesets) throws IOException { @@ -113,53 +107,40 @@ public class RuleDocGenerator { generator.generateSidebar(sortedRulesets); } - private Iterator resolveAdditionalRulesets(List additionalRulesets) throws RuleSetNotFoundException { + private List resolveAdditionalRulesets(List additionalRulesets) { if (additionalRulesets == null) { - return Collections.emptyIterator(); + return Collections.emptyList(); } List rulesets = new ArrayList<>(); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); + RuleSetLoader ruleSetLoader = new RuleSetLoader(); for (String filename : additionalRulesets) { try { // do not take rulesets from pmd-test or pmd-core if (!filename.contains("pmd-test") && !filename.contains("pmd-core")) { - rulesets.add(ruleSetFactory.createRuleSet(filename)); + rulesets.add(ruleSetLoader.loadFromResource(filename)); } else { LOG.fine("Ignoring ruleset " + filename); } - } catch (IllegalArgumentException e) { + } catch (RuleSetLoadException e) { // ignore rulesets, we can't read LOG.log(Level.WARNING, "ruleset file " + filename + " ignored (" + e.getMessage() + ")", e); } } - return rulesets.iterator(); + return rulesets; } private Path getAbsoluteOutputPath(String filename) { return root.resolve(FilenameUtils.normalize(filename)); } - private Map> sortRulesets(Iterator rulesets) throws RuleSetNotFoundException { - SortedMap> rulesetsByLanguage = new TreeMap<>(); - - while (rulesets.hasNext()) { - RuleSet ruleset = rulesets.next(); - Language language = getRuleSetLanguage(ruleset); - - if (!rulesetsByLanguage.containsKey(language)) { - rulesetsByLanguage.put(language, new ArrayList()); - } - rulesetsByLanguage.get(language).add(ruleset); - } + private Map> sortRulesets(List rulesets) { + SortedMap> rulesetsByLanguage = rulesets.stream().collect(Collectors.groupingBy(RuleDocGenerator::getRuleSetLanguage, + TreeMap::new, + Collectors.toCollection(ArrayList::new))); for (List rulesetsOfOneLanguage : rulesetsByLanguage.values()) { - Collections.sort(rulesetsOfOneLanguage, new Comparator() { - @Override - public int compare(RuleSet o1, RuleSet o2) { - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); + rulesetsOfOneLanguage.sort((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); } return rulesetsByLanguage; } diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java index 5e7561cdf3..23d91a043d 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java @@ -65,7 +65,7 @@ public class RuleDocGeneratorTest { RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.LOW, false, false, true); RuleSet ruleset = rsf.createRuleSet("rulesets/ruledoctest/sample.xml"); - generator.generate(Arrays.asList(ruleset).iterator(), + generator.generate(Arrays.asList(ruleset), Arrays.asList( "rulesets/ruledoctest/sample-deprecated.xml", "rulesets/ruledoctest/other-ruleset.xml")); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java index 3401199005..23de5ce3de 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java @@ -7,16 +7,15 @@ package net.sourceforge.pmd.lang.java; import org.junit.Assert; import org.junit.Test; -import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.util.ResourceLoader; +import net.sourceforge.pmd.RuleSetLoader; public class PMD5RulesetTest { @Test public void loadRuleset() throws Exception { - RuleSetFactory ruleSetFactory = new RuleSetFactory(new ResourceLoader(), RulePriority.LOW, true, true); + RuleSetFactory ruleSetFactory = new RuleSetLoader().toFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet("net/sourceforge/pmd/lang/java/pmd5ruleset.xml"); Assert.assertNotNull(ruleset); Assert.assertNull(ruleset.getRuleByName("GuardLogStatementJavaUtil")); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java index 816c98b918..16c3142ab9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java @@ -14,11 +14,10 @@ import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; -import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; +import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.util.ResourceLoader; public class QuickstartRulesetTest { @@ -50,7 +49,7 @@ public class QuickstartRulesetTest { } }); - RuleSetFactory ruleSetFactory = new RuleSetFactory(new ResourceLoader(), RulePriority.LOW, true, false); + RuleSetFactory ruleSetFactory = new RuleSetLoader().enableCompatibility(false).toFactory(); RuleSet quickstart = ruleSetFactory.createRuleSet("rulesets/java/quickstart.xml"); Assert.assertFalse(quickstart.getRules().isEmpty()); } diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java index 90b38f89a6..80a8c252da 100644 --- a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java +++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java @@ -18,7 +18,6 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.jsp.JspLanguageModule; import net.sourceforge.pmd.lang.rule.XPathRule; @@ -36,7 +35,7 @@ public class XPathJspRuleTest extends RuleTst { Rule rule = new XPathRule(XPathVersion.XPATH_3_1, XPATH_EXPRESSION); rule.setMessage("Test"); rule.setLanguage(LanguageRegistry.getLanguage(JspLanguageModule.NAME)); - RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet rules = RuleSet.forSingleRule(rule); RuleContext ctx = new RuleContext(); Report report = new Report(); diff --git a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java index 111815dbca..7c4e8d4978 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java +++ b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java @@ -20,5 +20,4 @@ public final class ScalaParsingHelper extends BaseParsingHelper org.codehaus.groovy groovy - 2.4.7 + 2.4.21