2 * Copyright 2000-2013 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.psi.impl.include;
19 import com.intellij.openapi.extensions.Extensions;
20 import com.intellij.openapi.fileTypes.FileTypes;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.util.Key;
23 import com.intellij.openapi.util.Pair;
24 import com.intellij.openapi.vfs.VfsUtilCore;
25 import com.intellij.openapi.vfs.VirtualFile;
26 import com.intellij.openapi.vfs.VirtualFileManager;
27 import com.intellij.openapi.vfs.VirtualFileWithId;
28 import com.intellij.psi.PsiFile;
29 import com.intellij.psi.PsiFileFactory;
30 import com.intellij.psi.PsiFileSystemItem;
31 import com.intellij.psi.PsiManager;
32 import com.intellij.psi.impl.source.PsiFileImpl;
33 import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet;
34 import com.intellij.psi.search.GlobalSearchScope;
35 import com.intellij.psi.util.CachedValueProvider;
36 import com.intellij.psi.util.CachedValuesManager;
37 import com.intellij.psi.util.ParameterizedCachedValue;
38 import com.intellij.psi.util.ParameterizedCachedValueProvider;
39 import com.intellij.util.Processor;
40 import com.intellij.util.containers.ContainerUtil;
41 import com.intellij.util.containers.HashMap;
42 import com.intellij.util.containers.MultiMap;
43 import gnu.trove.THashSet;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
50 * @author Dmitry Avdeev
52 public class FileIncludeManagerImpl extends FileIncludeManager {
54 private final Project myProject;
55 private final PsiManager myPsiManager;
56 private final PsiFileFactory myPsiFileFactory;
57 private final CachedValuesManager myCachedValuesManager;
59 private final IncludeCacheHolder myIncludedHolder = new IncludeCacheHolder("compile time includes", "runtime includes") {
61 protected VirtualFile[] computeFiles(final PsiFile file, final boolean compileTimeOnly) {
62 final Set<VirtualFile> files = new THashSet<VirtualFile>();
63 processIncludes(file, new Processor<FileIncludeInfo>() {
65 public boolean process(FileIncludeInfo info) {
66 if (compileTimeOnly != info.runtimeOnly) {
67 PsiFileSystemItem item = resolveFileInclude(info, file);
69 ContainerUtil.addIfNotNull(files, item.getVirtualFile());
76 return VfsUtilCore.toVirtualFileArray(files);
79 private final Map<String, FileIncludeProvider> myProviderMap;
81 public void processIncludes(PsiFile file, Processor<FileIncludeInfo> processor) {
82 GlobalSearchScope scope = GlobalSearchScope.allScope(myProject);
83 List<FileIncludeInfoImpl> infoList = FileIncludeIndex.getIncludes(file.getVirtualFile(), scope);
84 for (FileIncludeInfoImpl info : infoList) {
85 if (!processor.process(info)) {
91 private final IncludeCacheHolder myIncludingHolder = new IncludeCacheHolder("compile time contexts", "runtime contexts") {
93 protected VirtualFile[] computeFiles(PsiFile context, boolean compileTimeOnly) {
94 final Set<VirtualFile> files = new THashSet<VirtualFile>();
95 processIncludingFiles(context, new Processor<Pair<VirtualFile, FileIncludeInfo>>() {
97 public boolean process(Pair<VirtualFile, FileIncludeInfo> virtualFileFileIncludeInfoPair) {
98 files.add(virtualFileFileIncludeInfoPair.first);
102 return VfsUtilCore.toVirtualFileArray(files);
107 public void processIncludingFiles(PsiFile context, Processor<Pair<VirtualFile, FileIncludeInfo>> processor) {
108 context = context.getOriginalFile();
109 VirtualFile contextFile = context.getVirtualFile();
110 if (contextFile == null) return;
111 MultiMap<VirtualFile,FileIncludeInfoImpl> infoList = FileIncludeIndex.getIncludingFileCandidates(context.getName(), GlobalSearchScope.allScope(myProject));
112 for (VirtualFile candidate : infoList.keySet()) {
113 PsiFile psiFile = myPsiManager.findFile(candidate);
114 if (psiFile == null || context.equals(psiFile)) continue;
115 for (FileIncludeInfo info : infoList.get(candidate)) {
116 PsiFileSystemItem item = resolveFileInclude(info, psiFile);
117 if (item != null && contextFile.equals(item.getVirtualFile())) {
118 if (!processor.process(Pair.create(candidate, info))) {
126 public FileIncludeManagerImpl(Project project, PsiManager psiManager, PsiFileFactory psiFileFactory,
127 CachedValuesManager cachedValuesManager) {
129 myPsiManager = psiManager;
130 myPsiFileFactory = psiFileFactory;
132 FileIncludeProvider[] providers = Extensions.getExtensions(FileIncludeProvider.EP_NAME);
133 myProviderMap = new HashMap<String, FileIncludeProvider>(providers.length);
134 for (FileIncludeProvider provider : providers) {
135 FileIncludeProvider old = myProviderMap.put(provider.getId(), provider);
138 myCachedValuesManager = cachedValuesManager;
142 public VirtualFile[] getIncludedFiles(@NotNull VirtualFile file, boolean compileTimeOnly) {
143 return getIncludedFiles(file, compileTimeOnly, false);
147 public VirtualFile[] getIncludedFiles(@NotNull VirtualFile file, boolean compileTimeOnly, boolean recursively) {
148 if (file instanceof VirtualFileWithId) {
149 return myIncludedHolder.getAllFiles(file, compileTimeOnly, recursively);
152 return VirtualFile.EMPTY_ARRAY;
157 public VirtualFile[] getIncludingFiles(@NotNull VirtualFile file, boolean compileTimeOnly) {
158 return myIncludingHolder.getAllFiles(file, compileTimeOnly, false);
162 public PsiFileSystemItem resolveFileInclude(@NotNull final FileIncludeInfo info, @NotNull final PsiFile context) {
163 return doResolve(info, context);
167 private PsiFileSystemItem doResolve(@NotNull final FileIncludeInfo info, @NotNull final PsiFile context) {
168 if (info instanceof FileIncludeInfoImpl) {
169 String id = ((FileIncludeInfoImpl)info).providerId;
170 FileIncludeProvider provider = id == null ? null : myProviderMap.get(id);
171 final PsiFileSystemItem resolvedByProvider = provider == null ? null : provider.resolveIncludedFile(info, context);
172 if (resolvedByProvider != null) {
173 return resolvedByProvider;
177 PsiFileImpl psiFile = (PsiFileImpl)myPsiFileFactory.createFileFromText("dummy.txt", FileTypes.PLAIN_TEXT, info.path);
178 psiFile.setOriginalFile(context);
179 return new FileReferenceSet(psiFile) {
181 protected boolean useIncludingFileAsContext() {
187 private abstract class IncludeCacheHolder {
189 private final Key<ParameterizedCachedValue<VirtualFile[], PsiFile>> COMPILE_TIME_KEY;
190 private final Key<ParameterizedCachedValue<VirtualFile[], PsiFile>> RUNTIME_KEY;
192 private final ParameterizedCachedValueProvider<VirtualFile[], PsiFile> COMPILE_TIME_PROVIDER = new IncludedFilesProvider(true) {
194 protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) {
195 return IncludeCacheHolder.this.computeFiles(file, compileTimeOnly);
199 private final ParameterizedCachedValueProvider<VirtualFile[], PsiFile> RUNTIME_PROVIDER = new IncludedFilesProvider(false) {
201 protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) {
202 return IncludeCacheHolder.this.computeFiles(file, compileTimeOnly);
206 private IncludeCacheHolder(String compileTimeKey, String runtimeKey) {
207 COMPILE_TIME_KEY = Key.create(compileTimeKey);
208 RUNTIME_KEY = Key.create(runtimeKey);
212 private VirtualFile[] getAllFiles(@NotNull VirtualFile file, boolean compileTimeOnly, boolean recursively) {
214 Set<VirtualFile> result = new HashSet<VirtualFile>();
215 getAllFilesRecursively(file, compileTimeOnly, result);
216 return VfsUtilCore.toVirtualFileArray(result);
218 return getFiles(file, compileTimeOnly);
221 private void getAllFilesRecursively(@NotNull VirtualFile file, boolean compileTimeOnly, Set<VirtualFile> result) {
222 if (!result.add(file)) return;
223 VirtualFile[] includes = getFiles(file, compileTimeOnly);
224 if (includes.length != 0) {
225 for (VirtualFile include : includes) {
226 getAllFilesRecursively(include, compileTimeOnly, result);
231 private VirtualFile[] getFiles(@NotNull VirtualFile file, boolean compileTimeOnly) {
232 PsiFile psiFile = myPsiManager.findFile(file);
233 if (psiFile == null) {
234 return VirtualFile.EMPTY_ARRAY;
236 if (compileTimeOnly) {
237 return myCachedValuesManager.getParameterizedCachedValue(psiFile, COMPILE_TIME_KEY, COMPILE_TIME_PROVIDER, false, psiFile);
239 return myCachedValuesManager.getParameterizedCachedValue(psiFile, RUNTIME_KEY, RUNTIME_PROVIDER, false, psiFile);
242 protected abstract VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly);
246 private abstract static class IncludedFilesProvider implements ParameterizedCachedValueProvider<VirtualFile[], PsiFile> {
247 private final boolean myRuntimeOnly;
249 public IncludedFilesProvider(boolean runtimeOnly) {
250 myRuntimeOnly = runtimeOnly;
253 protected abstract VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly);
256 public CachedValueProvider.Result<VirtualFile[]> compute(PsiFile psiFile) {
257 VirtualFile[] value = computeFiles(psiFile, myRuntimeOnly);
258 // todo: we need "url modification tracker" for VirtualFile
259 List<Object> deps = new ArrayList<Object>(Arrays.asList(value));
261 deps.add(VirtualFileManager.getInstance());
263 return CachedValueProvider.Result.create(value, deps);