Package org.infernus.idea.checkstyle.checker

Source Code of org.infernus.idea.checkstyle.checker.FileScanner

package org.infernus.idea.checkstyle.checker;

import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleServiceManager;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.puppycrawl.tools.checkstyle.Checker;
import com.puppycrawl.tools.checkstyle.api.Configuration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.infernus.idea.checkstyle.CheckStyleModuleConfiguration;
import org.infernus.idea.checkstyle.CheckStylePlugin;
import org.infernus.idea.checkstyle.checks.Check;
import org.infernus.idea.checkstyle.checks.CheckFactory;
import org.infernus.idea.checkstyle.exception.CheckStylePluginException;
import org.infernus.idea.checkstyle.model.ConfigurationLocation;
import org.infernus.idea.checkstyle.toolwindow.CheckStyleToolWindowPanel;
import org.infernus.idea.checkstyle.util.CheckStyleUtilities;
import org.infernus.idea.checkstyle.util.ScannableFile;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
* Runnable for scanning an individual file.
*/
final class FileScanner implements Runnable {

    private static final Log LOG = LogFactory.getLog(FileScanner.class);

    private final CheckStylePlugin plugin;
    private final Set<PsiFile> filesToScan;
    private final ClassLoader moduleClassLoader;
    private final ConfigurationLocation overrideConfigLocation;

    private ConfigurationLocationStatus configurationLocationStatus = ConfigurationLocationStatus.PRESENT;
    private Map<PsiFile, List<ProblemDescriptor>> results;
    private Throwable error;

    /**
     * Create a new file scanner.
     *
     * @param checkStylePlugin       CheckStylePlugin.
     * @param filesToScan            the files to scan.
     * @param moduleClassLoader      the class loader for the file's module
     * @param overrideConfigLocation if non-null this configuration will be used in preference to the normal configuration.
     */
    public FileScanner(final CheckStylePlugin checkStylePlugin,
                       final Set<PsiFile> filesToScan,
                       final ClassLoader moduleClassLoader,
                       final ConfigurationLocation overrideConfigLocation) {
        this.plugin = checkStylePlugin;
        this.filesToScan = filesToScan;
        this.moduleClassLoader = moduleClassLoader;
        this.overrideConfigLocation = overrideConfigLocation;
    }

    public void run() {
        try {
            results = checkPsiFile(filesToScan, overrideConfigLocation);

            final CheckStyleToolWindowPanel toolWindowPanel = CheckStyleToolWindowPanel.panelFor(plugin.getProject());
            if (toolWindowPanel != null) {
                toolWindowPanel.incrementProgressBarBy(filesToScan.size());
            }
        } catch (Throwable e) {
            error = e;
        }
    }

    /**
     * Get the results of the scan.
     *
     * @return the results of the scan.
     */
    public Map<PsiFile, List<ProblemDescriptor>> getResults() {
        if (results != null) {
            return Collections.unmodifiableMap(results);
        }

        return Collections.emptyMap();
    }

    /**
     * Get any error that may have occurred during the scan.
     *
     * @return any error that may have occurred during the scan
     */
    public Throwable getError() {
        return error;
    }

    ConfigurationLocationStatus getConfigurationLocationStatus() {
        return configurationLocationStatus;
    }

    /**
     * Scan a PSI file with CheckStyle.
     *
     * @param psiFilesToScan the PSI psiFilesToScan to scan. These will be
     *                       ignored if not a java file and not from the same module.
     * @param override       if non-null this location will be used in preference to the configured rules file.
     * @return a list of tree nodes representing the result tree for this
     * file, an empty list or null if this file is invalid or
     * has no errors.
     * @throws Throwable if the
     */
    private Map<PsiFile, List<ProblemDescriptor>> checkPsiFile(final Set<PsiFile> psiFilesToScan,
                                                               final ConfigurationLocation override)
            throws Throwable {
        if (psiFilesToScan == null || psiFilesToScan.isEmpty()) {
            LOG.debug("No elements were specified");
            return null;
        }

        Module module = null;

        final List<ScannableFile> tempFiles = new ArrayList<ScannableFile>();
        final Map<String, PsiFile> filesToElements = new HashMap<String, PsiFile>();

        final boolean checkTestClasses = this.plugin.getConfiguration().isScanningTestClasses();
        final boolean scanOnlyJavaFiles = !plugin.getConfiguration().isScanningNonJavaFiles();

        try {
            final AccessToken readAccessToken = ApplicationManager.getApplication().acquireReadActionLock();
            try {
                for (final PsiFile psiFile : psiFilesToScan) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Processing " + describe(psiFile));
                    }

                    if (psiFile == null || !psiFile.getVirtualFile().isValid() || !psiFile.isPhysical()) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Skipping as invalid type: " + describe(psiFile));
                        }
                        continue;
                    }

                    if (module == null) {
                        module = ModuleUtil.findModuleForPsiElement(psiFile);
                    } else {
                        final Module elementModule = ModuleUtil.findModuleForPsiElement(psiFile);
                        if (!elementModule.equals(module)) {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("Skipping as modules do not match: " + describe(psiFile)
                                        + " : " + elementModule + " does not match " + module);
                            }
                            continue;
                        }
                    }

                    if (!checkTestClasses && isTestClass(psiFile)) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Skipping test class " + psiFile.getName());
                        }
                        continue;
                    }

                    if (scanOnlyJavaFiles && !CheckStyleUtilities.isJavaFile(psiFile.getFileType())) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Skipping non-Java file " + psiFile.getName());
                        }
                        continue;
                    }

                    final ScannableFile tempFile = createTemporaryFile(psiFile, module);
                    if (tempFile != null) {
                        tempFiles.add(tempFile);
                        filesToElements.put(tempFile.getAbsolutePath(), psiFile);
                    }
                }
            } finally {
                readAccessToken.finish();
            }

            if (module == null || filesToElements.isEmpty()) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("No valid files were supplied");
                }
                return null;
            }

            return performCheckStyleScan(module, tempFiles, filesToElements, override);

        } finally {
            for (final ScannableFile tempFile : tempFiles) {
                if (tempFile != null) {
                    tempFile.deleteIfRequired();
                }
            }
        }
    }

    private String describe(final PsiFile psiFile) {
        if (psiFile != null) {
            return psiFile.getName();
        }
        return null;
    }

    @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
    private Map<PsiFile, List<ProblemDescriptor>> performCheckStyleScan(final Module module,
                                                                        final List<ScannableFile> tempFiles,
                                                                        final Map<String, PsiFile> filesToElements,
                                                                        final ConfigurationLocation override) {
        final InspectionManager manager = InspectionManager.getInstance(module.getProject());

        final ConfigurationLocation location = getConfigurationLocation(module, override);
        if (location == null) {
            configurationLocationStatus = ConfigurationLocationStatus.NOT_PRESENT;
            return null;
        }

        if (location.isBlacklisted()) {
            configurationLocationStatus = ConfigurationLocationStatus.BLACKLISTED;
            return null;
        }

        final CheckerContainer checkerContainer = getChecker(module, moduleClassLoader, location);
        final Configuration config = getConfig(module, location);
        if (checkerContainer == null || config == null) {
            return Collections.emptyMap();
        }

        final List<Check> checks = CheckFactory.getChecks(config);
        final boolean suppressingErrors = plugin.getConfiguration().isSuppressingErrors();

        final CheckStyleAuditListener listener;
        final Checker checker = checkerContainer.getChecker();
        synchronized (checker) {
            listener = new CheckStyleAuditListener(
                    filesToElements, manager, true, suppressingErrors, checkerContainer.getTabWidth(), checks);
            checker.addListener(listener);
            checker.process(asListOfFiles(tempFiles));
        }

        return listener.getAllProblems();
    }

    private List<File> asListOfFiles(final List<ScannableFile> tempFiles) {
        final List<File> listOfFiles = new ArrayList<File>();
        for (ScannableFile tempFile : tempFiles) {
            listOfFiles.add(tempFile.getFile());
        }
        return listOfFiles;
    }

    private ScannableFile createTemporaryFile(final PsiFile psiFile, final Module module) {
        ScannableFile tempFile = null;
        try {
            // we need to copy to a file as IntelliJ may not have
            // saved the file recently...
            final CreateScannableFileAction fileAction
                    = new CreateScannableFileAction(psiFile, module);
            ApplicationManager.getApplication().runReadAction(fileAction);

            // rethrow any error from the thread.
            //noinspection ThrowableResultOfMethodCallIgnored
            if (fileAction.getFailure() != null) {
                throw fileAction.getFailure();
            }

            tempFile = fileAction.getFile();
            if (tempFile == null) {
                throw new IllegalStateException("Failed to create temporary file.");
            }

        } catch (IOException e) {
            LOG.error("Failure when creating temp file", e);
        }

        return tempFile;
    }

    private CheckerContainer getChecker(final Module module,
                                        final ClassLoader classLoader,
                                        final ConfigurationLocation location) {
        LOG.debug("Getting CheckStyle checker with location " + location);

        try {
            return getCheckerFactory().getChecker(location, module, classLoader);

        } catch (Exception e) {
            throw new CheckStylePluginException("Couldn't create Checker", e);
        }
    }

    private ConfigurationLocation getConfigurationLocation(final Module module,
                                                           final ConfigurationLocation override) {
        if (override != null) {
            return override;
        }

        if (module != null) {
            final CheckStyleModuleConfiguration moduleConfiguration
                    = ModuleServiceManager.getService(module, CheckStyleModuleConfiguration.class);
            if (moduleConfiguration == null) {
                throw new IllegalStateException("Couldn't get checkstyle module configuration");
            }

            if (moduleConfiguration.isExcluded()) {
                return null;
            }
            return moduleConfiguration.getActiveConfiguration();

        }
        return plugin.getConfiguration().getActiveConfiguration();
    }

    private CheckerFactory getCheckerFactory() {
        return ServiceManager.getService(CheckerFactory.class);
    }

    private Configuration getConfig(final Module module, final ConfigurationLocation location) {
        LOG.debug("Getting CheckStyle config for location " + location);

        try {
            return getCheckerFactory().getConfig(location, module);

        } catch (Throwable e) {
            throw new CheckStylePluginException("Couldn't create Checker", e);
        }
    }

    private boolean isTestClass(final PsiElement element) {
        final VirtualFile elementFile = element.getContainingFile().getVirtualFile();
        if (elementFile == null) {
            return false;
        }

        final Module module = ModuleUtil.findModuleForFile(elementFile, plugin.getProject());
        if (module == null) {
            return false;
        }

        final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
        return moduleRootManager != null && moduleRootManager.getFileIndex().isInTestSourceContent(elementFile);
    }
}
TOP

Related Classes of org.infernus.idea.checkstyle.checker.FileScanner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.