package org.jetbrains.android.inspections.lint; import com.android.tools.lint.client.api.LintDriver; import com.android.tools.lint.client.api.LintRequest; import com.android.tools.lint.detector.api.Issue; import com.android.tools.lint.detector.api.Scope; import com.google.common.collect.Lists; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInspection.GlobalInspectionContext; import com.intellij.codeInspection.ex.InspectionToolWrapper; import com.intellij.codeInspection.ex.Tools; import com.intellij.codeInspection.lang.GlobalInspectionContextExtension; import com.intellij.facet.ProjectFacetManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.module.impl.scopes.ModuleWithDependenciesScope; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.util.ProgressWrapper; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiFile; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.util.containers.HashMap; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.*; /** * @author Eugene.Kudelevsky */ class AndroidLintGlobalInspectionContext implements GlobalInspectionContextExtension<AndroidLintGlobalInspectionContext> { static final Key<AndroidLintGlobalInspectionContext> ID = Key.create("AndroidLintGlobalInspectionContext"); private Map<Issue, Map<File, List<ProblemData>>> myResults; @NotNull @Override public Key<AndroidLintGlobalInspectionContext> getID() { return ID; } @Override public void performPreRunActivities(@NotNull List<Tools> globalTools, @NotNull List<Tools> localTools, @NotNull final GlobalInspectionContext context) { final Project project = context.getProject(); if (!ProjectFacetManager.getInstance(project).hasFacets(AndroidFacet.ID)) { return; } final List<Issue> issues = AndroidLintExternalAnnotator.getIssuesFromInspections(project, null); if (issues.size() == 0) { return; } final Map<Issue, Map<File, List<ProblemData>>> problemMap = new HashMap<Issue, Map<File, List<ProblemData>>>(); final AnalysisScope scope = context.getRefManager().getScope(); if (scope == null) { return; } final IntellijLintClient client = IntellijLintClient.forBatch(project, problemMap, scope, issues); final LintDriver lint = new LintDriver(new IntellijLintIssueRegistry(), client); final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator != null) { ProgressWrapper.unwrap(indicator).setText("Running Android Lint"); } EnumSet<Scope> lintScope; //noinspection ConstantConditions if (!IntellijLintProject.SUPPORT_CLASS_FILES) { lintScope = EnumSet.copyOf(Scope.ALL); // Can't run class file based checks lintScope.remove(Scope.CLASS_FILE); lintScope.remove(Scope.ALL_CLASS_FILES); lintScope.remove(Scope.JAVA_LIBRARIES); } else { lintScope = Scope.ALL; } List<VirtualFile> files = null; final List<Module> modules = Lists.newArrayList(); int scopeType = scope.getScopeType(); switch (scopeType) { case AnalysisScope.MODULE: { SearchScope searchScope = scope.toSearchScope(); if (searchScope instanceof ModuleWithDependenciesScope) { ModuleWithDependenciesScope s = (ModuleWithDependenciesScope)searchScope; if (!s.isSearchInLibraries()) { modules.add(s.getModule()); } } break; } case AnalysisScope.FILE: case AnalysisScope.VIRTUAL_FILES: case AnalysisScope.UNCOMMITTED_FILES: { files = Lists.newArrayList(); SearchScope searchScope = scope.toSearchScope(); if (searchScope instanceof LocalSearchScope) { final LocalSearchScope localSearchScope = (LocalSearchScope)searchScope; final PsiElement[] elements = localSearchScope.getScope(); final List<VirtualFile> finalFiles = files; ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { for (PsiElement element : elements) { if (element instanceof PsiFile) { // should be the case since scope type is FILE Module module = ModuleUtilCore.findModuleForPsiElement(element); if (module != null && !modules.contains(module)) { modules.add(module); } VirtualFile virtualFile = ((PsiFile)element).getVirtualFile(); if (virtualFile != null) { finalFiles.add(virtualFile); } } } } }); } else { final List<VirtualFile> finalList = files; scope.accept(new PsiElementVisitor() { @Override public void visitFile(PsiFile file) { VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile != null) { finalList.add(virtualFile); } } }); } if (files.isEmpty()) { files = null; } else { // Lint will compute it lazily based on actual files in the request lintScope = null; } break; } case AnalysisScope.PROJECT: { modules.addAll(Arrays.asList(ModuleManager.getInstance(project).getModules())); break; } case AnalysisScope.CUSTOM: case AnalysisScope.MODULES: case AnalysisScope.DIRECTORY: { // Handled by the getNarrowedComplementaryScope case below break; } case AnalysisScope.INVALID: break; default: Logger.getInstance(this.getClass()).warn("Unexpected inspection scope " + scope + ", " + scopeType); } if (modules.isEmpty()) { for (Module module : ModuleManager.getInstance(project).getModules()) { if (scope.containsModule(module)) { modules.add(module); } } if (modules.isEmpty() && files != null) { for (VirtualFile file : files) { Module module = ModuleUtilCore.findModuleForFile(file, project); if (module != null && !modules.contains(module)) { modules.add(module); } } } if (modules.isEmpty()) { AnalysisScope narrowed = scope.getNarrowedComplementaryScope(project); for (Module module : ModuleManager.getInstance(project).getModules()) { if (narrowed.containsModule(module)) { modules.add(module); } } } } LintRequest request = new IntellijLintRequest(client, project, files, modules, false); request.setScope(lintScope); lint.analyze(request); myResults = problemMap; } @Nullable public Map<Issue, Map<File, List<ProblemData>>> getResults() { return myResults; } @Override public void performPostRunActivities(@NotNull List<InspectionToolWrapper> inspections, @NotNull final GlobalInspectionContext context) { } @Override public void cleanup() { } }