org.sonar.ide.eclipse.jdt.internal.JavaProjectConfigurator.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.ide.eclipse.jdt.internal.JavaProjectConfigurator.java

Source

/*
 * SonarQube Eclipse
 * Copyright (C) 2010-2014 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.ide.eclipse.jdt.internal;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.ide.eclipse.core.configurator.ProjectConfigurationRequest;
import org.sonar.ide.eclipse.core.configurator.ProjectConfigurator;
import org.sonar.ide.eclipse.core.configurator.SonarConfiguratorProperties;

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

public class JavaProjectConfigurator extends ProjectConfigurator {

    private static final Logger LOG = LoggerFactory.getLogger(JavaProjectConfigurator.class);
    // TODO Allow to configure this pattern in Sonar Eclipse preferences
    private static final String TEST_PATTERN = ".*test.*";

    @Override
    public boolean canConfigure(IProject project) {
        return SonarJdtPlugin.hasJavaNature(project);
    }

    @Override
    public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) {
        IProject project = request.getProject();
        IJavaProject javaProject = JavaCore.create(project);
        boolean isGreaterThan4_2 = request.isServerVersionGreaterOrEquals("4.2");
        configureJavaProject(javaProject, request.getSonarProjectProperties(), isGreaterThan4_2);
    }

    // Visible for testing
    public void configureJavaProject(IJavaProject javaProject, Properties sonarProjectProperties,
            boolean isGreaterThan4_2) {
        String javaSource = javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
        String javaTarget = javaProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true);

        if (!isGreaterThan4_2) {
            sonarProjectProperties.setProperty(SonarConfiguratorProperties.PROJECT_LANGUAGE_PROPERTY, "java");
        }
        sonarProjectProperties.setProperty("sonar.java.source", javaSource);
        LOG.info("Source Java version: {}", javaSource);
        sonarProjectProperties.setProperty("sonar.java.target", javaTarget);
        LOG.info("Target Java version: {}", javaTarget);

        try {
            JavaProjectConfiguration configuration = new JavaProjectConfiguration();
            configuration.dependentProjects().add(javaProject);
            addClassPathToSonarProject(javaProject, configuration, true);
            configurationToProperties(sonarProjectProperties, configuration);
        } catch (JavaModelException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    /**
     * Adds the classpath of an eclipse project to the sonarProject recursively, i.e
     * it iterates all dependent projects. Libraries and output folders of dependent projects
     * are added, but no source folders.
     * @param javaProject the eclipse project to get the classpath from
     * @param sonarProjectProperties the sonar project properties to add the classpath to
     * @param context
     * @param topProject indicate we are working on the project to be analysed and not on a dependent project
     * @throws JavaModelException see {@link IJavaProject#getResolvedClasspath(boolean)}
     */
    private void addClassPathToSonarProject(IJavaProject javaProject, JavaProjectConfiguration context,
            boolean topProject) throws JavaModelException {
        IClasspathEntry[] classPath = javaProject.getResolvedClasspath(true);
        for (IClasspathEntry entry : classPath) {
            switch (entry.getEntryKind()) {
            case IClasspathEntry.CPE_SOURCE:
                if (!isSourceExcluded(entry)) {
                    processSourceEntry(entry, javaProject, context, topProject);
                }
                break;
            case IClasspathEntry.CPE_LIBRARY:
                if (topProject || entry.isExported()) {
                    final String libDir = resolveLibrary(javaProject, entry);
                    if (libDir != null) {
                        LOG.debug("Library: {}", libDir);
                        context.libraries().add(libDir);
                    }
                }
                break;
            case IClasspathEntry.CPE_PROJECT:
                IJavaModel javaModel = javaProject.getJavaModel();
                IJavaProject referredProject = javaModel.getJavaProject(entry.getPath().segment(0));
                if (!context.dependentProjects().contains(referredProject)) {
                    LOG.debug("Adding project: {}", referredProject.getProject().getName());
                    addClassPathToSonarProject(referredProject, context, false);
                    context.dependentProjects().add(referredProject);
                }
                break;
            default:
                LOG.warn("Unhandled ClassPathEntry : {}", entry);
                break;
            }
        }

        processOutputDir(javaProject.getOutputLocation(), context, topProject);
    }

    private void processOutputDir(IPath outputDir, JavaProjectConfiguration context, boolean topProject)
            throws JavaModelException {
        String outDir = getAbsolutePath(outputDir);
        if (outDir != null) {
            LOG.debug("Output directory: {}", outDir);
            if (topProject) {
                context.binaries().add(outDir);
            } else {
                // Output dir of dependents projects should be considered as libraries
                context.libraries().add(outDir);
            }
        } else {
            LOG.warn(
                    "Binary directory was not added because it was not found. Maybe should you enable auto build of your project.");
        }
    }

    private void processSourceEntry(IClasspathEntry entry, IJavaProject javaProject,
            JavaProjectConfiguration context, boolean topProject) throws JavaModelException {
        String srcDir = getAbsolutePath(entry.getPath());
        if (srcDir == null) {
            LOG.warn("Skipping non existing source entry: {}", entry.getPath().toOSString());
            return;
        }
        String relativeDir = getRelativePath(javaProject, entry.getPath());
        if (relativeDir.toLowerCase().matches(TEST_PATTERN)) {
            if (topProject) {
                LOG.debug("Test directory: {}", srcDir);
                context.testDirs().add(srcDir);
            }
        } else {
            if (topProject) {
                LOG.debug("Source directory: {}", srcDir);
                context.sourceDirs().add(srcDir);
            }
            if (entry.getOutputLocation() != null) {
                processOutputDir(entry.getOutputLocation(), context, topProject);
            }
        }
    }

    private String resolveLibrary(IJavaProject javaProject, IClasspathEntry entry) {
        final String libDir;
        IResource member = findPath(javaProject.getProject(), entry.getPath());
        if (member != null) {
            LOG.debug("Found member: {}", member.getLocation().toOSString());
            libDir = member.getLocation().toOSString();
        } else {
            libDir = entry.getPath().makeAbsolute().toOSString();
        }
        if (!new File(libDir).exists()) {
            return null;
        }
        return libDir;
    }

    private IResource findPath(IProject project, IPath path) {
        IResource member = project.findMember(path);
        if (member == null) {
            IWorkspaceRoot workSpaceRoot = project.getWorkspace().getRoot();
            member = workSpaceRoot.findMember(path);
        }
        return member;
    }

    /**
     * Allows to determine directories with resources to exclude them from analysis, otherwise analysis might fail due to SONAR-791.
     * This is a kind of workaround, which is based on the fact that M2Eclipse configures exclusion pattern "**" for directories with resources.
     */
    private boolean isSourceExcluded(IClasspathEntry entry) {
        IPath[] exclusionPatterns = entry.getExclusionPatterns();
        if (exclusionPatterns != null) {
            for (IPath exclusionPattern : exclusionPatterns) {
                if ("**".equals(exclusionPattern.toString())) {
                    return true;
                }
            }
        }
        return false;
    }

    private String getRelativePath(IJavaProject javaProject, IPath path) {
        return path.makeRelativeTo(javaProject.getPath()).toOSString();
    }

    private void configurationToProperties(Properties sonarProjectProperties, JavaProjectConfiguration context) {
        setPropertyList(sonarProjectProperties, SonarConfiguratorProperties.LIBRARIES_PROPERTY,
                context.libraries());
        setPropertyList(sonarProjectProperties, SonarConfiguratorProperties.TEST_DIRS_PROPERTY, context.testDirs());
        setPropertyList(sonarProjectProperties, SonarConfiguratorProperties.SOURCE_DIRS_PROPERTY,
                context.sourceDirs());
        setPropertyList(sonarProjectProperties, SonarConfiguratorProperties.BINARIES_PROPERTY, context.binaries());
    }
}