/* * Copyright 2000-2009 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.packageDependencies; import com.intellij.analysis.AnalysisScope; import com.intellij.analysis.AnalysisScopeBundle; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class ForwardDependenciesBuilder extends DependenciesBuilder { private final Map<PsiFile, Set<PsiFile>> myDirectDependencies = new HashMap<PsiFile, Set<PsiFile>>(); public ForwardDependenciesBuilder(@NotNull Project project, @NotNull AnalysisScope scope) { super(project, scope); } public ForwardDependenciesBuilder(final Project project, final AnalysisScope scope, final AnalysisScope scopeOfInterest) { super(project, scope, scopeOfInterest); } public ForwardDependenciesBuilder(final Project project, final AnalysisScope scope, final int transitive) { super(project, scope); myTransitive = transitive; } @Override public String getRootNodeNameInUsageView(){ return AnalysisScopeBundle.message("forward.dependencies.usage.view.root.node.text"); } @Override public String getInitialUsagesPosition(){ return AnalysisScopeBundle.message("forward.dependencies.usage.view.initial.text"); } @Override public boolean isBackward(){ return false; } @Override public void analyze() { final PsiManager psiManager = PsiManager.getInstance(getProject()); psiManager.startBatchFilesProcessingMode(); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(getProject()).getFileIndex(); try { getScope().accept(new PsiRecursiveElementVisitor() { @Override public void visitFile(final PsiFile file) { visit(file, fileIndex, psiManager, 0); } }); } finally { psiManager.finishBatchFilesProcessingMode(); } } private void visit(final PsiFile file, final ProjectFileIndex fileIndex, final PsiManager psiManager, int depth) { final FileViewProvider viewProvider = file.getViewProvider(); if (viewProvider.getBaseLanguage() != file.getLanguage()) return; if (getScopeOfInterest() != null && !getScopeOfInterest().contains(file)) return; ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); final VirtualFile virtualFile = file.getVirtualFile(); if (indicator != null) { if (indicator.isCanceled()) { throw new ProcessCanceledException(); } indicator.setText(AnalysisScopeBundle.message("package.dependencies.progress.text")); if (virtualFile != null) { indicator.setText2(ProjectUtil.calcRelativeToProjectPath(virtualFile, getProject())); } if ( myTotalFileCount > 0) { indicator.setFraction(((double)++ myFileCount) / myTotalFileCount); } } final boolean isInLibrary = virtualFile == null || fileIndex.isInLibrarySource(virtualFile) || fileIndex.isInLibraryClasses(virtualFile); final Set<PsiFile> collectedDeps = new HashSet<PsiFile>(); final HashSet<PsiFile> processed = new HashSet<PsiFile>(); collectedDeps.add(file); do { if (depth++ > getTransitiveBorder()) return; for (PsiFile psiFile : new HashSet<PsiFile>(collectedDeps)) { final VirtualFile vFile = psiFile.getVirtualFile(); if (vFile != null) { if (indicator != null) { indicator.setText2(ProjectUtil.calcRelativeToProjectPath(vFile, getProject())); } if (!isInLibrary && (fileIndex.isInLibraryClasses(vFile) || fileIndex.isInLibrarySource(vFile))) { processed.add(psiFile); } } final Set<PsiFile> found = new HashSet<PsiFile>(); if (!processed.contains(psiFile)) { processed.add(psiFile); analyzeFileDependencies(psiFile, new DependencyProcessor() { @Override public void process(PsiElement place, PsiElement dependency) { PsiFile dependencyFile = dependency.getContainingFile(); if (dependencyFile != null) { if (viewProvider == dependencyFile.getViewProvider()) return; if (dependencyFile.isPhysical()) { final VirtualFile virtualFile = dependencyFile.getVirtualFile(); if (virtualFile != null && (fileIndex.isInContent(virtualFile) || fileIndex.isInLibraryClasses(virtualFile) || fileIndex.isInLibrarySource(virtualFile))) { found.add(dependencyFile); } } } } }); Set<PsiFile> deps = getDependencies().get(file); if (deps == null) { deps = new HashSet<PsiFile>(); getDependencies().put(file, deps); } deps.addAll(found); getDirectDependencies().put(psiFile, new HashSet<PsiFile>(found)); collectedDeps.addAll(found); psiManager.dropResolveCaches(); InjectedLanguageManager.getInstance(file.getProject()).dropFileCaches(file); } } collectedDeps.removeAll(processed); } while (isTransitive() && !collectedDeps.isEmpty()); } @Override public Map<PsiFile, Set<PsiFile>> getDirectDependencies() { return myDirectDependencies; } }