Merge pull request #3260 from oowekyala:deprecate-ThreadSafeReportListener
[core] Deprecate more APIs around PMD's entry point #3260
This commit is contained in:
14 files changed
+405
-151
No files matched your search
@@ -70,7 +70,7 @@ public class PmdExample {
|
||||
configuration.setReportFormat("xml");
|
||||
configuration.setReportFile("/home/workspace/pmd-report.xml");
|
||||
|
||||
PMD.doPMD(configuration);
|
||||
PMD.runPMD(configuration);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -78,7 +78,7 @@ public class PmdExample {
|
||||
## Programmatically, variant 2
|
||||
|
||||
This gives you more control over which files are processed, but is also more complicated.
|
||||
You can also provide your own listeners and renderers.
|
||||
You can also provide your own custom renderers.
|
||||
|
||||
1. First we create a `PMDConfiguration`. This is currently the only way to specify a ruleset:
|
||||
|
||||
@@ -96,11 +96,12 @@ You can also provide your own listeners and renderers.
|
||||
configuration.prependClasspath("/home/workspace/target/classes:/home/.m2/repository/my/dependency.jar");
|
||||
```
|
||||
|
||||
3. Then we need a ruleset factory. This is created using the configuration, taking the minimum priority into
|
||||
3. Then we need to load the rulesets. This is done by using the configuration, taking the minimum priority into
|
||||
account:
|
||||
|
||||
```java
|
||||
RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.createFactory(configuration);
|
||||
RuleSetLoader ruleSetLoader = RuleSetLoader.fromPmdConfig(configuration);
|
||||
List<RuleSet> ruleSets = ruleSetLoader.loadFromResources(Arrays.asList(configuration.getRuleSets().split(",")));
|
||||
```
|
||||
|
||||
4. PMD operates on a list of `DataSource`. You can assemble a own list of `FileDataSource`, e.g.
|
||||
@@ -109,7 +110,8 @@ You can also provide your own listeners and renderers.
|
||||
List<DataSource> files = Arrays.asList(new FileDataSource(new File("/path/to/src/MyClass.java")));
|
||||
```
|
||||
|
||||
5. For reporting, you can use a built-in renderer, e.g. `XMLRenderer`. Note, that you must manually initialize
|
||||
5. For reporting, you can use a built-in renderer, e.g. `XMLRenderer` or a custom renderer implementing
|
||||
`Renderer`. Note, that you must manually initialize
|
||||
the renderer by setting a suitable `Writer` and calling `start()`. After the PMD run, you need to call
|
||||
`end()` and `flush()`. Then your writer should have received all output.
|
||||
|
||||
@@ -120,36 +122,15 @@ You can also provide your own listeners and renderers.
|
||||
xmlRenderer.start();
|
||||
```
|
||||
|
||||
6. Create a `RuleContext`. This is the context instance, that is available then in the rule implementations.
|
||||
Note: when running in multi-threaded mode (which is the default), the rule context instance is cloned for
|
||||
each thread.
|
||||
|
||||
```java
|
||||
RuleContext ctx = new RuleContext();
|
||||
```
|
||||
|
||||
7. Optionally register a report listener. This allows you to react immediately on found violations. You could also
|
||||
use such a listener to implement your own renderer. The listener must implement the interface
|
||||
`ThreadSafeReportListener` and can be registered via `ctx.getReport().addListener(...)`.
|
||||
|
||||
```java
|
||||
ctx.getReport().addListener(new ThreadSafeReportListener() {
|
||||
public void ruleViolationAdded(RuleViolation ruleViolation) {
|
||||
}
|
||||
public void metricAdded(Metric metric) {
|
||||
}
|
||||
```
|
||||
|
||||
8. Now, all the preparations are done, and PMD can be executed. This is done by calling
|
||||
`PMD.processFiles(...)`. This method call takes the configuration, the ruleset factory, the files
|
||||
to process, the rule context and the list of renderers. Provide an empty list, if you don't want to use
|
||||
6. Now, all the preparations are done, and PMD can be executed. This is done by calling
|
||||
`PMD.processFiles(...)`. This method call takes the configuration, the rulesets, the files
|
||||
to process, and the list of renderers. Provide an empty list, if you don't want to use
|
||||
any renderer. Note: The auxclasspath needs to be closed explicitly. Otherwise the class or jar files may
|
||||
remain open and file resources are leaked.
|
||||
|
||||
```java
|
||||
try {
|
||||
PMD.processFiles(configuration, ruleSetFactory, files, ctx,
|
||||
Collections.singletonList(renderer));
|
||||
PMD.processFiles(configuration, ruleSets, files, Collections.singletonList(renderer));
|
||||
} finally {
|
||||
ClassLoader auxiliaryClassLoader = configuration.getClassLoader();
|
||||
if (auxiliaryClassLoader instanceof ClasspathClassLoader) {
|
||||
@@ -158,7 +139,7 @@ You can also provide your own listeners and renderers.
|
||||
}
|
||||
```
|
||||
|
||||
9. After the call, you need to finish the renderer via `end()` and `flush()`.
|
||||
7. After the call, you need to finish the renderer via `end()` and `flush()`.
|
||||
Then you can check the rendered output.
|
||||
|
||||
``` java
|
||||
@@ -182,20 +163,17 @@ import java.nio.file.PathMatcher;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.PMD;
|
||||
import net.sourceforge.pmd.PMDConfiguration;
|
||||
import net.sourceforge.pmd.RuleContext;
|
||||
import net.sourceforge.pmd.RulePriority;
|
||||
import net.sourceforge.pmd.RuleSetFactory;
|
||||
import net.sourceforge.pmd.RuleViolation;
|
||||
import net.sourceforge.pmd.RulesetsFactoryUtils;
|
||||
import net.sourceforge.pmd.ThreadSafeReportListener;
|
||||
import net.sourceforge.pmd.RuleSet;
|
||||
import net.sourceforge.pmd.RuleSetLoader;
|
||||
import net.sourceforge.pmd.renderers.Renderer;
|
||||
import net.sourceforge.pmd.renderers.XMLRenderer;
|
||||
import net.sourceforge.pmd.stat.Metric;
|
||||
import net.sourceforge.pmd.util.ClasspathClassLoader;
|
||||
import net.sourceforge.pmd.util.datasource.DataSource;
|
||||
import net.sourceforge.pmd.util.datasource.FileDataSource;
|
||||
@@ -207,7 +185,8 @@ public class PmdExample2 {
|
||||
configuration.setMinimumPriority(RulePriority.MEDIUM);
|
||||
configuration.setRuleSets("rulesets/java/quickstart.xml");
|
||||
configuration.prependClasspath("/home/workspace/target/classes");
|
||||
RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.createFactory(configuration);
|
||||
RuleSetLoader ruleSetLoader = RuleSetLoader.fromPmdConfig(configuration);
|
||||
List<RuleSet> ruleSets = ruleSetLoader.loadFromResources(Arrays.asList(configuration.getRuleSets().split(",")));
|
||||
|
||||
List<DataSource> files = determineFiles("/home/workspace/src/main/java/code");
|
||||
|
||||
@@ -215,13 +194,8 @@ public class PmdExample2 {
|
||||
Renderer renderer = createRenderer(rendererOutput);
|
||||
renderer.start();
|
||||
|
||||
RuleContext ctx = new RuleContext();
|
||||
|
||||
ctx.getReport().addListener(createReportListener()); // alternative way to collect violations
|
||||
|
||||
try {
|
||||
PMD.processFiles(configuration, ruleSetFactory, files, ctx,
|
||||
Collections.singletonList(renderer));
|
||||
PMD.processFiles(configuration, ruleSets, files, Collections.singletonList(renderer));
|
||||
} finally {
|
||||
ClassLoader auxiliaryClassLoader = configuration.getClassLoader();
|
||||
if (auxiliaryClassLoader instanceof ClasspathClassLoader) {
|
||||
@@ -235,21 +209,6 @@ public class PmdExample2 {
|
||||
System.out.println(rendererOutput.toString());
|
||||
}
|
||||
|
||||
private static ThreadSafeReportListener createReportListener() {
|
||||
return new ThreadSafeReportListener() {
|
||||
@Override
|
||||
public void ruleViolationAdded(RuleViolation ruleViolation) {
|
||||
System.out.printf("%-20s:%d %s%n", ruleViolation.getFilename(),
|
||||
ruleViolation.getBeginLine(), ruleViolation.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void metricAdded(Metric metric) {
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Renderer createRenderer(Writer writer) {
|
||||
XMLRenderer xml = new XMLRenderer("UTF-8");
|
||||
xml.setWriter(writer);
|
||||
@@ -258,9 +217,9 @@ public class PmdExample2 {
|
||||
|
||||
private static List<DataSource> determineFiles(String basePath) throws IOException {
|
||||
Path dirPath = FileSystems.getDefault().getPath(basePath);
|
||||
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");
|
||||
final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");
|
||||
|
||||
List<DataSource> files = new ArrayList<>();
|
||||
final List<DataSource> files = new ArrayList<>();
|
||||
|
||||
Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
|
||||
@@ -78,6 +78,7 @@ to analyze JavaScript code. Note that PMD core still only requires Java 7.
|
||||
* [#3243](https://github.com/pmd/pmd/pull/3243): \[apex] Correct findBoundary when traversing AST
|
||||
* core
|
||||
* [#2639](https://github.com/pmd/pmd/issues/2639): \[core] PMD CLI output file is not created if directory or directories in path don't exist
|
||||
* [#3196](https://github.com/pmd/pmd/issues/3196): \[core] Deprecate ThreadSafeReportListener
|
||||
* doc
|
||||
* [#3230](https://github.com/pmd/pmd/issues/3230): \[doc] Remove "Edit me" button for language index pages
|
||||
* dist
|
||||
@@ -115,6 +116,17 @@ to analyze JavaScript code. Note that PMD core still only requires Java 7.
|
||||
|
||||
### API Changes
|
||||
|
||||
#### Deprecated API
|
||||
|
||||
* {% jdoc !!core::PMD#doPMD(PMDConfiguration) %} is deprecated.
|
||||
Use {% jdoc !!core::PMD#runPMD(PMDConfiguration) %} instead.
|
||||
* {% jdoc !!core::PMD#run(String[]) %} is deprecated.
|
||||
Use {% jdoc !!core::PMD#runPMD(String...) %} instead.
|
||||
* {% jdoc core::ThreadSafeReportListener %} and the methods to use them in {% jdoc core::Report %}
|
||||
({% jdoc core::Report#addListener(ThreadSafeReportListener) %}, {% jdoc core::Report#getListeners() %},
|
||||
{% jdoc core::Report#addListeners(List<ThreadSafeReportListener>) %}) are deprecated. This functionality
|
||||
will be replaced by another TBD mechanism in PMD 7.
|
||||
|
||||
### External Contributions
|
||||
* [#3272](https://github.com/pmd/pmd/pull/3272): \[apex] correction for ApexUnitTestMethodShouldHaveIsTestAnnotation false positives - [William Brockhus](https://github.com/YodaDaCoda)
|
||||
* [#3246](https://github.com/pmd/pmd/pull/3246): \[java] New Rule: MutableStaticState - [Vsevolod Zholobov](https://github.com/vszholobov)
|
||||
|
||||
@@ -31,7 +31,7 @@ import net.sourceforge.pmd.benchmark.TimingReport;
|
||||
import net.sourceforge.pmd.benchmark.TimingReportRenderer;
|
||||
import net.sourceforge.pmd.cache.NoopAnalysisCache;
|
||||
import net.sourceforge.pmd.cli.PMDCommandLineInterface;
|
||||
import net.sourceforge.pmd.cli.PMDParameters;
|
||||
import net.sourceforge.pmd.cli.PmdParametersParseResult;
|
||||
import net.sourceforge.pmd.lang.Language;
|
||||
import net.sourceforge.pmd.lang.LanguageFilenameFilter;
|
||||
import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
@@ -213,10 +213,13 @@ public class PMD {
|
||||
/**
|
||||
* This method is the main entry point for command line usage.
|
||||
*
|
||||
* @param configuration
|
||||
* the configure to use
|
||||
* @param configuration the configure to use
|
||||
*
|
||||
* @return number of violations found.
|
||||
*
|
||||
* @deprecated Use {@link #runPmd(PMDConfiguration)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static int doPMD(PMDConfiguration configuration) {
|
||||
|
||||
// Load the RuleSets
|
||||
@@ -263,7 +266,7 @@ public class PMD {
|
||||
* Make sure it's our own classloader before attempting to close it....
|
||||
* Maven + Jacoco provide us with a cloaseable classloader that if closed
|
||||
* will throw a ClassNotFoundException.
|
||||
*/
|
||||
*/
|
||||
if (configuration.getClassLoader() instanceof ClasspathClassLoader) {
|
||||
IOUtil.tryCloseClassLoader(configuration.getClassLoader());
|
||||
}
|
||||
@@ -413,7 +416,7 @@ public class PMD {
|
||||
}
|
||||
|
||||
private static List<DataSource> internalGetApplicableFiles(PMDConfiguration configuration,
|
||||
Set<Language> languages) {
|
||||
Set<Language> languages) {
|
||||
LanguageFilenameFilter fileSelector = new LanguageFilenameFilter(languages);
|
||||
List<DataSource> files = new ArrayList<>();
|
||||
|
||||
@@ -490,59 +493,100 @@ public class PMD {
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry to invoke PMD as command line tool. Note that this will invoke {@link System#exit(int)}.
|
||||
* Entry to invoke PMD as command line tool. Note that this will
|
||||
* invoke {@link System#exit(int)}.
|
||||
*
|
||||
* @param args
|
||||
* command line arguments
|
||||
* @param args command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
PMDCommandLineInterface.run(args);
|
||||
StatusCode exitCode = runPmd(args);
|
||||
PMDCommandLineInterface.setStatusCodeOrExit(exitCode.toInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the command line arguments and executes PMD. Returns the
|
||||
* exit code without exiting the VM.
|
||||
*
|
||||
* @param args
|
||||
* command line arguments
|
||||
* @param args command line arguments
|
||||
*
|
||||
* @return the exit code, where <code>0</code> means successful execution,
|
||||
* <code>1</code> means error, <code>4</code> means there have been
|
||||
* violations found.
|
||||
* <code>1</code> means error, <code>4</code> means there have been
|
||||
* violations found.
|
||||
*
|
||||
* @deprecated Use {@link #runPmd(String...)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static int run(String[] args) {
|
||||
final PMDParameters params = PMDCommandLineInterface.extractParameters(new PMDParameters(), args, "pmd");
|
||||
return runPmd(args).toInt();
|
||||
}
|
||||
|
||||
if (params.isBenchmark()) {
|
||||
/**
|
||||
* Parses the command line arguments and executes PMD. Returns the
|
||||
* status code without exiting the VM. Note that the arguments parsing
|
||||
* may itself fail and produce a {@link StatusCode#ERROR}. This will
|
||||
* print the error message to standard error.
|
||||
*
|
||||
* @param args command line arguments
|
||||
*
|
||||
* @return the status code. Note that {@link PMDConfiguration#isFailOnViolation()}
|
||||
* (flag {@code --failOnViolation}) may turn an {@link StatusCode#OK} into a {@link
|
||||
* StatusCode#VIOLATIONS_FOUND}.
|
||||
*/
|
||||
public static StatusCode runPmd(String... args) {
|
||||
PmdParametersParseResult parseResult = PmdParametersParseResult.extractParameters(args);
|
||||
if (parseResult.isHelp()) {
|
||||
PMDCommandLineInterface.printJcommanderUsageOnConsole();
|
||||
System.out.println(PMDCommandLineInterface.buildUsageText());
|
||||
return StatusCode.OK;
|
||||
} else if (parseResult.isError()) {
|
||||
System.out.println(PMDCommandLineInterface.buildUsageText());
|
||||
System.err.println(parseResult.getError().getMessage());
|
||||
return StatusCode.ERROR;
|
||||
}
|
||||
return runPmd(parseResult.toConfiguration());
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute PMD from a configuration. Returns the status code without
|
||||
* exiting the VM. This is the main entry point to run a full PMD run
|
||||
* with a manually created configuration.
|
||||
*
|
||||
* @param configuration Configuration to run
|
||||
*
|
||||
* @return the status code. Note that {@link PMDConfiguration#isFailOnViolation()}
|
||||
* (flag {@code --failOnViolation}) may turn an {@link StatusCode#OK} into a {@link
|
||||
* StatusCode#VIOLATIONS_FOUND}.
|
||||
*/
|
||||
public static StatusCode runPmd(PMDConfiguration configuration) {
|
||||
if (configuration.isBenchmark()) {
|
||||
TimeTracker.startGlobalTracking();
|
||||
}
|
||||
|
||||
int status = PMDCommandLineInterface.NO_ERRORS_STATUS;
|
||||
final PMDConfiguration configuration = params.toConfiguration();
|
||||
|
||||
final Level logLevel = params.isDebug() ? Level.FINER : Level.INFO;
|
||||
final Level logLevel = configuration.isDebug() ? Level.FINER : Level.INFO;
|
||||
final ScopedLogHandlersManager logHandlerManager = new ScopedLogHandlersManager(logLevel, new ConsoleHandler());
|
||||
final Level oldLogLevel = LOG.getLevel();
|
||||
// Need to do this, since the static logger has already been initialized
|
||||
// at this point
|
||||
LOG.setLevel(logLevel);
|
||||
|
||||
StatusCode status;
|
||||
try {
|
||||
int violations = PMD.doPMD(configuration);
|
||||
if (violations > 0 && configuration.isFailOnViolation()) {
|
||||
status = PMDCommandLineInterface.VIOLATIONS_FOUND;
|
||||
status = StatusCode.VIOLATIONS_FOUND;
|
||||
} else {
|
||||
status = PMDCommandLineInterface.NO_ERRORS_STATUS;
|
||||
status = StatusCode.OK;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println(PMDCommandLineInterface.buildUsageText());
|
||||
System.out.println();
|
||||
System.err.println(e.getMessage());
|
||||
status = PMDCommandLineInterface.ERROR_STATUS;
|
||||
status = StatusCode.ERROR;
|
||||
} finally {
|
||||
logHandlerManager.close();
|
||||
LOG.setLevel(oldLogLevel);
|
||||
|
||||
if (params.isBenchmark()) {
|
||||
if (configuration.isBenchmark()) {
|
||||
final TimingReport timingReport = TimeTracker.stopGlobalTracking();
|
||||
|
||||
// TODO get specified report format from config
|
||||
@@ -559,4 +603,37 @@ public class PMD {
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents status codes that are used as exit codes during CLI runs.
|
||||
*
|
||||
* @see #runPmd(String[])
|
||||
*/
|
||||
public enum StatusCode {
|
||||
/** No errors, no violations. This is exit code {@code 0}. */
|
||||
OK(0),
|
||||
/**
|
||||
* Errors were detected, PMD may have not run to the end.
|
||||
* This is exit code {@code 1}.
|
||||
*/
|
||||
ERROR(1),
|
||||
/**
|
||||
* No errors, but PMD found violations. This is exit code {@code 4}.
|
||||
* This is only returned if {@link PMDConfiguration#isFailOnViolation()}
|
||||
* is set (CLI flag {@code --failOnViolation}).
|
||||
*/
|
||||
VIOLATIONS_FOUND(4);
|
||||
|
||||
private final int code;
|
||||
|
||||
StatusCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/** Returns the exit code as used in CLI. */
|
||||
public int toInt() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import java.util.Properties;
|
||||
import net.sourceforge.pmd.cache.AnalysisCache;
|
||||
import net.sourceforge.pmd.cache.FileAnalysisCache;
|
||||
import net.sourceforge.pmd.cache.NoopAnalysisCache;
|
||||
import net.sourceforge.pmd.cli.PmdParametersParseResult;
|
||||
import net.sourceforge.pmd.lang.LanguageRegistry;
|
||||
import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
|
||||
@@ -21,8 +22,11 @@ import net.sourceforge.pmd.renderers.RendererFactory;
|
||||
import net.sourceforge.pmd.util.ClasspathClassLoader;
|
||||
|
||||
/**
|
||||
* This class contains the details for the runtime configuration of PMD. There
|
||||
* are several aspects to the configuration of PMD.
|
||||
* This class contains the details for the runtime configuration of a PMD run.
|
||||
* You can either create one and set individual fields, or mimic a CLI run by
|
||||
* using {@link PmdParametersParseResult#extractParameters(String...) extractParameters}.
|
||||
*
|
||||
* <p>There are several aspects to the configuration of PMD.
|
||||
*
|
||||
* <p>The aspects related to generic PMD behavior:</p>
|
||||
* <ul>
|
||||
|
||||
@@ -312,6 +312,7 @@ public class Report implements Iterable<RuleViolation> {
|
||||
* @param listener
|
||||
* the listener
|
||||
*/
|
||||
@Deprecated
|
||||
public void addListener(ThreadSafeReportListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
@@ -629,6 +630,10 @@ public class Report implements Iterable<RuleViolation> {
|
||||
return end - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated {@link ThreadSafeReportListener} is deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public List<ThreadSafeReportListener> getListeners() {
|
||||
return listeners;
|
||||
}
|
||||
@@ -638,7 +643,10 @@ public class Report implements Iterable<RuleViolation> {
|
||||
*
|
||||
* @param allListeners
|
||||
* the report listeners
|
||||
*
|
||||
* @deprecated {@link ThreadSafeReportListener} is deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public void addListeners(List<ThreadSafeReportListener> allListeners) {
|
||||
listeners.addAll(allListeners);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@ import net.sourceforge.pmd.stat.Metric;
|
||||
*
|
||||
* Thread-safety is required only for concurrently notifying about different files.
|
||||
* Same file violations are guaranteed to be reported serially.
|
||||
*
|
||||
* @deprecated All entry points of PMD that allowed usage of this are now deprecated.
|
||||
* This will be replaced by another TBD mechanism in PMD 7.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ThreadSafeReportListener {
|
||||
/**
|
||||
* A new violation has been found.
|
||||
|
||||
@@ -19,8 +19,8 @@ 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 Internal API. Use {@link PMD#runPmd(String...)} or {@link PMD#main(String[])},
|
||||
* or {@link PmdParametersParseResult} if you just want to produce a configuration.
|
||||
*/
|
||||
@Deprecated
|
||||
@InternalApi
|
||||
@@ -37,6 +37,12 @@ public final class PMDCommandLineInterface {
|
||||
|
||||
private PMDCommandLineInterface() { }
|
||||
|
||||
/**
|
||||
* Note: this may terminate the VM.
|
||||
*
|
||||
* @deprecated Use {@link PmdParametersParseResult#extractParameters(String...)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static PMDParameters extractParameters(PMDParameters arguments, String[] args, String progName) {
|
||||
JCommander jcommander = new JCommander(arguments);
|
||||
jcommander.setProgramName(progName);
|
||||
@@ -152,6 +158,10 @@ public final class PMDCommandLineInterface {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link PMD#main(String[])}
|
||||
*/
|
||||
@Deprecated
|
||||
public static void run(String[] args) {
|
||||
setStatusCodeOrExit(PMD.run(args));
|
||||
}
|
||||
@@ -176,4 +186,7 @@ public final class PMDCommandLineInterface {
|
||||
System.setProperty(STATUS_CODE_PROPERTY, Integer.toString(statusCode));
|
||||
}
|
||||
|
||||
public static void printJcommanderUsageOnConsole() {
|
||||
new JCommander(new PMDParameters()).usage();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ 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 Internal API. Use {@link PMD#runPmd(String[])} or {@link PMD#main(String[])}
|
||||
*/
|
||||
@Deprecated
|
||||
@InternalApi
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.cli;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import net.sourceforge.pmd.PMDConfiguration;
|
||||
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.ParameterException;
|
||||
|
||||
/**
|
||||
* Result of parsing a bunch of CLI arguments. Parsing may fail with an
|
||||
* exception, or succeed and produce a {@link PMDConfiguration}. If the
|
||||
* {@code --help} argument is mentioned, no configuration is produced.
|
||||
*/
|
||||
public final class PmdParametersParseResult {
|
||||
|
||||
private final PMDParameters result;
|
||||
private final ParameterException error;
|
||||
|
||||
PmdParametersParseResult(PMDParameters result) {
|
||||
this.result = Objects.requireNonNull(result);
|
||||
this.error = null;
|
||||
}
|
||||
|
||||
PmdParametersParseResult(ParameterException error) {
|
||||
this.result = null;
|
||||
this.error = Objects.requireNonNull(error);
|
||||
}
|
||||
|
||||
/** Returns true if parsing failed. */
|
||||
public boolean isError() {
|
||||
return result == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether parsing just requested the {@code --help} text.
|
||||
* In this case no configuration is produced.
|
||||
*/
|
||||
public boolean isHelp() {
|
||||
return !isError() && result.isHelp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error if parsing failed. Parsing may fail if required
|
||||
* parameters are not provided, or if some parameters don't pass validation.
|
||||
* Otherwise returns null.
|
||||
*/
|
||||
public ParameterException getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resulting configuration if parsing succeeded and not {@link #isHelp().
|
||||
* Otherwise returns null.
|
||||
*/
|
||||
public PMDConfiguration toConfiguration() {
|
||||
return result != null && !isHelp() ? result.toConfiguration() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an array of CLI parameters and returns a result (which may be failed).
|
||||
* Use this instead of {@link PMDCommandLineInterface#extractParameters(PMDParameters, String[], String)},
|
||||
* because that one may terminate the VM.
|
||||
*
|
||||
* @param args Array of parameters
|
||||
*
|
||||
* @return A parse result
|
||||
*
|
||||
* @throws NullPointerException If the parameter array is null
|
||||
*/
|
||||
public static PmdParametersParseResult extractParameters(String... args) {
|
||||
Objects.requireNonNull(args, "Null parameter array");
|
||||
PMDParameters result = new PMDParameters();
|
||||
JCommander jcommander = new JCommander(result);
|
||||
jcommander.setProgramName("pmd");
|
||||
|
||||
try {
|
||||
jcommander.parse(args);
|
||||
return new PmdParametersParseResult(result);
|
||||
} catch (ParameterException e) {
|
||||
return new PmdParametersParseResult(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,8 +145,8 @@ public abstract class BaseLanguageModule implements Language {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasExtension(String extension) {
|
||||
return extensions != null && extensions.contains(extension);
|
||||
public boolean hasExtension(String extensionWithoutDot) {
|
||||
return extensions != null && extensions.contains(extensionWithoutDot);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,22 +5,25 @@
|
||||
package net.sourceforge.pmd.lang;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Interface each Language implementation has to implement. It is used by the
|
||||
* LanguageRegistry to access constants and implementation classes in order to
|
||||
* provide support for the language.
|
||||
* <p>
|
||||
* The following are key components of a Language in PMD:
|
||||
* Represents a language module, and provides access to language-specific
|
||||
* functionality. You can get a language instance from {@link LanguageRegistry}.
|
||||
* Using a language involves first selecting the relevant {@link LanguageVersion}
|
||||
* for the sources, and accessing implemented services through {@link LanguageVersion#getLanguageVersionHandler()}.
|
||||
*
|
||||
* <p>Language instances must be registered with a {@linkplain ServiceLoader service file}
|
||||
* to be picked up on by the {@link LanguageRegistry}.
|
||||
*
|
||||
* <p>The following are key components of a language in PMD:
|
||||
* <ul>
|
||||
* <li>Name - Full name of the Language</li>
|
||||
* <li>Short name - The common short form of the Language</li>
|
||||
* <li>Terse name - The shortest and simplest possible form of the Language
|
||||
* name, generally used for Rule configuration</li>
|
||||
* <li>Extensions - File extensions associated with the Language</li>
|
||||
* <li>Rule Chain Visitor - The RuleChainVisitor implementation used for this
|
||||
* Language</li>
|
||||
* <li>Versions - The LanguageVersions associated with the Language</li>
|
||||
* <li>Name - Full name of the language</li>
|
||||
* <li>Short name - The common short form of the language</li>
|
||||
* <li>Terse name - The shortest and simplest possible form of the language
|
||||
* name, generally used for rule configuration</li>
|
||||
* <li>Extensions - File extensions associated with the language</li>
|
||||
* <li>Versions - The language versions associated with the language</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see LanguageVersion
|
||||
@@ -31,45 +34,51 @@ public interface Language extends Comparable<Language> {
|
||||
String LANGUAGE_MODULES_CLASS_NAMES_PROPERTY = "languageModulesClassNames";
|
||||
|
||||
/**
|
||||
* Get the full name of this Language. This is generally the name of this
|
||||
* Language without the use of acronyms.
|
||||
* Returns the full name of this Language. This is generally the name of this
|
||||
* language without the use of acronyms, but possibly some capital letters,
|
||||
* eg {@code "Java"}. It's suitable for displaying in a GUI.
|
||||
*
|
||||
* @return The full name of this Language.
|
||||
* @return The full name of this language.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Get the short name of this Language. This is the commonly used short form
|
||||
* of this Language's name, perhaps an acronym.
|
||||
* Returns the short name of this language. This is the commonly
|
||||
* used short form of this language's name, perhaps an acronym,
|
||||
* but possibly with special characters.
|
||||
*
|
||||
* @return The short name of this Language.
|
||||
* @return The short name of this language.
|
||||
*/
|
||||
String getShortName();
|
||||
|
||||
/**
|
||||
* Get the terse name of this Language. This is used for Rule configuration.
|
||||
* Returns the terse name of this language. This is a short, alphanumeric,
|
||||
* lowercase name, eg {@code "java"}. It's used to identify the language
|
||||
* in the ruleset XML, and is also in the package name of the language
|
||||
* module.
|
||||
*
|
||||
* @return The terse name of this Language.
|
||||
* @return The terse name of this language.
|
||||
*/
|
||||
String getTerseName();
|
||||
|
||||
/**
|
||||
* Get the list of file extensions associated with this Language.
|
||||
* Returns the list of file extensions associated with this language.
|
||||
* This list is unmodifiable. Extensions do not have a '.' prefix.
|
||||
*
|
||||
* @return List of file extensions.
|
||||
* @return A list of file extensions.
|
||||
*/
|
||||
List<String> getExtensions();
|
||||
|
||||
/**
|
||||
* Returns whether the given Language handles the given file extension. The
|
||||
* comparison is done ignoring case.
|
||||
* Returns whether this language handles the given file extension.
|
||||
* The comparison is done ignoring case.
|
||||
*
|
||||
* @param extension
|
||||
* A file extension.
|
||||
* @return <code>true</code> if this Language handles this extension,
|
||||
* <code>false</code> otherwise.
|
||||
* @param extensionWithoutDot A file extension (without '.' prefix)
|
||||
*
|
||||
* @return <code>true</code> if this language handles the extension,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
boolean hasExtension(String extension);
|
||||
boolean hasExtension(String extensionWithoutDot);
|
||||
|
||||
/**
|
||||
* Get the RuleChainVisitor implementation class used when visiting the AST
|
||||
@@ -84,30 +93,39 @@ public interface Language extends Comparable<Language> {
|
||||
Class<?> getRuleChainVisitorClass();
|
||||
|
||||
/**
|
||||
* Gets the list of supported LanguageVersion for this Language.
|
||||
* Returns an ordered list of supported versions for this language.
|
||||
*
|
||||
* @return The LanguageVersion for this Language.
|
||||
* @return All supported language versions.
|
||||
*/
|
||||
List<LanguageVersion> getVersions();
|
||||
|
||||
/**
|
||||
* Returns true if a language version with the given {@linkplain LanguageVersion#getVersion() version string}
|
||||
* is registered. Then, {@link #getVersion(String) getVersion} will return a non-null value.
|
||||
*
|
||||
* @param version A version string
|
||||
*
|
||||
* @return True if the version string is known
|
||||
*/
|
||||
boolean hasVersion(String version);
|
||||
|
||||
/**
|
||||
* Get the LanguageVersion for the version string from this Language.
|
||||
* Returns the language version with the given {@linkplain LanguageVersion#getVersion() version string}.
|
||||
* Returns null if no such version exists.
|
||||
*
|
||||
* @param version
|
||||
* The language version string.
|
||||
* @return The corresponding LanguageVersion, <code>null</code> if the
|
||||
* version string is not recognized.
|
||||
* @param version A language version string.
|
||||
*
|
||||
* @return The corresponding LanguageVersion, {@code null} if the
|
||||
* version string is not recognized.
|
||||
*/
|
||||
LanguageVersion getVersion(String version);
|
||||
|
||||
/**
|
||||
* Get the current PMD defined default LanguageVersion for this Language.
|
||||
* Returns the default language version for this language.
|
||||
* This is an arbitrary choice made by the PMD product, and can change
|
||||
* between PMD releases. Every Language has a default version.
|
||||
* between PMD releases. Every language has a default version.
|
||||
*
|
||||
* @return The current default LanguageVersion for this Language.
|
||||
* @return The current default language version for this language.
|
||||
*/
|
||||
LanguageVersion getDefaultVersion();
|
||||
|
||||
|
||||
@@ -18,13 +18,14 @@ import java.util.ServiceLoader;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Created by christoferdutz on 20.09.14.
|
||||
* Provides access to the registered PMD languages. These are found
|
||||
* from the classpath of the {@link ClassLoader} of this class.
|
||||
*/
|
||||
public final class LanguageRegistry {
|
||||
|
||||
private static LanguageRegistry instance = new LanguageRegistry();
|
||||
private static final LanguageRegistry INSTANCE = new LanguageRegistry();
|
||||
|
||||
private Map<String, Language> languages;
|
||||
private final Map<String, Language> languages;
|
||||
|
||||
private LanguageRegistry() {
|
||||
List<Language> languagesList = new ArrayList<>();
|
||||
@@ -70,9 +71,13 @@ public final class LanguageRegistry {
|
||||
*/
|
||||
@Deprecated
|
||||
public static LanguageRegistry getInstance() {
|
||||
return instance;
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of all the known languages. The ordering of this
|
||||
* collection is undefined.
|
||||
*/
|
||||
public static Collection<Language> getLanguages() {
|
||||
// Filter out languages, that are not fully supported by PMD yet.
|
||||
// Those languages should not have a LanguageModule then, but they have it.
|
||||
@@ -95,10 +100,25 @@ public final class LanguageRegistry {
|
||||
return languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a language from its {@linkplain Language#getName() full name}
|
||||
* (eg {@code "Java"}). This is case sensitive.
|
||||
*
|
||||
* @param languageName Language name
|
||||
*
|
||||
* @return A language, or null if the name is unknown
|
||||
*/
|
||||
public static Language getLanguage(String languageName) {
|
||||
return getInstance().languages.get(languageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a "default language" known to the service loader. This
|
||||
* is the Java language if available, otherwise an arbitrary one.
|
||||
* If no languages are loaded, returns null.
|
||||
*
|
||||
* @return A language, or null if the name is unknown
|
||||
*/
|
||||
public static Language getDefaultLanguage() {
|
||||
Language defaultLanguage = getLanguage("Java");
|
||||
if (defaultLanguage == null) {
|
||||
@@ -110,6 +130,14 @@ public final class LanguageRegistry {
|
||||
return defaultLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a language from its {@linkplain Language#getTerseName() terse name}
|
||||
* (eg {@code "java"}). This is case sensitive.
|
||||
*
|
||||
* @param terseName Language terse name
|
||||
*
|
||||
* @return A language, or null if the name is unknown
|
||||
*/
|
||||
public static Language findLanguageByTerseName(String terseName) {
|
||||
for (Language language : getInstance().languages.values()) {
|
||||
if (language.getTerseName().equals(terseName)) {
|
||||
@@ -144,10 +172,15 @@ public final class LanguageRegistry {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<Language> findByExtension(String extension) {
|
||||
/**
|
||||
* Returns all languages that support the given extension.
|
||||
*
|
||||
* @param extensionWithoutDot A file extension (without '.' prefix)
|
||||
*/
|
||||
public static List<Language> findByExtension(String extensionWithoutDot) {
|
||||
List<Language> languages = new ArrayList<>();
|
||||
for (Language language : getInstance().languages.values()) {
|
||||
if (language.hasExtension(extension)) {
|
||||
if (language.hasExtension(extensionWithoutDot)) {
|
||||
languages.add(language);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,30 @@
|
||||
|
||||
package net.sourceforge.pmd.lang;
|
||||
|
||||
import net.sourceforge.pmd.Rule;
|
||||
import net.sourceforge.pmd.annotation.InternalApi;
|
||||
|
||||
/**
|
||||
* Created by christoferdutz on 21.09.14.
|
||||
* Represents a version of a {@link Language}. Language instances provide
|
||||
* a list of supported versions ({@link Language#getVersions()}). Individual
|
||||
* versions can be retrieved from their version number ({@link Language#getVersion(String)}).
|
||||
*
|
||||
* <p>Versions are used to limit some rules to operate on only a version range.
|
||||
* For instance, a rule that suggests eliding local variable types in Java
|
||||
* (replacing them with {@code var}) makes no sense if the codebase is not
|
||||
* using Java 10 or later. This is determined by {@link Rule#getMinimumLanguageVersion()}
|
||||
* and {@link Rule#getMaximumLanguageVersion()}. These should be set in the
|
||||
* ruleset XML (they're attributes of the {@code <rule>} element), and not
|
||||
* overridden.
|
||||
*
|
||||
* <p>Example usage:
|
||||
* <pre>
|
||||
* Language javaLanguage = LanguageRegistry.{@link LanguageRegistry#getLanguage(String) getLanguage}("Java");
|
||||
* LanguageVersion java11 = javaLanguage.{@link Language#getVersion(String) getVersion}("11");
|
||||
* LanguageVersionHandler handler = java11.getLanguageVersionHandler();
|
||||
* Parser parser = handler.getParser(handler.getDefaultParserOptions());
|
||||
* // use parser
|
||||
* </pre>
|
||||
*/
|
||||
public class LanguageVersion implements Comparable<LanguageVersion> {
|
||||
|
||||
@@ -13,27 +35,44 @@ public class LanguageVersion implements Comparable<LanguageVersion> {
|
||||
private final String version;
|
||||
private final LanguageVersionHandler languageVersionHandler;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link Language#getVersion(String)}. This is only
|
||||
* supposed to be used when initializing a {@link Language} instance.
|
||||
*/
|
||||
@Deprecated
|
||||
@InternalApi
|
||||
public LanguageVersion(Language language, String version, LanguageVersionHandler languageVersionHandler) {
|
||||
this.language = language;
|
||||
this.version = version;
|
||||
this.languageVersionHandler = languageVersionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language that owns this version.
|
||||
*/
|
||||
public Language getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version string. This is usually a version number, e.g.
|
||||
* {@code "1.7"} or {@code "11"}. This is used by {@link Language#getVersion(String)}.
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link LanguageVersionHandler}, which provides access
|
||||
* to version-specific services, like the parser.
|
||||
*/
|
||||
public LanguageVersionHandler getLanguageVersionHandler() {
|
||||
return languageVersionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this LanguageVersion. This is Language name appended with
|
||||
* the LanguageVersion version if not an empty String.
|
||||
* Returns the name of this language version. This is the version string
|
||||
* prefixed with the {@linkplain Language#getName() language name}.
|
||||
*
|
||||
* @return The name of this LanguageVersion.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -47,21 +48,19 @@ public class PMDCoverageTest {
|
||||
* @param commandLine
|
||||
*/
|
||||
private void runPmd(String commandLine) {
|
||||
String[] args;
|
||||
args = commandLine.split("\\s");
|
||||
String[] args = commandLine.split("\\s");
|
||||
|
||||
try {
|
||||
File f = folder.newFile();
|
||||
int n = args.length;
|
||||
String[] a = new String[n + 2 + 2];
|
||||
System.arraycopy(args, 0, a, 0, n);
|
||||
a[n] = "-reportfile";
|
||||
a[n + 1] = f.getAbsolutePath();
|
||||
a[n + 2] = "-threads";
|
||||
a[n + 3] = String.valueOf(Runtime.getRuntime().availableProcessors());
|
||||
args = a;
|
||||
args = ArrayUtils.addAll(
|
||||
args,
|
||||
"-reportfile",
|
||||
f.getAbsolutePath(),
|
||||
"-threads",
|
||||
String.valueOf(Runtime.getRuntime().availableProcessors())
|
||||
);
|
||||
|
||||
PMD.run(args);
|
||||
PMD.runPmd(args);
|
||||
|
||||
assertEquals("Nothing should be output to stdout", 0, output.getLog().length());
|
||||
|
||||
|
||||
Reference in new issue
Block a user