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

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

There is a newer version: 7.7.0
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