• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmd / pmd / 670

16 Jul 2026 02:41PM UTC coverage: 79.205% (+0.002%) from 79.203%
670

push

github

web-flow
[core] Fix #4953: Deprecate PMDConfiguration#getClassLoader (#6845)

* [core] Fix #4953: Deprecate PMDConfiguration#getClassLoader

PMDConfiguration now stores the given raw auxClasspath as string.
For backwards compatibility, a classloader can still be set externally
when using the programmatic API.
But usually (e.g. via CLI), the auxClasspath is set now via the
JvmLanguagePropertyBundle only,
which creates the actual classloader only when requested.
This means, the classloader is not created anymore eagerly by
PMDConfiguration.

The given auxClasspath is checked early for validity.
All files/directories must exist.

* [core] fix #4952: Document PMDConfiguration classLoader closing

* Add TODO for #6841

* Fix test auxClasspathWithRelativeFile() under Windows

* fix: when verifying auxclasspath allow not existing directory entries

At least maven-pmd-plugin might provide a directory, which doesn't
exist, e.g. "target/test-classes". As we don't check the auxclasspath
for completeness (which we can't anyway at this point), there is
no difference between a non-existing directory or an empty
directory or an incomplete directory. The result is the same:
the provided auxclasspath is incomplete.
The reason for the empty/non-existing directory might be, that the
project being analyzed was not compiled yet - but we don't know.

Later on, when trying to resolve classes from the auxclasspath
and we don't find classes, we can report this problem, as we
know for sure, that the auxclasspath is incomplete (see #5064).

For now, we only log a warning for each suspicious auxClasspath entry.

* Revert formatting changes in release notes

* Fix tests

* Fix unused import

* refactor: Use AuxClasspathUtil in PMDConfiguration

19235 of 25221 branches covered (76.27%)

Branch coverage included in aggregate %.

42 of 56 new or added lines in 2 files covered. (75.0%)

41684 of 51692 relevant lines covered (80.64%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.95
/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd;
6

7
import java.io.File;
8
import java.io.IOException;
9
import java.io.UncheckedIOException;
10
import java.net.URI;
11
import java.net.URLClassLoader;
12
import java.nio.charset.Charset;
13
import java.nio.file.Files;
14
import java.nio.file.Path;
15
import java.util.ArrayList;
16
import java.util.List;
17
import java.util.Objects;
18
import java.util.Properties;
19
import java.util.stream.Collectors;
20
import java.util.stream.Stream;
21

22
import org.checkerframework.checker.nullness.qual.NonNull;
23
import org.slf4j.LoggerFactory;
24

25
import net.sourceforge.pmd.cache.internal.AnalysisCache;
26
import net.sourceforge.pmd.cache.internal.FileAnalysisCache;
27
import net.sourceforge.pmd.cache.internal.NoopAnalysisCache;
28
import net.sourceforge.pmd.internal.util.IOUtil;
29
import net.sourceforge.pmd.lang.Language;
30
import net.sourceforge.pmd.lang.LanguageRegistry;
31
import net.sourceforge.pmd.lang.LanguageVersion;
32
import net.sourceforge.pmd.lang.PmdCapableLanguage;
33
import net.sourceforge.pmd.lang.rule.RulePriority;
34
import net.sourceforge.pmd.lang.rule.RuleSetLoader;
35
import net.sourceforge.pmd.renderers.Renderer;
36
import net.sourceforge.pmd.renderers.RendererFactory;
37
import net.sourceforge.pmd.util.AssertionUtil;
38
import net.sourceforge.pmd.util.internal.AuxClasspathUtil;
39
import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter;
40

41
/**
42
 * This class contains the details for the runtime configuration of a
43
 * PMD run. Once configured, use {@link PmdAnalysis#create(PMDConfiguration)}
44
 * in a try-with-resources to execute the analysis (see {@link PmdAnalysis}).
45
 *
46
 * <h2>Rulesets</h2>
47
 *
48
 * <ul>
49
 * <li>You can configure paths to the rulesets to use with {@link #addRuleSet(String)}.
50
 * These can be file paths or classpath resources.</li>
51
 * <li>Use {@link #setMinimumPriority(RulePriority)} to control the minimum priority a
52
 * rule must have to be included. Defaults to the lowest priority, ie all rules are loaded.</li>
53
 * </ul>
54
 *
55
 * <h2>Source files</h2>
56
 *
57
 * <ul>
58
 * <li>The default encoding of source files is the system default as
59
 * returned by <code>System.getProperty("file.encoding")</code>.
60
 * You can set it with {@link #setSourceEncoding(Charset)}.</li>
61
 * <li>The source files to analyze can be given in many ways. See
62
 * {@link #addInputPath(Path)} {@link #setInputFilePath(Path)}, {@link #setInputUri(URI)}.
63
 * <li>Files are assigned a language based on their name. The language
64
 * version of languages can be given with
65
 * {@link #setDefaultLanguageVersion(LanguageVersion)}.
66
 * The default language assignment can be overridden with
67
 * {@link #setForceLanguageVersion(LanguageVersion)}.</li>
68
 * </ul>
69
 *
70
 * <h2>Rendering</h2>
71
 *
72
 * <ul>
73
 * <li>The renderer format to use for Reports. {@link #getReportFormat()}</li>
74
 * <li>The file to which the Report should render. {@link #getReportFilePath()}</li>
75
 * <li>Configure the root paths that are used to relativize file names in reports via {@link #addRelativizeRoot(Path)}.
76
 * This enables to get short names in reports.</li>
77
 * <li>The initialization properties to use when creating a Renderer instance.
78
 * {@link #getReportProperties()}</li>
79
 * <li>An indicator of whether to show suppressed Rule violations in Reports.
80
 * {@link #isShowSuppressedViolations()}</li>
81
 * </ul>
82
 *
83
 * <h2>Language configuration</h2>
84
 * <ul>
85
 * <li>Use {@link #setSuppressMarker(String)} to change the comment marker for suppression comments. Defaults to {@value #DEFAULT_SUPPRESS_MARKER}.</li>
86
 * <li>See {@link #setClassLoader(ClassLoader)} and {@link #prependAuxClasspath(String)} for
87
 *  information for how to configure classpath for Java analysis.</li>
88
 * <li>You can set additional language properties with {@link #getLanguageProperties(Language)}</li>
89
 * </ul>
90
 *
91
 * <h2>Miscellaneous</h2>
92
 * <ul>
93
 * <li>Use {@link #setThreads(int)} to control the parallelism of the analysis. Defaults
94
 * one thread per available processor. {@link #getThreads()}</li>
95
 * </ul>
96
 */
97
public class PMDConfiguration extends AbstractConfiguration {
98
    private static final LanguageRegistry DEFAULT_REGISTRY = LanguageRegistry.PMD;
1✔
99

100
    /** The default suppress marker string. */
101
    public static final String DEFAULT_SUPPRESS_MARKER = "NOPMD";
102

103
    // General behavior options
104
    private String suppressMarker = DEFAULT_SUPPRESS_MARKER;
1✔
105
    private int threads = Runtime.getRuntime().availableProcessors();
1✔
106
    /**
107
     * @deprecated Since 7.27.0. This field is only used for fallback behavior.
108
     */
109
    @Deprecated
1✔
110
    private ClassLoader classLoader = null;
111
    private String auxClasspath = null;
1✔
112

113
    // Rule and source file options
114
    private List<String> ruleSets = new ArrayList<>();
1✔
115
    private RulePriority minimumPriority = RulePriority.LOW;
1✔
116

117
    // Reporting options
118
    private String reportFormat;
119
    private Properties reportProperties = new Properties();
1✔
120
    private boolean showSuppressedViolations = false;
1✔
121

122
    private AnalysisCache analysisCache = new NoopAnalysisCache();
1✔
123
    private boolean ignoreIncrementalAnalysis;
124

125
    public PMDConfiguration() {
126
        this(DEFAULT_REGISTRY);
1✔
127
    }
1✔
128

129
    public PMDConfiguration(@NonNull LanguageRegistry languageRegistry) {
130
        super(languageRegistry, new SimpleMessageReporter(LoggerFactory.getLogger(PmdAnalysis.class)));
1✔
131
    }
1✔
132

133
    /**
134
     * Get the suppress marker. This is the source level marker used to indicate
135
     * a RuleViolation should be suppressed.
136
     *
137
     * @return The suppress marker.
138
     */
139
    public String getSuppressMarker() {
140
        return suppressMarker;
1✔
141
    }
142

143
    /**
144
     * Set the suppress marker.
145
     *
146
     * @param suppressMarker
147
     *            The suppress marker to use.
148
     */
149
    public void setSuppressMarker(String suppressMarker) {
150
        Objects.requireNonNull(suppressMarker, "Suppress marker was null");
1✔
151
        this.suppressMarker = suppressMarker;
1✔
152
    }
1✔
153

154
    /**
155
     * Get the number of threads to use when processing Rules.
156
     *
157
     * @return The number of threads.
158
     */
159
    public int getThreads() {
160
        return threads;
1✔
161
    }
162

163
    /**
164
     * Set the number of threads to use when processing Rules.
165
     *
166
     * @param threads
167
     *            The number of threads.
168
     */
169
    public void setThreads(int threads) {
170
        this.threads = threads;
1✔
171
    }
1✔
172

173
    /**
174
     * Get the ClassLoader being used by PMD when analyzing code, e.g. Java.
175
     *
176
     * <p>Since 7.27.0, this method will only return the classloader, that has been
177
     * set explicitly via {@link #setClassLoader(ClassLoader)}. Instead of setting a classloader,
178
     * the auxClasspath should be configured via {@link #setAuxClasspath(String)} or {@link #prependAuxClasspath(String)}.</p>
179
     *
180
     * <p>See also the notes on {@link #setClassLoader(ClassLoader)} regarding closing and supported types.</p>
181
     *
182
     * @return The ClassLoader being used
183
     * @deprecated Since 7.27.0. Use {@link #getAuxClasspath()} instead.
184
     */
185
    @Deprecated
186
    public ClassLoader getClassLoader() {
187
        if (classLoader == null && auxClasspath == null) {
1✔
188
            // preserve old behavior for default classloader
189
            return PMDConfiguration.class.getClassLoader();
1✔
190
        }
191
        return classLoader;
1✔
192
    }
193

194
    /**
195
     * Set the ClassLoader being used by PMD when analyzing code, e.g. Java. Setting a
196
     * value of <code>null</code> will cause the default ClassLoader to be used.
197
     *
198
     * <p>Since 7.27.0, a classloader should not be set anymore. Instead, the auxClasspath should be configured
199
     * via {@link #setAuxClasspath(String)} or {@link #prependAuxClasspath(String)}. For
200
     * backwards compatibility, if setting a classloader here, it will still be used.</p>
201
     *
202
     * <p>Note 1: The classloader might keep open file references to jar files if it is not closed.
203
     * A {@link java.net.URLClassLoader} is closeable and any given classloader that is {@link java.io.Closeable}
204
     * will be closed, when PMD is called via {@link PmdAnalysis}. In other cases, e.g. not using PmdAnalysis,
205
     * you need to close the classloader yourself.</p>
206
     *
207
     * <p>Note 2: Only subtypes of {@link java.net.URLClassLoader} are compatible with PMD, as for the
208
     * <a href="https://docs.pmd-code.org/latest/pmd_userdocs_incremental_analysis.html">incremental analysis</a>
209
     * we need to figure out the actual URLs of the classpath to check the validity of our cache.</p>
210
     *
211
     * @param classLoader
212
     *            The ClassLoader to use
213
     * @deprecated Since 7.27.0. Use {@link #prependAuxClasspath(String)} or {@link #setAuxClasspath(String)} instead.
214
     */
215
    @Deprecated
216
    public void setClassLoader(ClassLoader classLoader) {
217
        if (auxClasspath != null) {
1!
NEW
218
            throw new IllegalStateException("Can't mix setClassLoader with setAuxClasspath or prependAuxClasspath!");
×
219
        }
220
        if (classLoader != null && !(classLoader instanceof URLClassLoader)) {
1!
NEW
221
            getReporter().warn("Unsupported classloader for auxClasspath detected: {0}. "
×
NEW
222
                    + "Only {1} is supported.", classLoader.getClass().getName(), URLClassLoader.class.getName());
×
223
        }
224
        this.classLoader = classLoader;
1✔
225
    }
1✔
226

227
    /**
228
     * Prepend the specified classpath like string to the currently set auxClasspath of
229
     * the configuration.
230
     *
231
     * <p>Use {@link #setAuxClasspath(String)} if you don't want any fallbacks (neither to PMD's runtime
232
     * classpath nor to the current JVM runtime) and only access classes on the given auxClasspath.</p>
233
     *
234
     * <p>Specify the JVM's platform classpath yourself explicitly (e.g. adding {@code jrt-fs.jar})
235
     * in order to not fall back to the current JVM runtime.</p>
236
     *
237
     * <p>If the classpath String looks like a URL to a file (i.e. starts with
238
     * <code>file://</code>) the file will be read with each line representing
239
     * an entry on the classpath.</p>
240
     *
241
     * <p>You can specify multiple class paths separated by `:` on Unix-systems or `;` under Windows.
242
     * See {@link File#pathSeparator}.
243
     *
244
     * @param classpath The additional classpath entries to be prepended.
245
     *
246
     * @throws IllegalArgumentException if the given classpath is invalid (e.g. does not exist)
247
     * @see PMDConfiguration#setAuxClasspath(String)
248
     */
249
    public void prependAuxClasspath(String classpath) {
250
        if (classpath == null) {
1!
NEW
251
            return;
×
252
        }
253
        if (classLoader != null) {
1!
NEW
254
            throw new IllegalStateException("Can't mix setClasspath with prependAuxClasspath!");
×
255
        }
256
        verifyAuxClasspath(classpath); // throws IllegalArgumentException...
1✔
257

258
        if (auxClasspath == null) {
1!
259
            auxClasspath = classpath;
1✔
260
        } else {
NEW
261
            auxClasspath = classpath + File.pathSeparator + auxClasspath;
×
262
        }
263
    }
1✔
264

265
    /**
266
     * Checks whether any referenced file on the classpath exists. File URLs are replaced by their
267
     * content and each referenced file in the classpath-file must exist. If the classpath entry
268
     * references a directory (an entry not ending with .jar) and the directory does not exist, it is
269
     * ignored - only files must exist.
270
     *
271
     * <p>Valid classpath formats (under Unix):
272
     * <ul>
273
     *     <li>/absolute/file.jar:relative/file.jar:/absolute/directory/with/classes</li>
274
     *     <li>file:relative/classpath.txt</li>
275
     *     <li>file:/absolute/classpath.txt</li>
276
     * </ul>
277
     * </p>
278
     *
279
     * @param classpath
280
     * @throws IllegalArgumentException if the given classpath is invalid (e.g. does not exist).
281
     */
282
    private void verifyAuxClasspath(String classpath) {
283
        if (classpath == null) {
1✔
284
            return;
1✔
285
        }
286

287
        List<Path> expandedClasspath = AuxClasspathUtil.expandClasspath(classpath);
1✔
288
        List<Path> notExistingFiles = expandedClasspath.stream()
1✔
289
                .filter(path -> {
1✔
290
                    boolean isJarFile = "jar".equalsIgnoreCase(IOUtil.getFilenameExtension(path.toString()));
1✔
291
                    if (isJarFile && !Files.exists(path)) {
1✔
292
                        return true;
1✔
293
                    } else if (!isJarFile) {
1✔
294
                        // might be a directory
295
                        if (!Files.exists(path)) {
1✔
296
                            getReporter().warn("Suspicious auxClasspath entry: {0} does not exist",
1✔
297
                                    path.toString());
1✔
298
                        } else if (Files.isDirectory(path)) {
1!
299
                            try (Stream<Path> dir = Files.list(path)) {
1✔
300
                                if (!dir.findAny().isPresent()) {
1✔
301
                                    getReporter().warn("Suspicious auxClasspath entry: directory {0} is empty",
1✔
302
                                            path.toString());
1✔
303
                                }
NEW
304
                            } catch (IOException e) {
×
NEW
305
                                throw new UncheckedIOException(e);
×
306
                            }
1✔
307
                        }
308
                    }
309
                    return false;
1✔
310
                })
311
                .collect(Collectors.toList());
1✔
312

313
        if (!notExistingFiles.isEmpty()) {
1✔
314
            throw new IllegalArgumentException("Invalid classpath - not existing files: " + notExistingFiles);
1✔
315
        }
316
    }
1✔
317

318
    /**
319
     * Uses the specified classpath like string as the auxClasspath of
320
     * the configuration.
321
     *
322
     * <p>If the classpath String looks like a URL to a file (i.e. starts with
323
     * <code>file://</code>) the file will be read with each line representing
324
     * an entry on the classpath.</p>
325
     *
326
     * <p>You can specify multiple class paths separated by `:` on Unix-systems or `;` under Windows.
327
     * See {@link File#pathSeparator}.
328
     *
329
     * @param classpath The classpath entries to be used.
330
     *
331
     * @throws IllegalArgumentException if the given classpath is invalid (e.g. does not exist)
332
     * @see PMDConfiguration#prependAuxClasspath(String)
333
     * @since 7.27.0
334
     */
335
    public void setAuxClasspath(String classpath) {
336
        if (classLoader != null) {
1!
NEW
337
            throw new IllegalStateException("Can't mix setClasspath with setAuxClasspath!");
×
338
        }
339
        verifyAuxClasspath(classpath);
1✔
340
        auxClasspath = classpath;
1✔
341
    }
1✔
342

343
    /**
344
     * Gets the currently set auxClasspath.
345
     *
346
     * @return the configured auxClasspath. Might be {@code null}.
347
     * @see #setAuxClasspath(String)
348
     * @see #prependAuxClasspath(String)
349
     * @since 7.27.0
350
     */
351
    public String getAuxClasspath() {
352
        if (classLoader != null) {
1!
NEW
353
            throw new IllegalStateException("Can't mix setClasspath with getAuxClasspath!");
×
354
        }
355
        return auxClasspath;
1✔
356
    }
357

358
    /**
359
     * Returns the list of ruleset URIs.
360
     *
361
     * @see RuleSetLoader#loadFromResource(String)
362
     */
363
    public @NonNull List<@NonNull String> getRuleSetPaths() {
364
        return ruleSets;
1✔
365
    }
366

367
    /**
368
     * Sets the list of ruleset paths to load when starting the analysis.
369
     *
370
     * @param ruleSetPaths A list of ruleset paths, understandable by {@link RuleSetLoader#loadFromResource(String)}.
371
     *
372
     * @throws NullPointerException If the parameter is null
373
     */
374
    public void setRuleSets(@NonNull List<@NonNull String> ruleSetPaths) {
375
        AssertionUtil.requireParamNotNull("ruleSetPaths", ruleSetPaths);
1✔
376
        AssertionUtil.requireContainsNoNullValue("ruleSetPaths", ruleSetPaths);
1✔
377
        this.ruleSets = new ArrayList<>(ruleSetPaths);
1✔
378
    }
1✔
379

380
    /**
381
     * Add a new ruleset paths to load when starting the analysis.
382
     * This list is initially empty.
383
     *
384
     * @param rulesetPath A ruleset path, understandable by {@link RuleSetLoader#loadFromResource(String)}.
385
     *
386
     * @throws NullPointerException If the parameter is null
387
     */
388
    public void addRuleSet(@NonNull String rulesetPath) {
389
        AssertionUtil.requireParamNotNull("rulesetPath", rulesetPath);
1✔
390
        this.ruleSets.add(rulesetPath);
1✔
391
    }
1✔
392

393
    /**
394
     * Get the minimum priority threshold when loading Rules from RuleSets.
395
     *
396
     * @return The minimum priority threshold.
397
     */
398
    public RulePriority getMinimumPriority() {
399
        return minimumPriority;
1✔
400
    }
401

402
    /**
403
     * Set the minimum priority threshold when loading Rules from RuleSets.
404
     *
405
     * @param minimumPriority
406
     *            The minimum priority.
407
     */
408
    public void setMinimumPriority(RulePriority minimumPriority) {
409
        this.minimumPriority = minimumPriority;
1✔
410
    }
1✔
411

412

413
    /**
414
     * Create a Renderer instance based upon the configured reporting options.
415
     * No writer is created.
416
     *
417
     * @return renderer
418
     */
419
    public Renderer createRenderer() {
420
        return createRenderer(false);
1✔
421
    }
422

423
    /**
424
     * Create a Renderer instance based upon the configured reporting options.
425
     * If withReportWriter then we'll configure it with a writer for the
426
     * reportFile specified.
427
     *
428
     * @param withReportWriter
429
     *            whether to configure a writer or not
430
     * @return A Renderer instance.
431
     */
432
    public Renderer createRenderer(boolean withReportWriter) {
433
        Renderer renderer = RendererFactory.createRenderer(reportFormat, reportProperties);
1✔
434
        renderer.setShowSuppressedViolations(showSuppressedViolations);
1✔
435
        if (withReportWriter) {
1✔
436
            renderer.setReportFile(getReportFilePath() != null ? getReportFilePath().toString() : null);
1!
437
        }
438
        return renderer;
1✔
439
    }
440

441
    /**
442
     * Get the report format.
443
     *
444
     * @return The report format.
445
     */
446
    public String getReportFormat() {
447
        return reportFormat;
1✔
448
    }
449

450
    /**
451
     * Set the report format. This should be a name of a Renderer.
452
     *
453
     * @param reportFormat
454
     *            The report format.
455
     *
456
     * @see Renderer
457
     */
458
    public void setReportFormat(String reportFormat) {
459
        this.reportFormat = reportFormat;
1✔
460
    }
1✔
461

462
    /**
463
     * Get whether the report should show suppressed violations.
464
     *
465
     * @return <code>true</code> if showing suppressed violations,
466
     *         <code>false</code> otherwise.
467
     */
468
    public boolean isShowSuppressedViolations() {
469
        return showSuppressedViolations;
1✔
470
    }
471

472
    /**
473
     * Set whether the report should show suppressed violations.
474
     *
475
     * @param showSuppressedViolations
476
     *            <code>true</code> if showing suppressed violations,
477
     *            <code>false</code> otherwise.
478
     */
479
    public void setShowSuppressedViolations(boolean showSuppressedViolations) {
480
        this.showSuppressedViolations = showSuppressedViolations;
1✔
481
    }
1✔
482

483
    /**
484
     * Get the Report properties. These are used to create the Renderer.
485
     *
486
     * @return The report properties.
487
     */
488
    public Properties getReportProperties() {
489
        return reportProperties;
1✔
490
    }
491

492
    /**
493
     * Set the Report properties. These are used to create the Renderer.
494
     *
495
     * @param reportProperties
496
     *            The Report properties to set.
497
     */
498
    public void setReportProperties(Properties reportProperties) {
499
        this.reportProperties = reportProperties;
1✔
500
    }
1✔
501

502
    /**
503
     * Retrieves the currently used analysis cache. Will never be null.
504
     *
505
     * @return The currently used analysis cache. Never null.
506
     *
507
     * @internalApi None of this is published API, and compatibility can be broken anytime! Use this only at your own risk.
508
     */
509
    AnalysisCache getAnalysisCache() {
510
        // Make sure we are not null
511
        if (analysisCache == null || isIgnoreIncrementalAnalysis() && !(analysisCache instanceof NoopAnalysisCache)) {
1!
512
            // sets a noop cache
513
            setAnalysisCache(new NoopAnalysisCache());
1✔
514
        }
515

516
        return analysisCache;
1✔
517
    }
518

519
    /**
520
     * Sets the analysis cache to be used. Setting a
521
     * value of {@code null} will cause a Noop AnalysisCache to be used.
522
     * If incremental analysis was explicitly disabled ({@link #isIgnoreIncrementalAnalysis()}),
523
     * then this method is a noop.
524
     *
525
     * @param cache The analysis cache to be used.
526
     *
527
     * @internalApi None of this is published API, and compatibility can be broken anytime! Use this only at your own risk.
528
     * Use {@link #setAnalysisCacheLocation(String)} to configure a cache.
529
     */
530
    void setAnalysisCache(final AnalysisCache cache) {
531
        // the doc says it's a noop if incremental analysis was disabled,
532
        // but it's actually the getter that enforces that
533
        this.analysisCache = cache == null ? new NoopAnalysisCache() : cache;
1✔
534
    }
1✔
535

536
    /**
537
     * Sets the location of the analysis cache to be used. This will automatically configure
538
     * and appropriate AnalysisCache implementation. Setting a
539
     * value of {@code null} will cause a Noop AnalysisCache to be used.
540
     * If incremental analysis was explicitly disabled ({@link #isIgnoreIncrementalAnalysis()}),
541
     * then this method is a noop.
542
     *
543
     * @param cacheLocation The location of the analysis cache to be used. Use {@code null}
544
     *                      to disable the cache.
545
     */
546
    public void setAnalysisCacheLocation(final String cacheLocation) {
547
        setAnalysisCache(cacheLocation == null
1✔
548
                         ? new NoopAnalysisCache()
1✔
549
                         : new FileAnalysisCache(new File(cacheLocation)));
1✔
550
    }
1✔
551

552

553
    /**
554
     * Sets whether the user has explicitly disabled incremental analysis or not.
555
     * If so, incremental analysis is not used, and all suggestions to use it are
556
     * disabled. The analysis cached location is ignored, even if it's specified.
557
     *
558
     * @param noCache Whether to ignore incremental analysis or not
559
     */
560
    public void setIgnoreIncrementalAnalysis(boolean noCache) {
561
        // see #getAnalysisCache for the implementation.
562
        this.ignoreIncrementalAnalysis = noCache;
1✔
563
    }
1✔
564

565

566
    /**
567
     * Returns whether incremental analysis was explicitly disabled by the user
568
     * or not.
569
     *
570
     * @return {@code true} if incremental analysis is explicitly disabled
571
     */
572
    public boolean isIgnoreIncrementalAnalysis() {
573
        return ignoreIncrementalAnalysis;
1✔
574
    }
575

576
    @Override
577
    protected void checkLanguageIsAcceptable(Language lang) throws UnsupportedOperationException {
578
        if (!(lang instanceof PmdCapableLanguage)) {
1✔
579
            throw new UnsupportedOperationException("Language " + lang.getId() + " does not support analysis with PMD and cannot be used in a PMDConfiguration. "
1✔
580
                + "You may be able to use it with CPD though.");
581
        }
582
    }
1✔
583
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc