All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sourceforge.pmd.AbstractConfiguration Maven / Gradle / Ivy

Go to download

PMD is an extensible multilanguage static code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth. It's mainly concerned with Java and Apex, but supports 16 other languages. It comes with 400+ built-in rules. It can be extended with custom rules. It uses JavaCC and Antlr to parse source files into abstract syntax trees (AST) and runs rules against them to find violations. Rules can be written in Java or using a XPath query. Currently, PMD supports Java, JavaScript, Salesforce.com Apex and Visualforce, Kotlin, Swift, Modelica, PLSQL, Apache Velocity, JSP, WSDL, Maven POM, HTML, XML and XSL. Scala is supported, but there are currently no Scala rules available. Additionally, it includes CPD, the copy-paste-detector. CPD finds duplicated code in Coco, C/C++, C#, Dart, Fortran, Gherkin, Go, Groovy, HTML, Java, JavaScript, JSP, Julia, Kotlin, Lua, Matlab, Modelica, Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex and Visualforce, Scala, Swift, T-SQL, Typescript, Apache Velocity, WSDL, XML and XSL.

There is a newer version: 7.5.0-metrics
Show newest version
/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */

package net.sourceforge.pmd;

import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
import net.sourceforge.pmd.util.AssertionUtil;
import net.sourceforge.pmd.util.log.PmdReporter;

/**
 * Base configuration class for both PMD and CPD.
 *
 * @author Brian Remedios
 */
public abstract class AbstractConfiguration {

    private final List relativizeRoots = new ArrayList<>();
    private URI inputUri;
    private Charset sourceEncoding = Charset.forName(System.getProperty("file.encoding"));
    private final Map langProperties = new HashMap<>();
    private final LanguageRegistry langRegistry;
    private PmdReporter reporter;
    private final LanguageVersionDiscoverer languageVersionDiscoverer;
    private LanguageVersion forceLanguageVersion;
    private @NonNull List inputPaths = new ArrayList<>();
    private Path inputFilePath;
    private Path ignoreFilePath;
    private List excludes = new ArrayList<>();
    private boolean collectRecursive = true;
    private boolean failOnViolation = true;
    private boolean failOnError = true;


    protected AbstractConfiguration(LanguageRegistry languageRegistry, PmdReporter messageReporter) {
        this.langRegistry = Objects.requireNonNull(languageRegistry);
        this.languageVersionDiscoverer = new LanguageVersionDiscoverer(languageRegistry);
        this.reporter = Objects.requireNonNull(messageReporter);
    }

    /**
     * Get the character encoding of source files.
     *
     * @return The character encoding.
     */
    public Charset getSourceEncoding() {
        return sourceEncoding;
    }

    /**
     * Set the character encoding of source files.
     *
     * @param sourceEncoding
     *            The character encoding.
     */
    public void setSourceEncoding(Charset sourceEncoding) {
        this.sourceEncoding = Objects.requireNonNull(sourceEncoding);
    }

    /**
     * Returns a mutable bundle of language properties that are associated
     * to the given language (always the same for a given language).
     *
     * @param language A language, which must be registered
     */
    public @NonNull LanguagePropertyBundle getLanguageProperties(Language language) {
        checkLanguageIsRegistered(language);
        return langProperties.computeIfAbsent(language, Language::newPropertyBundle);
    }

    void checkLanguageIsRegistered(Language language) {
        if (!langRegistry.getLanguages().contains(language)) {
            throw new IllegalArgumentException(
                "Language '" + language.getId() + "' is not registered in " + getLanguageRegistry());
        }
        checkLanguageIsAcceptable(language);
    }

    public LanguageRegistry getLanguageRegistry() {
        return langRegistry;
    }

    /**
     * Returns the message reporter that is to be used while running
     * the analysis.
     */
    public @NonNull PmdReporter getReporter() {
        return reporter;
    }

    /**
     * Sets the message reporter that is to be used while running
     * the analysis.
     *
     * @param reporter A non-null message reporter
     */
    public void setReporter(@NonNull PmdReporter reporter) {
        AssertionUtil.requireParamNotNull("reporter", reporter);
        this.reporter = reporter;
    }

    /**
     * Get the LanguageVersionDiscoverer, used to determine the LanguageVersion
     * of a source file.
     *
     * @return The LanguageVersionDiscoverer.
     */
    public LanguageVersionDiscoverer getLanguageVersionDiscoverer() {
        return languageVersionDiscoverer;
    }

    /**
     * Get the LanguageVersion specified by the force-language parameter. This overrides detection based on file
     * extensions
     *
     * @return The LanguageVersion.
     */
    public LanguageVersion getForceLanguageVersion() {
        return forceLanguageVersion;
    }

    /**
     * Is the force-language parameter set to anything?
     *
     * @return true if ${@link #getForceLanguageVersion()} is not null
     */
    public boolean isForceLanguageVersion() {
        return forceLanguageVersion != null;
    }

    /**
     * Set the LanguageVersion specified by the force-language parameter. This overrides detection based on file
     * extensions
     *
     * @param forceLanguageVersion the language version
     */
    public void setForceLanguageVersion(@Nullable LanguageVersion forceLanguageVersion) {
        if (forceLanguageVersion != null) {
            checkLanguageIsRegistered(forceLanguageVersion.getLanguage());
        }
        this.forceLanguageVersion = forceLanguageVersion;
        languageVersionDiscoverer.setForcedVersion(forceLanguageVersion);
    }

    /**
     * Make it so that the only extensions that are considered are those
     * of the given language. This is different from {@link #setForceLanguageVersion(LanguageVersion)}
     * because that one will assign the given language version to all files
     * irrespective of extension. This method, on the other hand, will
     * ignore files that do not match the given language.
     *
     * @param lang A language
     */
    public void setOnlyRecognizeLanguage(Language lang) {
        AssertionUtil.requireParamNotNull("language", lang);
        checkLanguageIsRegistered(lang);
        this.languageVersionDiscoverer.onlyRecognizeLanguages(LanguageRegistry.singleton(lang));
    }

    /**
     * Set the given LanguageVersion as the current default for it's Language.
     *
     * @param languageVersion the LanguageVersion
     */
    public void setDefaultLanguageVersion(LanguageVersion languageVersion) {
        Objects.requireNonNull(languageVersion);
        checkLanguageIsRegistered(languageVersion.getLanguage());

        languageVersionDiscoverer.setDefaultLanguageVersion(languageVersion);
        getLanguageProperties(languageVersion.getLanguage()).setLanguageVersion(languageVersion.getVersion());
    }

    /**
     * Set the given LanguageVersions as the current default for their
     * Languages.
     *
     * @param languageVersions
     *            The LanguageVersions.
     */
    public void setDefaultLanguageVersions(List languageVersions) {
        for (LanguageVersion languageVersion : languageVersions) {
            setDefaultLanguageVersion(languageVersion);
        }
    }

    /**
     * Check that it is correct to use the given language with this configuration.
     *
     * @throws UnsupportedOperationException if the language isn't supported.
     */
    protected void checkLanguageIsAcceptable(Language lang) throws UnsupportedOperationException {
        // do nothing
    }

    /**
     * Get the LanguageVersion of the source file with given name. This depends
     * on the fileName extension, and the java version.
     * 

* For compatibility with older code that does not always pass in a correct * filename, unrecognized files are assumed to be java files. *

* * @param fileName * Name of the file, can be absolute, or simple. * @return the LanguageVersion */ // FUTURE Delete this? I can't think of a good reason to keep it around. // Failure to determine the LanguageVersion for a file should be a hard // error, or simply cause the file to be skipped? public @Nullable LanguageVersion getLanguageVersionOfFile(String fileName) { LanguageVersion forcedVersion = getForceLanguageVersion(); if (forcedVersion != null) { // use force language if given return forcedVersion; } // otherwise determine by file extension return languageVersionDiscoverer.getDefaultLanguageVersionForFile(fileName); } /** * Set the path used to shorten paths output in the report. * The path does not need to exist. If it exists, it must point * to a directory and not a file. See {@link #getRelativizeRoots()} * for the interpretation. * *

If several paths are added, the shortest paths possible are * built. * * @param path A path * * @throws IllegalArgumentException If the path points to a file, and not a directory * @throws NullPointerException If the path is null */ public void addRelativizeRoot(Path path) { // Note: the given path is not further modified or resolved. E.g. there is no special handling for symlinks. // The goal is, that if the user inputs a path, PMD should output in terms of that path, not it's resolution. this.relativizeRoots.add(Objects.requireNonNull(path)); if (Files.isRegularFile(path)) { throw new IllegalArgumentException("Relativize root should be a directory: " + path); } } /** * Add several paths to shorten paths that are output in the report. * See {@link #addRelativizeRoot(Path)}. * * @param paths A list of non-null paths * * @throws IllegalArgumentException If any path points to a file, and not a directory * @throws NullPointerException If the list, or any path in the list is null */ public void addRelativizeRoots(List paths) { for (Path path : paths) { addRelativizeRoot(path); } } /** * Returns the paths used to shorten paths output in the report. *

    *
  • If the list is empty, then paths are not touched *
  • If the list is non-empty, then source file paths are relativized with all the items in the list. * The shortest of these relative paths is taken as the display name of the file. *
*/ public List getRelativizeRoots() { return Collections.unmodifiableList(relativizeRoots); } /** * Get the input URI to process for source code objects. * * @return URI */ public URI getUri() { return inputUri; } /** * Set the input URI to process for source code objects. * * @param inputUri a single URI */ public void setInputUri(URI inputUri) { this.inputUri = inputUri; } /** * Returns the list of input paths to explore. This is an * unmodifiable list. */ public @NonNull List getInputPathList() { return Collections.unmodifiableList(inputPaths); } /** * Set the input paths to the given list of paths. * * @throws NullPointerException If the parameter is null or contains a null value */ public void setInputPathList(final List inputPaths) { AssertionUtil.requireContainsNoNullValue("input paths", inputPaths); this.inputPaths = new ArrayList<>(inputPaths); } /** * Add an input path. It is not split on commas. * * @throws NullPointerException If the parameter is null */ public void addInputPath(@NonNull Path inputPath) { Objects.requireNonNull(inputPath); this.inputPaths.add(inputPath); } /** Returns the path to the file list include file. */ public @Nullable Path getInputFile() { return inputFilePath; } /** Returns the path to the file list exclude file. */ public @Nullable Path getIgnoreFile() { return ignoreFilePath; } /** * The input file path points to a single file, which contains a * comma-separated list of source file names to process. * * @param inputFilePath path to the file */ public void setInputFilePath(Path inputFilePath) { this.inputFilePath = inputFilePath; } /** * The input file path points to a single file, which contains a * comma-separated list of source file names to ignore. * * @param ignoreFilePath path to the file */ public void setIgnoreFilePath(Path ignoreFilePath) { this.ignoreFilePath = ignoreFilePath; } public List getExcludes() { return excludes; } public void setExcludes(List excludes) { this.excludes = Objects.requireNonNull(excludes); } public boolean collectFilesRecursively() { return collectRecursive; } public void collectFilesRecursively(boolean collectRecursive) { this.collectRecursive = collectRecursive; } /** * Whether PMD should exit with status 4 (the default behavior, true) if * violations are found or just with 0 (to not break the build, e.g.). * *

Note: If additionally recoverable errors occurred, the exit status is 5. See * {@link #isFailOnError()}. * * @return failOnViolation * * @see #isFailOnError() * @since 6.0.0 */ public boolean isFailOnViolation() { return failOnViolation; } /** * Sets whether PMD should exit with status 4 (the default behavior, true) * if violations are found or just with 0 (to not break the build, e.g.). * *

Note: If additionally recoverable errors occurred, the exit status is 5. See * {@link #isFailOnError()}. * * @param failOnViolation whether to exit with 4 and fail the build if violations are found. * * @see #isFailOnError() * @since 6.0.0 */ public void setFailOnViolation(boolean failOnViolation) { this.failOnViolation = failOnViolation; } /** * Whether PMD should exit with status 5 (the default behavior, true) if * recoverable errors occurred or just with 0 (to not break the build, e.g.). * *

Note: If only violations are found, the exit status is 4. See * {@link #isFailOnViolation()}. * * @return failOnError * * @see #isFailOnViolation() * @since 7.3.0 */ public boolean isFailOnError() { return failOnError; } /** * Sets whether PMD should exit with status 5 (the default behavior, true) * if recoverable errors occurred or just with 0 (to not break the build, e.g.). * *

Note: If only violations are found, the exit status is 4. See * {@link #isFailOnViolation()}. * * @param failOnError whether to exit with 5 and fail the build if recoverable errors occurred. * * @see #isFailOnViolation() * @since 7.3.0 */ public void setFailOnError(boolean failOnError) { this.failOnError = failOnError; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy