org.sonar.plugins.csharp.core.VisualStudioProjectBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.plugins.csharp.core.VisualStudioProjectBuilder.java

Source

/*
 * Sonar C# Plugin :: Core
 * Copyright (C) 2010 Jose Chillan, Alexandre Victoor and SonarSource
 * dev@sonar.codehaus.org
 *
 * 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  02
 */
package org.sonar.plugins.csharp.core;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Properties;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.bootstrap.ProjectBuilder;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.utils.SonarException;
import org.sonar.dotnet.tools.commons.DotNetToolsException;
import org.sonar.dotnet.tools.commons.visualstudio.ModelFactory;
import org.sonar.dotnet.tools.commons.visualstudio.SourceFile;
import org.sonar.dotnet.tools.commons.visualstudio.VisualStudioProject;
import org.sonar.dotnet.tools.commons.visualstudio.VisualStudioSolution;
import org.sonar.plugins.csharp.api.CSharpConfiguration;
import org.sonar.plugins.csharp.api.CSharpConstants;
import org.sonar.plugins.csharp.api.MicrosoftWindowsEnvironment;

/**
 * Project Builder created and executed once per build to override the project definition, based on the Visual Studio files found in the
 * sources.
 */
public class VisualStudioProjectBuilder extends ProjectBuilder {

    private static final Logger LOG = LoggerFactory.getLogger(VisualStudioProjectBuilder.class);

    private CSharpConfiguration configuration;
    private MicrosoftWindowsEnvironment microsoftWindowsEnvironment;

    /**
     * Creates a new {@link VisualStudioProjectBuilder}
     * 
     * @param reactor
     *          the reactor
     * @param configuration
     *          the shared C# configuration
     * @param microsoftWindowsEnvironment
     *          the shared Microsoft Windows Environment
     */
    public VisualStudioProjectBuilder(ProjectReactor reactor, CSharpConfiguration configuration,
            MicrosoftWindowsEnvironment microsoftWindowsEnvironment) {
        super(reactor);
        this.configuration = configuration;
        this.microsoftWindowsEnvironment = microsoftWindowsEnvironment;
    }

    @Override
    protected void build(ProjectReactor reactor) {
        if ("cs".equals(configuration.getString("sonar.language", ""))) {
            LOG.debug("Executing VisualStudioProjectBuilder");
            ProjectDefinition root = reactor.getRoot();

            // First, read all the plugin configuration details related to MS Windows
            retrieveMicrosoftWindowsEnvironmentConfig();

            // Then create the Visual Studio Solution object from the ".sln" file
            createVisualStudioSolution(root.getBaseDir());

            // And finally create the Sonar projects definition
            createMultiProjectStructure(root);

            // lock the MicrosoftWindowsEnvironment object so that nobody can modify it afterwards
            microsoftWindowsEnvironment.lock();
        }
    }

    private void createMultiProjectStructure(ProjectDefinition root) {
        VisualStudioSolution currentSolution = microsoftWindowsEnvironment.getCurrentSolution();
        root.resetSourceDirs();
        LOG.debug("- Root Project: {}", root.getName());
        String workDir = root.getWorkDir().getAbsolutePath()
                .substring(root.getBaseDir().getAbsolutePath().length() + 1);
        microsoftWindowsEnvironment.setWorkingDirectory(workDir);
        Properties rootProps = enhanceRootProperties(root);

        for (VisualStudioProject vsProject : currentSolution.getProjects()) {
            String projectKey = StringUtils.substringBefore(root.getKey(), ":") + ":"
                    + StringUtils.deleteWhitespace(vsProject.getName());
            if (projectKey.equals(root.getKey())) {
                throw new SonarException("The solution and one of its projects have the same key ('" + projectKey
                        + "'). Please set a unique 'sonar.projectKey' for the solution.");
            }

            ProjectDefinition subProject = ProjectDefinition.create((Properties) rootProps.clone())
                    .setBaseDir(vsProject.getDirectory()).setWorkDir(new File(vsProject.getDirectory(), workDir))
                    .setKey(projectKey).setVersion(root.getVersion()).setName(vsProject.getName())
                    .addContainerExtension(microsoftWindowsEnvironment);

            if (vsProject.isTest()) {
                subProject.setTestDirs(".");
                for (SourceFile sourceFile : vsProject.getSourceFiles()) {
                    subProject.addTestFiles(sourceFile.getFile());
                }
            } else {
                subProject.setSourceDirs(".");
                for (SourceFile sourceFile : vsProject.getSourceFiles()) {
                    subProject.addSourceFiles(sourceFile.getFile());
                }
            }

            LOG.debug("  - Adding Sub Project => {}", subProject.getName());
            root.addSubProject(subProject);
        }
    }

    protected Properties enhanceRootProperties(ProjectDefinition root) {
        Properties props = root.getProperties();
        // Handling encoding
        if (StringUtils.isBlank(props.getProperty("sonar.sourceEncoding"))) {
            LOG.info("'sonar.sourceEncoding' has not been defined: setting it to default value 'UTF-8'.");
            props.setProperty("sonar.sourceEncoding", "UTF-8");
        }
        // Handling exclusions
        if (configuration.getBoolean(CSharpConstants.EXCLUDE_GENERATED_CODE_KEY,
                CSharpConstants.EXCLUDE_GENERATED_CODE_DEFVALUE)) {
            String exclusions = props.getProperty("sonar.exclusions", "");
            StringBuilder newExclusions = new StringBuilder(exclusions);
            if (!StringUtils.isEmpty(exclusions)) {
                newExclusions.append(",");
            }
            newExclusions.append(CSharpConstants.DEFAULT_FILES_TO_EXCLUDE);
            props.setProperty("sonar.exclusions", newExclusions.toString());
        }
        return props;
    }

    private void retrieveMicrosoftWindowsEnvironmentConfig() {
        // .NET version
        String dotnetVersion = configuration.getString(CSharpConstants.DOTNET_VERSION_KEY,
                CSharpConstants.DOTNET_VERSION_DEFVALUE);
        microsoftWindowsEnvironment.setDotnetVersion(dotnetVersion);
        // .NET SDK folder
        File dotnetSdkDirectory = new File(
                configuration.getString(CSharpConstants.getDotnetSdkDirKey(dotnetVersion),
                        CSharpConstants.getDotnetSdkDirDefaultValue(dotnetVersion)));
        if (!dotnetSdkDirectory.isDirectory()) {
            throw new SonarException(
                    "The following .NET SDK directory does not exist, please check your plugin configuration: "
                            + dotnetSdkDirectory.getPath());
        } else {
            microsoftWindowsEnvironment.setDotnetSdkDirectory(dotnetSdkDirectory);
        }
        // Silverlight version
        String silverlightVersion = configuration.getString(CSharpConstants.SILVERLIGHT_VERSION_KEY,
                CSharpConstants.SILVERLIGHT_VERSION_DEFVALUE);
        microsoftWindowsEnvironment.setSilverlightVersion(silverlightVersion);
        // Silverlight folder
        String defaultSilverlightPath = CSharpConstants.getSilverlightDirDefaultValue(silverlightVersion);
        String silverlightPath = configuration.getString(CSharpConstants.getSilverlightDirKey(silverlightVersion),
                defaultSilverlightPath);
        File silverlightDirectory = new File(silverlightPath);
        if (defaultSilverlightPath.equals(silverlightPath)) {
            // default value used, no validity check
            LOG.debug("Default silverlight path will be used");
        } else if (!silverlightDirectory.isDirectory()) {
            throw new SonarException(
                    "The following silverlight SDK directory does not exist, please check your plugin configuration: "
                            + silverlightDirectory.getPath());
        }
        microsoftWindowsEnvironment.setSilverlightDirectory(silverlightDirectory);
    }

    private void createVisualStudioSolution(File baseDir) {
        File slnFile = findSlnFile(baseDir);
        if (slnFile == null) {
            throw new SonarException(
                    "No valid '.sln' file could be found. Please read the previous log messages to know more.");
        }
        LOG.info("The following 'sln' file has been found and will be used: " + slnFile.getAbsolutePath());

        try {
            ModelFactory.setTestProjectNamePattern(configuration.getString(CSharpConstants.TEST_PROJET_PATTERN_KEY,
                    CSharpConstants.TEST_PROJET_PATTERN_DEFVALUE));
            VisualStudioSolution solution = ModelFactory.getSolution(slnFile);
            microsoftWindowsEnvironment.setCurrentSolution(solution);
        } catch (IOException e) {
            throw new SonarException("Error occured while reading Visual Studio files.", e);
        } catch (DotNetToolsException e) {
            throw new SonarException("Error occured while reading Visual Studio files.", e);
        }
    }

    private File findSlnFile(File baseDir) {
        String slnFilePath = configuration.getString(CSharpConstants.SOLUTION_FILE_KEY,
                CSharpConstants.SOLUTION_FILE_DEFVALUE);
        final File slnFile;
        if (StringUtils.isEmpty(slnFilePath)) {
            LOG.info("No '.sln' file found or specified: trying to find one...");
            slnFile = searchForSlnFile(baseDir);
        } else {
            final File confSlnFile = new File(baseDir, slnFilePath);
            if (confSlnFile.isFile()) {
                slnFile = confSlnFile;
            } else {
                slnFile = null;
                LOG.warn("The specified '.sln' path does not point to an existing file: "
                        + confSlnFile.getAbsolutePath());
            }
        }
        return slnFile;
    }

    private File searchForSlnFile(File baseDir) {
        File slnFile = null;
        @SuppressWarnings("unchecked")
        Collection<File> foundSlnFiles = FileUtils.listFiles(baseDir, new String[] { "sln" }, false);
        if (foundSlnFiles.isEmpty()) {
            LOG.warn("No '.sln' file specified, and none found at the root of the project: "
                    + baseDir.getAbsolutePath());
        } else if (foundSlnFiles.size() > 1) {
            LOG.warn(
                    "More than one '.sln' file found at the root of the project: please tell which one to use via the configuration ("
                            + CSharpConstants.SOLUTION_FILE_KEY + ").");
        } else {
            slnFile = foundSlnFiles.iterator().next();
        }
        return slnFile;
    }

}