org.sonar.plugins.fxcop.FxCopSensor.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.plugins.fxcop.FxCopSensor.java

Source

/*
 * SonarQube FxCop Library
 * Copyright (C) 2014-2016 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.plugins.fxcop;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputFile.Type;
import org.sonar.api.component.ResourcePerspectives;
import org.sonar.api.config.Settings;
import org.sonar.api.issue.Issuable;
import org.sonar.api.issue.Issuable.IssueBuilder;
import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.ActiveRule;

public class FxCopSensor implements Sensor {

    private static final String CUSTOM_RULE_KEY = "CustomRuleTemplate";
    private static final String CUSTOM_RULE_CHECK_ID_PARAMETER = "CheckId";
    private static final Logger LOG = LoggerFactory.getLogger(FxCopSensor.class);

    private final FxCopConfiguration fxCopConf;
    private final Settings settings;
    private final RulesProfile profile;
    private final FileSystem fs;
    private final ResourcePerspectives perspectives;

    public FxCopSensor(FxCopConfiguration fxCopConf, Settings settings, RulesProfile profile, FileSystem fs,
            ResourcePerspectives perspectives) {
        this.fxCopConf = fxCopConf;
        this.settings = settings;
        this.profile = profile;
        this.fs = fs;
        this.perspectives = perspectives;
    }

    @Override
    public boolean shouldExecuteOnProject(Project project) {
        boolean shouldExecute;

        if (!hasFilesToAnalyze()) {
            shouldExecute = false;
        } else if (profile.getActiveRulesByRepository(fxCopConf.repositoryKey()).isEmpty()) {
            LOG.info("All FxCop rules are disabled, skipping its execution.");
            shouldExecute = false;
        } else if (!SystemUtils.IS_OS_WINDOWS) {
            shouldExecute = false;
        } else {
            shouldExecute = true;
        }

        return shouldExecute;
    }

    private boolean hasFilesToAnalyze() {
        return fs.files(fs.predicates().and(fs.predicates().hasLanguage(fxCopConf.languageKey()),
                fs.predicates().hasType(Type.MAIN))).iterator().hasNext();
    }

    @Override
    public void analyse(Project project, SensorContext context) {
        analyse(context, new FxCopRulesetWriter(), new FxCopReportParser(), new FxCopExecutor(),
                perspectives.as(Issuable.class, (Resource) project));
    }

    @VisibleForTesting
    void analyse(SensorContext context, FxCopRulesetWriter writer, FxCopReportParser parser, FxCopExecutor executor,
            Issuable projectIssuable) {
        fxCopConf.checkProperties(settings);

        File reportFile;
        String reportPath = settings.getString(fxCopConf.reportPathPropertyKey());
        if (reportPath == null) {
            File rulesetFile = new File(fs.workDir(), "fxcop-sonarqube.ruleset");
            writer.write(enabledRuleConfigKeys(), rulesetFile);

            reportFile = new File(fs.workDir(), "fxcop-report.xml");

            executor.execute(settings.getString(fxCopConf.fxCopCmdPropertyKey()),
                    settings.getString(fxCopConf.assemblyPropertyKey()), rulesetFile, reportFile,
                    settings.getInt(fxCopConf.timeoutPropertyKey()),
                    settings.getBoolean(fxCopConf.aspnetPropertyKey()),
                    splitOnCommas(settings.getString(fxCopConf.directoriesPropertyKey())),
                    splitOnCommas(settings.getString(fxCopConf.referencesPropertyKey())));
        } else {
            LOG.debug("Using the provided FxCop report" + reportPath);
            reportFile = new File(reportPath);
        }

        for (FxCopIssue issue : parser.parse(reportFile)) {
            String absolutePath = getSourceFileAbsolutePath(issue);

            InputFile inputFile = null;
            if (absolutePath != null) {
                inputFile = fs.inputFile(fs.predicates().hasAbsolutePath(absolutePath));
            }

            String messageLocation = "";
            Issuable issuable;
            boolean isOnProjectIssuable = false;
            if (inputFile != null) {
                issuable = perspectives.as(Issuable.class, inputFile);
            } else {
                issuable = projectIssuable;
                isOnProjectIssuable = true;

                if (absolutePath != null) {
                    messageLocation += absolutePath;

                    if (issue.line() != null) {
                        messageLocation += " line " + issue.line();
                    }
                }

                if (!messageLocation.isEmpty()) {
                    messageLocation += ": ";
                }
            }

            if (issuable == null) {
                LOG.warn("Skipping the FxCop issue at line " + issue.reportLine()
                        + " which has no associated SonarQube issuable.");
                continue;
            }

            IssueBuilder issueBuilder = issuable.newIssueBuilder()
                    .ruleKey(RuleKey.of(fxCopConf.repositoryKey(), ruleKey(issue.ruleConfigKey())))
                    .message(messageLocation + issue.message());

            Integer line = fxCopToSonarQubeLine(issue.line(), isOnProjectIssuable);
            if (line != null) {
                issueBuilder.line(line);
            }

            issuable.addIssue(issueBuilder.build());
        }
    }

    @CheckForNull
    private static Integer fxCopToSonarQubeLine(@Nullable Integer fxcopLine, boolean isOnProjectIssuable) {
        if (fxcopLine == null || isOnProjectIssuable) {
            return null;
        }

        return fxcopLine <= 0 ? null : fxcopLine;
    }

    private static List<String> splitOnCommas(@Nullable String property) {
        if (property == null) {
            return ImmutableList.of();
        } else {
            return ImmutableList.copyOf(Splitter.on(",").trimResults().omitEmptyStrings().split(property));
        }
    }

    @CheckForNull
    private static String getSourceFileAbsolutePath(FxCopIssue issue) {
        if (issue.path() == null || issue.file() == null) {
            return null;
        }

        File file = new File(new File(issue.path()), issue.file());
        return file.getAbsolutePath();
    }

    private List<String> enabledRuleConfigKeys() {
        ImmutableList.Builder<String> builder = ImmutableList.builder();
        for (ActiveRule activeRule : profile.getActiveRulesByRepository(fxCopConf.repositoryKey())) {
            if (!CUSTOM_RULE_KEY.equals(activeRule.getRuleKey())) {
                String effectiveConfigKey = activeRule.getConfigKey();
                if (effectiveConfigKey == null) {
                    effectiveConfigKey = activeRule.getParameter(CUSTOM_RULE_CHECK_ID_PARAMETER);
                }

                builder.add(effectiveConfigKey);
            }
        }
        return builder.build();
    }

    private String ruleKey(String ruleConfigKey) {
        for (ActiveRule activeRule : profile.getActiveRulesByRepository(fxCopConf.repositoryKey())) {
            if (ruleConfigKey.equals(activeRule.getConfigKey())
                    || ruleConfigKey.equals(activeRule.getParameter(CUSTOM_RULE_CHECK_ID_PARAMETER))) {
                return activeRule.getRuleKey();
            }
        }

        throw new IllegalStateException("Unable to find the rule key corresponding to the rule config key \""
                + ruleConfigKey + "\" in repository \"" + fxCopConf.repositoryKey() + "\".");
    }

}