org.sonar.java.JavaClasspath.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.java.JavaClasspath.java

Source

/*
 * SonarQube Java
 * Copyright (C) 2012 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.java;

import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.AndFileFilter;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.FalseFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.OrFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.project.MavenProject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchExtension;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
import org.sonar.api.utils.SonarException;
import org.sonar.api.utils.WildcardPattern;

import javax.annotation.Nullable;
import java.io.File;
import java.io.FileFilter;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import static org.apache.commons.io.filefilter.FileFilterUtils.suffixFileFilter;

public class JavaClasspath implements BatchExtension {

    private static final char SEPARATOR = ',';
    private static final Logger LOG = LoggerFactory.getLogger(JavaClasspath.class);

    private List<File> binaries;
    private List<File> elements;
    private boolean validateLibraries;

    public JavaClasspath(Project project, Settings settings, FileSystem fileSystem) {
        this(project, settings, fileSystem, null);
    }

    public JavaClasspath(Project project, Settings settings, FileSystem fileSystem, @Nullable MavenProject pom) {
        validateLibraries = project.getModules().isEmpty();
        binaries = getFilesFromProperty(JavaClasspathProperties.SONAR_JAVA_BINARIES, settings,
                fileSystem.baseDir());
        List<File> libraries = getFilesFromProperty(JavaClasspathProperties.SONAR_JAVA_LIBRARIES, settings,
                fileSystem.baseDir());
        boolean useDeprecatedProperties = binaries.isEmpty() && libraries.isEmpty();
        if (useDeprecatedProperties) {
            binaries = getFilesFromProperty("sonar.binaries", settings, fileSystem.baseDir());
            libraries = getFilesFromProperty("sonar.libraries", settings, fileSystem.baseDir());
        }
        if (pom != null && libraries.isEmpty()) {
            //check mojo
            elements = getLibrariesFromMaven(pom);
        } else {
            elements = Lists.newArrayList(binaries);
            elements.addAll(libraries);
            if (useDeprecatedProperties && !elements.isEmpty()) {
                LOG.warn(
                        "sonar.binaries and sonar.libraries are deprecated since version 2.5 of sonar-java-plugin, please use sonar.java.binaries and sonar.java.libraries instead");
            }
        }
    }

    private List<File> getLibrariesFromMaven(MavenProject pom) {
        try {
            List<File> files = Lists.newArrayList();
            if (pom.getCompileClasspathElements() != null) {
                for (String classPathString : (List<String>) pom.getCompileClasspathElements()) {
                    files.add(new File(classPathString));
                }
            }
            if (pom.getBuild().getOutputDirectory() != null) {
                File outputDirectoryFile = new File(pom.getBuild().getOutputDirectory());
                if (outputDirectoryFile.exists()) {
                    files.add(outputDirectoryFile);
                }
            }
            return files;
        } catch (DependencyResolutionRequiredException e) {
            throw new SonarException("Fail to create the project classloader", e);
        }
    }

    private List<File> getFilesFromProperty(String property, Settings settings, File baseDir) {
        List<File> result = Lists.newArrayList();
        String fileList = settings.getString(property);
        if (StringUtils.isNotEmpty(fileList)) {
            Iterable<String> fileNames = Splitter.on(SEPARATOR).omitEmptyStrings().split(fileList);
            for (String pathPattern : fileNames) {
                boolean isLibraryProperty = property.endsWith("libraries");
                List<File> libraryFilesForPattern = getFilesForPattern(baseDir, pathPattern, isLibraryProperty);
                if (validateLibraries && libraryFilesForPattern.isEmpty()) {
                    LOG.error("Invalid value for " + property);
                    String message = "No files nor directories matching '" + pathPattern + "'";
                    throw new IllegalStateException(message);
                }
                result.addAll(libraryFilesForPattern);
            }
        }
        return result;
    }

    private List<File> getFilesForPattern(File baseDir, String pathPattern, boolean libraryProperty) {
        String dirPath = pathPattern;
        String filePattern;
        int wildcardIndex = pathPattern.indexOf('*');
        if (wildcardIndex >= 0) {
            dirPath = pathPattern.substring(0, wildcardIndex);
        }
        int lastPathSeparator = Math.max(dirPath.lastIndexOf('/'), dirPath.lastIndexOf('\\'));
        if (lastPathSeparator == -1) {
            dirPath = ".";
            filePattern = pathPattern;
        } else {
            dirPath = pathPattern.substring(0, lastPathSeparator);
            filePattern = pathPattern.substring(lastPathSeparator + 1);
        }
        File dir = resolvePath(baseDir, dirPath);
        if (!dir.isDirectory()) {
            return Lists.newArrayList();
        }
        return getMatchingFiles(filePattern, dir, libraryProperty);
    }

    private List<File> getMatchingFiles(String pattern, File dir, boolean libraryProperty) {
        WilcardPatternFileFilter wilcardPatternFileFilter = new WilcardPatternFileFilter(dir, pattern);
        FileFilter fileFilter = wilcardPatternFileFilter;
        List<File> files = Lists.newArrayList();
        if (libraryProperty) {
            if (pattern.endsWith("*")) {
                fileFilter = new AndFileFilter((IOFileFilter) fileFilter,
                        new OrFileFilter(Lists.newArrayList(suffixFileFilter(".jar", IOCase.INSENSITIVE),
                                suffixFileFilter(".zip", IOCase.INSENSITIVE))));
            }
            //find jar and zip files
            files.addAll(
                    Lists.newArrayList(FileUtils.listFiles(dir, (IOFileFilter) fileFilter, TrueFileFilter.TRUE)));
        }
        //find directories matching pattern.
        IOFileFilter subdirectories = pattern.isEmpty() ? FalseFileFilter.FALSE : TrueFileFilter.TRUE;
        Collection<File> dirs = FileUtils.listFilesAndDirs(dir,
                new AndFileFilter(wilcardPatternFileFilter, DirectoryFileFilter.DIRECTORY), subdirectories);
        //remove searching dir from matching as listFilesAndDirs always includes it in the list see https://issues.apache.org/jira/browse/IO-328
        if (!pattern.isEmpty()) {
            dirs.remove(dir);
            //remove subdirectories that were included during search
            Iterator<File> iterator = dirs.iterator();
            while (iterator.hasNext()) {
                File matchingDir = iterator.next();
                if (!wilcardPatternFileFilter.accept(matchingDir)) {
                    iterator.remove();
                }
            }
        }

        if (libraryProperty) {
            for (File directory : dirs) {
                files.addAll(getMatchingFiles("**/*.jar", directory, true));
                files.addAll(getMatchingFiles("**/*.zip", directory, true));
            }
        }
        files.addAll(dirs);
        return files;
    }

    private File resolvePath(File baseDir, String fileName) {
        File file = new File(fileName);
        if (!file.isAbsolute()) {
            file = new File(baseDir, fileName);
        }
        return file;
    }

    public List<File> getElements() {
        return elements;
    }

    public List<File> getBinaryDirs() {
        return binaries;
    }

    private static class WilcardPatternFileFilter implements IOFileFilter {
        private File baseDir;
        private WildcardPattern wildcardPattern;

        public WilcardPatternFileFilter(File baseDir, String wildcardPattern) {
            this.baseDir = baseDir;
            this.wildcardPattern = WildcardPattern.create(FilenameUtils.separatorsToSystem(wildcardPattern),
                    File.separator);
        }

        @Override
        public boolean accept(File dir, String name) {
            return accept(new File(dir, name));
        }

        @Override
        public boolean accept(File file) {
            String path = file.getAbsolutePath();
            path = path.substring(baseDir.getAbsolutePath().length() + 1);
            return wildcardPattern.match(path);
        }
    }
}