| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.intellij.analysis; |
| |
| import com.intellij.codeInsight.FileModificationService; |
| import com.intellij.codeInsight.daemon.ProblemHighlightFilter; |
| import com.intellij.lang.injection.InjectedLanguageManager; |
| import com.intellij.openapi.application.AccessToken; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.ReadAction; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ProjectCoreUtil; |
| import com.intellij.openapi.project.ProjectUtilCore; |
| import com.intellij.openapi.roots.*; |
| import com.intellij.openapi.roots.libraries.LibraryUtil; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.vfs.VirtualFileFilter; |
| import com.intellij.openapi.vfs.VirtualFileVisitor; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.search.GlobalSearchScopesCore; |
| import com.intellij.psi.search.LocalSearchScope; |
| import com.intellij.psi.search.SearchScope; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.Function; |
| import org.intellij.lang.annotations.MagicConstant; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.util.*; |
| |
| /** |
| * @author max |
| */ |
| public class AnalysisScope { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.analysis.AnalysisScope"); |
| |
| public static final int PROJECT = 1; |
| public static final int DIRECTORY = 2; |
| public static final int FILE = 3; |
| public static final int MODULE = 4; |
| public static final int INVALID = 6; |
| public static final int MODULES = 7; |
| public static final int CUSTOM = 8; |
| public static final int VIRTUAL_FILES = 9; |
| public static final int UNCOMMITTED_FILES = 10; |
| |
| @MagicConstant(intValues = {PROJECT, DIRECTORY, FILE, MODULE, INVALID, MODULES, CUSTOM, VIRTUAL_FILES, UNCOMMITTED_FILES}) |
| public @interface Type { } |
| |
| private final Project myProject; |
| protected List<Module> myModules; |
| protected Module myModule; |
| protected PsiElement myElement; |
| private SearchScope myScope; |
| private boolean mySearchInLibraries = false; |
| @Type protected int myType; |
| |
| private final Set<VirtualFile> myVFiles; // initial files and directories the scope is configured on |
| protected Set<VirtualFile> myFilesSet; // set of files (not directories) this scope consists of. calculated in initFilesSet() |
| |
| protected boolean myIncludeTestSource = true; |
| |
| public AnalysisScope(@NotNull Project project) { |
| myProject = project; |
| myElement = null; |
| myModules = null; |
| myModule = null; |
| myScope = null; |
| myType = PROJECT; |
| myVFiles = null; |
| } |
| |
| public AnalysisScope(@NotNull Module module) { |
| myProject = null; |
| myElement = null; |
| myModules = null; |
| myScope = null; |
| myModule = module; |
| myType = MODULE; |
| myVFiles = null; |
| } |
| |
| public AnalysisScope(@NotNull Module[] modules) { |
| myModules = Arrays.asList(modules); |
| myModule = null; |
| myProject = null; |
| myElement = null; |
| myScope = null; |
| myType = MODULES; |
| myVFiles = null; |
| } |
| |
| public AnalysisScope(@NotNull PsiDirectory psiDirectory) { |
| myProject = null; |
| myModules = null; |
| myModule = null; |
| myScope = null; |
| myElement = psiDirectory; |
| myType = DIRECTORY; |
| myVFiles = null; |
| } |
| |
| public AnalysisScope(@NotNull PsiFile psiFile) { |
| myProject = null; |
| myElement = psiFile; |
| myModule = null; |
| myModules = null; |
| myScope = null; |
| myType = FILE; |
| myVFiles = null; |
| } |
| |
| public AnalysisScope(@NotNull SearchScope scope, @NotNull Project project) { |
| myProject = project; |
| myElement = null; |
| myModule = null; |
| myModules = null; |
| myScope = scope; |
| myType = CUSTOM; |
| mySearchInLibraries = scope instanceof GlobalSearchScope && ((GlobalSearchScope)scope).isSearchInLibraries(); |
| myVFiles = null; |
| } |
| |
| public AnalysisScope(@NotNull Project project, @NotNull Collection<VirtualFile> virtualFiles) { |
| myProject = project; |
| myElement = null; |
| myModule = null; |
| myModules = null; |
| myScope = null; |
| myVFiles = new HashSet<VirtualFile>(virtualFiles); |
| myType = VIRTUAL_FILES; |
| } |
| |
| public void setScope(SearchScope scope) { |
| myScope = scope; |
| } |
| |
| public void setSearchInLibraries(final boolean searchInLibraries) { |
| mySearchInLibraries = searchInLibraries; |
| } |
| |
| public void setIncludeTestSource(final boolean includeTestSource) { |
| myIncludeTestSource = includeTestSource; |
| } |
| |
| @NotNull |
| protected PsiElementVisitor createFileSearcher() { |
| final FileIndex fileIndex; |
| if (myModule != null) { |
| fileIndex = ModuleRootManager.getInstance(myModule).getFileIndex(); |
| } |
| else if (myModules != null && !myModules.isEmpty()) { |
| fileIndex = ProjectRootManager.getInstance(myModules.get(0).getProject()).getFileIndex(); |
| } |
| else if (myElement != null) { |
| fileIndex = ProjectRootManager.getInstance(myElement.getProject()).getFileIndex(); |
| } |
| else if (myProject != null) { |
| fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); |
| } |
| else { |
| //can't be |
| fileIndex = null; |
| } |
| final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); |
| |
| return new PsiRecursiveElementVisitor() { |
| @Override |
| public void visitFile(@NotNull PsiFile file) { |
| if (/*file instanceof PsiJavaFile && */mySearchInLibraries || !(file instanceof PsiCompiledElement)) { |
| final VirtualFile virtualFile = file.getVirtualFile(); |
| if (virtualFile == null) return; |
| if (!myIncludeTestSource) { |
| if (fileIndex == null || fileIndex.isInTestSourceContent(virtualFile)) { |
| return; |
| } |
| } |
| if (!shouldHighlightFile(file)) return; |
| myFilesSet.add(virtualFile); |
| if (indicator != null) { |
| indicator.setText(AnalysisScopeBundle.message("scanning.scope.progress.title")); |
| Project project = file.getProject(); |
| String text = displayProjectRelativePath(virtualFile, project); |
| indicator.setText2(text); |
| } |
| } |
| } |
| }; |
| } |
| |
| private static String displayProjectRelativePath(@NotNull VirtualFile virtualFile, @NotNull Project project) { |
| return ProjectUtilCore.displayUrlRelativeToProject(virtualFile, virtualFile.getPresentableUrl(), project, false, false); |
| } |
| |
| public boolean contains(@NotNull PsiElement psiElement) { |
| VirtualFile file = psiElement.getContainingFile().getVirtualFile(); |
| return file != null && contains(file); |
| } |
| |
| public boolean contains(@NotNull VirtualFile file) { |
| if (myFilesSet == null) { |
| if (myType == CUSTOM) { |
| // optimization |
| if (myScope instanceof GlobalSearchScope) return ((GlobalSearchScope)myScope).contains(file); |
| if (myScope instanceof LocalSearchScope) return ((LocalSearchScope)myScope).isInScope(file); |
| } |
| if (myType == PROJECT) { //optimization |
| final ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex(); |
| return index.isInContent(file) && (myIncludeTestSource || !index.isInTestSourceContent(file)); |
| } |
| initFilesSet(); |
| } |
| |
| return myFilesSet.contains(file); |
| } |
| |
| protected void initFilesSet() { |
| if (myType == FILE) { |
| myFilesSet = new HashSet<VirtualFile>(1); |
| myFilesSet.add(((PsiFileSystemItem)myElement).getVirtualFile()); |
| } |
| else if (myType == DIRECTORY || myType == PROJECT || myType == MODULES || myType == MODULE || myType == CUSTOM) { |
| myFilesSet = new HashSet<VirtualFile>(); |
| accept(createFileSearcher()); |
| } |
| else if (myType == VIRTUAL_FILES) { |
| myFilesSet = new HashSet<VirtualFile>(); |
| final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); |
| for (Iterator<VirtualFile> iterator = myVFiles.iterator(); iterator.hasNext(); ) { |
| final VirtualFile vFile = iterator.next(); |
| VfsUtilCore.visitChildrenRecursively(vFile, new VirtualFileVisitor() { |
| @NotNull |
| @Override |
| public Result visitFileEx(@NotNull VirtualFile file) { |
| boolean ignored = fileIndex.isExcluded(file); |
| if (!ignored && !file.isDirectory()) { |
| myFilesSet.add(file); |
| } |
| return ignored ? SKIP_CHILDREN : CONTINUE; |
| } |
| }); |
| |
| if (vFile.isDirectory()) { |
| iterator.remove(); |
| } |
| } |
| } |
| } |
| |
| |
| public void accept(@NotNull final PsiElementVisitor visitor) { |
| accept(visitor, !ApplicationManager.getApplication().isReadAccessAllowed()); |
| } |
| |
| protected void accept(@NotNull final PsiElementVisitor visitor, final boolean needReadAction) { |
| if (myType == VIRTUAL_FILES) { |
| final PsiManager psiManager = PsiManager.getInstance(myProject); |
| final FileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex(); |
| for (final VirtualFile file : myFilesSet) { |
| if (!myIncludeTestSource && index.isInTestSourceContent(file)) continue; |
| if (!processFile(file, visitor, psiManager, needReadAction)) return; |
| } |
| } |
| else if (myScope instanceof GlobalSearchScope) { |
| final PsiManager psiManager = PsiManager.getInstance(myProject); |
| final FileIndex projectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); |
| final ContentIterator contentIterator = new ContentIterator() { |
| @Override |
| public boolean processFile(@NotNull final VirtualFile fileOrDir) { |
| final boolean isInScope = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { |
| @Override |
| public Boolean compute() { |
| if (!myIncludeTestSource && projectFileIndex.isInTestSourceContent(fileOrDir)) return false; |
| if (isInGeneratedSources(fileOrDir, myProject)) return false; |
| return ((GlobalSearchScope)myScope).contains(fileOrDir); |
| } |
| }).booleanValue(); |
| return !isInScope || AnalysisScope.processFile(fileOrDir, visitor, psiManager, needReadAction); |
| } |
| }; |
| projectFileIndex.iterateContent(contentIterator); |
| if (mySearchInLibraries) { |
| final VirtualFile[] libraryRoots = LibraryUtil.getLibraryRoots(myProject, false, false); |
| for (VirtualFile libraryRoot : libraryRoots) { |
| VfsUtilCore.iterateChildrenRecursively(libraryRoot, VirtualFileFilter.ALL, contentIterator); |
| } |
| } |
| } |
| else if (myScope instanceof LocalSearchScope) { |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| @Override |
| public void run() { |
| final PsiElement[] psiElements = ((LocalSearchScope)myScope).getScope(); |
| final Set<PsiFile> files = new LinkedHashSet<PsiFile>(); |
| for (PsiElement element : psiElements) { |
| final PsiFile file = element.getContainingFile(); |
| if (file != null && files.add(file)) { |
| file.accept(visitor); |
| } |
| } |
| } |
| }); |
| } |
| else if (myModule != null) { |
| final FileIndex moduleFileIndex = ModuleRootManager.getInstance(myModule).getFileIndex(); |
| final PsiManager psiManager = PsiManager.getInstance(myModule.getProject()); |
| moduleFileIndex.iterateContent(new ContentIterator() { |
| @Override |
| public boolean processFile(@NotNull VirtualFile fileOrDir) { |
| return AnalysisScope.this.processFile(fileOrDir, visitor, moduleFileIndex, psiManager, needReadAction); |
| } |
| }); |
| } |
| else if (myModules != null) { |
| for (final Module module : myModules) { |
| final PsiManager psiManager = PsiManager.getInstance(module.getProject()); |
| final FileIndex moduleFileIndex = ModuleRootManager.getInstance(module).getFileIndex(); |
| moduleFileIndex.iterateContent(new ContentIterator() { |
| @Override |
| public boolean processFile(@NotNull VirtualFile fileOrDir) { |
| return AnalysisScope.this.processFile(fileOrDir, visitor, moduleFileIndex, psiManager, needReadAction); |
| } |
| }); |
| } |
| } |
| else if (myElement instanceof PsiDirectory) { |
| accept((PsiDirectory)myElement, visitor, needReadAction); |
| } |
| else if (myElement != null) { |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| @Override |
| public void run() { |
| myElement.accept(visitor); |
| } |
| }); |
| } |
| else if (myProject != null) { |
| final PsiManager psiManager = PsiManager.getInstance(myProject); |
| final FileIndex projectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); |
| projectFileIndex.iterateContent(new ContentIterator() { |
| @Override |
| public boolean processFile(@NotNull final VirtualFile fileOrDir) { |
| return AnalysisScope.this.processFile(fileOrDir, visitor, projectFileIndex, psiManager, needReadAction); |
| } |
| }); |
| } |
| } |
| |
| @SuppressWarnings({"SimplifiableIfStatement"}) |
| private boolean processFile(@NotNull final VirtualFile fileOrDir, |
| @NotNull final PsiElementVisitor visitor, |
| @NotNull final FileIndex projectFileIndex, |
| @NotNull final PsiManager psiManager, |
| final boolean needReadAction) { |
| if (fileOrDir.isDirectory()) return true; |
| if (ProjectCoreUtil.isProjectOrWorkspaceFile(fileOrDir)) return true; |
| if (projectFileIndex.isInContent(fileOrDir) && (myIncludeTestSource || !projectFileIndex.isInTestSourceContent(fileOrDir)) |
| && !isInGeneratedSources(fileOrDir, psiManager.getProject())) { |
| return processFile(fileOrDir, visitor, psiManager, needReadAction); |
| } |
| return true; |
| } |
| |
| private static boolean isInGeneratedSources(@NotNull VirtualFile file, @NotNull Project project) { |
| for (GeneratedSourcesFilter filter : GeneratedSourcesFilter.EP_NAME.getExtensions()) { |
| if (filter.isGeneratedSource(file, project)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static boolean processFile(@NotNull final VirtualFile fileOrDir, |
| @NotNull final PsiElementVisitor visitor, |
| @NotNull final PsiManager psiManager, |
| final boolean needReadAction) { |
| final PsiFile file; |
| |
| AccessToken accessToken = ReadAction.start(); |
| try { |
| if (!fileOrDir.isValid()) return false; |
| |
| file = psiManager.findFile(fileOrDir); |
| if (file == null) { |
| //skip .class files under src directory |
| return true; |
| } |
| |
| if (!shouldHighlightFile(file)) return true; |
| } |
| finally { |
| accessToken.finish(); |
| } |
| |
| if (needReadAction) { |
| PsiDocumentManager.getInstance(psiManager.getProject()).commitAndRunReadAction(new Runnable(){ |
| @Override |
| public void run() { |
| doProcessFile(visitor, psiManager, file); |
| } |
| }); |
| } |
| else { |
| doProcessFile(visitor, psiManager, file); |
| } |
| final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); |
| return indicator == null || !indicator.isCanceled(); |
| } |
| |
| protected static boolean shouldHighlightFile(@NotNull PsiFile file) { |
| return ProblemHighlightFilter.shouldProcessFileInBatch(file); |
| } |
| |
| public boolean containsModule(Module module) { |
| switch (myType) { |
| case PROJECT: |
| return true; |
| case MODULE: |
| return myModule == module; |
| case MODULES: |
| return myModules.contains(module); |
| default: |
| return false; |
| } |
| } |
| |
| private static void doProcessFile(@NotNull PsiElementVisitor visitor, @NotNull PsiManager psiManager, @NotNull PsiFile file) { |
| file.accept(visitor); |
| psiManager.dropResolveCaches(); |
| InjectedLanguageManager.getInstance(file.getProject()).dropFileCaches(file); |
| } |
| |
| protected void accept(@NotNull final PsiDirectory dir, @NotNull final PsiElementVisitor visitor, final boolean needReadAction) { |
| final Project project = dir.getProject(); |
| final PsiManager psiManager = PsiManager.getInstance(project); |
| final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex(); |
| //we should analyze generated source files only if the action is explicitly invoked for a directory located under generated roots |
| final boolean processGeneratedFiles = isInGeneratedSources(dir.getVirtualFile(), project); |
| VfsUtilCore.iterateChildrenRecursively(dir.getVirtualFile(), VirtualFileFilter.ALL, new ContentIterator() { |
| @Override |
| @SuppressWarnings({"SimplifiableIfStatement"}) |
| public boolean processFile(@NotNull final VirtualFile fileOrDir) { |
| if (!myIncludeTestSource && index.isInTestSourceContent(fileOrDir)) return true; |
| if (!processGeneratedFiles && isInGeneratedSources(fileOrDir, project)) return true; |
| if (!fileOrDir.isDirectory()) { |
| return AnalysisScope.processFile(fileOrDir, visitor, psiManager, needReadAction); |
| } |
| return true; |
| } |
| }); |
| } |
| |
| public boolean isValid() { |
| if (myProject != null) return true; |
| if (myModule != null && !myModule.isDisposed()) return true; |
| if (myModules != null){ |
| for (Module module : myModules) { |
| if (module.isDisposed()) return false; |
| } |
| return true; |
| } |
| return myType == VIRTUAL_FILES || myType == CUSTOM || myElement != null && myElement.isValid(); |
| } |
| |
| @Type |
| public int getScopeType() { |
| return myType; |
| } |
| |
| @NotNull |
| public String getDisplayName() { |
| switch (myType) { |
| case CUSTOM: |
| return myScope.getDisplayName(); |
| |
| case MODULE: |
| return AnalysisScopeBundle.message("scope.option.module", pathToName(myModule.getModuleFilePath())); |
| |
| case MODULES: |
| String modules = StringUtil.join(myModules, new Function<Module, String>() { |
| @Override |
| public String fun(@NotNull final Module module) { |
| return pathToName(module.getModuleFilePath()); |
| } |
| }, ", "); |
| |
| return AnalysisScopeBundle.message("scope.module.list", modules, Integer.valueOf(myModules.size())); |
| |
| case PROJECT: |
| return AnalysisScopeBundle.message("scope.project", myProject.getName()); |
| |
| case FILE: |
| return AnalysisScopeBundle.message("scope.file", getPresentableUrl((PsiFileSystemItem)myElement)); |
| |
| case DIRECTORY: |
| return AnalysisScopeBundle.message("scope.directory", getPresentableUrl((PsiFileSystemItem)myElement)); |
| |
| case VIRTUAL_FILES: |
| return AnalysisScopeBundle.message("scope.virtual.files"); |
| } |
| |
| return ""; |
| } |
| |
| @NotNull |
| private static String getPresentableUrl(@NotNull final PsiFileSystemItem element) { |
| final VirtualFile virtualFile = element.getVirtualFile(); |
| assert virtualFile != null : element; |
| return virtualFile.getPresentableUrl(); |
| } |
| |
| @NotNull |
| public String getShortenName(){ |
| switch (myType) { |
| case CUSTOM: |
| return myScope.getDisplayName(); |
| |
| case MODULE: |
| return AnalysisScopeBundle.message("scope.option.module", myModule.getName()); |
| |
| case MODULES: |
| String modules = StringUtil.join(myModules, new Function<Module, String>() { |
| @Override |
| @NotNull |
| public String fun(@NotNull final Module module) { |
| return module.getName(); |
| } |
| }, ", "); |
| return AnalysisScopeBundle.message("scope.module.list", modules, Integer.valueOf(myModules.size())); |
| |
| case PROJECT: |
| return AnalysisScopeBundle.message("scope.project", myProject.getName()); |
| |
| case FILE: |
| final String relativePath = getRelativePath(); |
| return relativePath != null ? AnalysisScopeBundle.message("scope.file", relativePath) : "Current File"; |
| |
| case DIRECTORY: |
| final String relativeDirPath = getRelativePath(); |
| return relativeDirPath != null ? AnalysisScopeBundle.message("scope.directory", relativeDirPath) : "Current Directory"; |
| |
| |
| case VIRTUAL_FILES: |
| return AnalysisScopeBundle.message("scope.selected.files"); |
| } |
| |
| return ""; |
| } |
| |
| @Nullable |
| private String getRelativePath() { |
| final String relativePath = displayProjectRelativePath(((PsiFileSystemItem)myElement).getVirtualFile(), myElement.getProject()); |
| if (relativePath.length() > 100) { |
| return null; |
| } |
| return relativePath; |
| } |
| |
| @NotNull |
| private static String pathToName(@NotNull String path) { |
| File file = new File(path); |
| return FileUtil.getNameWithoutExtension(file); |
| } |
| |
| public int getFileCount() { |
| if (myFilesSet == null) initFilesSet(); |
| final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); |
| if (indicator != null) { //clear text after building analysis scope set |
| indicator.setText(""); |
| indicator.setText2(""); |
| } |
| return myFilesSet.size(); |
| } |
| |
| public boolean checkScopeWritable(@NotNull Project project) { |
| if (myFilesSet == null) initFilesSet(); |
| return !FileModificationService.getInstance().prepareVirtualFilesForWrite(project, myFilesSet); |
| } |
| |
| public void invalidate(){ |
| if (myType == VIRTUAL_FILES) { |
| for (Iterator<VirtualFile> i = myVFiles.iterator(); i.hasNext();) { |
| final VirtualFile virtualFile = i.next(); |
| if (virtualFile == null || !virtualFile.isValid()) { |
| i.remove(); |
| } |
| } |
| } |
| else { |
| myFilesSet = null; |
| } |
| } |
| |
| public boolean containsSources(boolean isTest) { |
| if (myElement != null) { |
| final Project project = myElement.getProject(); |
| final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex(); |
| if (myElement instanceof PsiDirectory) { |
| final VirtualFile directory = ((PsiFileSystemItem)myElement).getVirtualFile(); |
| if (index.isInSourceContent(directory)) { |
| return isTest ? index.isInTestSourceContent(directory) : !index.isInTestSourceContent(directory); |
| } |
| } else if (myElement instanceof PsiFile) { |
| final VirtualFile file = ((PsiFileSystemItem)myElement).getVirtualFile(); |
| if (file != null) { |
| return isTest ? index.isInTestSourceContent(file) : !index.isInTestSourceContent(file); |
| } |
| } |
| } |
| return true; |
| } |
| |
| @NotNull |
| public AnalysisScope getNarrowedComplementaryScope(@NotNull Project defaultProject) { |
| final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(defaultProject).getFileIndex(); |
| final HashSet<Module> modules = new HashSet<Module>(); |
| if (myType == FILE || myType == DIRECTORY) { |
| final VirtualFile vFile = ((PsiFileSystemItem)myElement).getVirtualFile(); |
| modules.addAll(getAllInterestingModules(fileIndex, vFile)); |
| } |
| else if (myType == MODULE) { |
| modules.add(myModule); |
| } |
| else if (myType == MODULES) { |
| modules.addAll(myModules); |
| } |
| return collectScopes(defaultProject, modules); |
| } |
| |
| @NotNull |
| protected static AnalysisScope collectScopes(@NotNull final Project defaultProject, @NotNull final HashSet<Module> modules) { |
| if (modules.isEmpty()) { |
| return new AnalysisScope(defaultProject); |
| } |
| final Module[] allModules = ModuleManager.getInstance(defaultProject).getModules(); |
| Set<Module> modulesToAnalyze = new HashSet<Module>(); |
| for (final Module module : modules) { |
| modulesToAnalyze.addAll(getDirectBackwardDependencies(module, allModules)); |
| modulesToAnalyze.addAll(getExportBackwardDependencies(module, allModules)); |
| modulesToAnalyze.add(module); |
| } |
| return new AnalysisScope(modulesToAnalyze.toArray(new Module[modulesToAnalyze.size()])); |
| } |
| |
| @NotNull |
| private static Set<Module> getExportBackwardDependencies(@NotNull Module fromModule, @NotNull Module[] allModules) { |
| Set<Module> result = new HashSet<Module>(); |
| for (Module module : allModules) { |
| final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); |
| final OrderEntry[] orderEntries = moduleRootManager.getOrderEntries(); |
| for (OrderEntry orderEntry : orderEntries) { |
| if (orderEntry instanceof ModuleOrderEntry && ((ExportableOrderEntry)orderEntry).isExported() && |
| fromModule == ((ModuleOrderEntry)orderEntry).getModule()) { |
| result.addAll(getDirectBackwardDependencies(module, allModules)); |
| } |
| } |
| } |
| return result; |
| } |
| |
| @NotNull |
| private static Set<Module> getDirectBackwardDependencies(@NotNull Module module, @NotNull Module[] allModules) { |
| Set<Module> result = new HashSet<Module>(); |
| for (Module dependency : allModules) { |
| if (ArrayUtil.find(ModuleRootManager.getInstance(dependency).getDependencies(), module) > -1) { |
| result.add(dependency); |
| } |
| } |
| return result; |
| } |
| |
| @NotNull |
| protected static HashSet<Module> getAllInterestingModules(@NotNull final ProjectFileIndex fileIndex, @NotNull final VirtualFile vFile) { |
| final HashSet<Module> modules = new HashSet<Module>(); |
| if (fileIndex.isInLibrarySource(vFile) || fileIndex.isInLibraryClasses(vFile)) { |
| for (OrderEntry orderEntry : fileIndex.getOrderEntriesForFile(vFile)) { |
| modules.add(orderEntry.getOwnerModule()); |
| } |
| } |
| else { |
| modules.add(fileIndex.getModuleForFile(vFile)); |
| } |
| return modules; |
| } |
| |
| @NotNull |
| public SearchScope toSearchScope() { |
| switch (myType) { |
| case CUSTOM: |
| return myScope; |
| case DIRECTORY: |
| return GlobalSearchScopesCore.directoryScope((PsiDirectory)myElement, true); |
| case FILE: |
| return new LocalSearchScope(myElement); |
| case INVALID: |
| return LocalSearchScope.EMPTY; |
| case MODULE: |
| GlobalSearchScope moduleScope = GlobalSearchScope.moduleScope(myModule); |
| return myIncludeTestSource ? moduleScope : GlobalSearchScope.notScope(GlobalSearchScopesCore.projectTestScope(myModule.getProject())).intersectWith(moduleScope); |
| case MODULES: |
| SearchScope scope = GlobalSearchScope.EMPTY_SCOPE; |
| for (Module module : myModules) { |
| scope = scope.union(GlobalSearchScope.moduleScope(module)); |
| } |
| return scope; |
| case PROJECT: |
| return myIncludeTestSource ? GlobalSearchScope.projectScope(myProject) : GlobalSearchScopesCore.projectProductionScope(myProject); |
| case VIRTUAL_FILES: |
| return new GlobalSearchScope() { |
| @Override |
| public boolean contains(@NotNull VirtualFile file) { |
| return myFilesSet.contains(file); |
| } |
| |
| @Override |
| public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) { |
| return 0; |
| } |
| |
| @Override |
| public boolean isSearchInModuleContent(@NotNull Module aModule) { |
| return false; |
| } |
| |
| @Override |
| public boolean isSearchInLibraries() { |
| return false; |
| } |
| }; |
| default: |
| LOG.error("invalid type " + myType); |
| return GlobalSearchScope.EMPTY_SCOPE; |
| } |
| } |
| |
| public boolean isAnalyzeTestsByDefault() { |
| switch (myType) { |
| case DIRECTORY: |
| return ProjectRootManager.getInstance(myElement.getProject()).getFileIndex() |
| .isInTestSourceContent(((PsiDirectory)myElement).getVirtualFile()); |
| case FILE: |
| final PsiFile containingFile = myElement.getContainingFile(); |
| return ProjectRootManager.getInstance(myElement.getProject()).getFileIndex().isInTestSourceContent(containingFile.getVirtualFile()); |
| case MODULE: |
| return isTestOnly(myModule); |
| case MODULES: |
| for (Module module : myModules) { |
| if (!isTestOnly(module)) return false; |
| } |
| return true; |
| |
| } |
| return false; |
| } |
| |
| private static boolean isTestOnly(@NotNull Module module) { |
| return ModuleRootManager.getInstance(module).getSourceRootUrls(false).length == 0; |
| } |
| |
| public boolean isIncludeTestSource() { |
| return myIncludeTestSource; |
| } |
| } |