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.FileReferenceHelper;
34 import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet;
35 import com.intellij.psi.search.GlobalSearchScope;
36 import com.intellij.psi.util.CachedValueProvider;
37 import com.intellij.psi.util.CachedValuesManager;
38 import com.intellij.psi.util.ParameterizedCachedValue;
39 import com.intellij.psi.util.ParameterizedCachedValueProvider;
40 import com.intellij.util.Processor;
41 import com.intellij.util.containers.ContainerUtil;
42 import com.intellij.util.containers.HashMap;
43 import com.intellij.util.containers.MultiMap;
44 import gnu.trove.THashSet;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
51 * @author Dmitry Avdeev
53 public class FileIncludeManagerImpl extends FileIncludeManager {
55 private final Project myProject;
56 private final PsiManager myPsiManager;
57 private final PsiFileFactory myPsiFileFactory;
58 private final CachedValuesManager myCachedValuesManager;
60 private final IncludeCacheHolder myIncludedHolder = new IncludeCacheHolder("compile time includes", "runtime includes") {
62 protected VirtualFile[] computeFiles(final PsiFile file, final boolean compileTimeOnly) {
63 final Set<VirtualFile> files = new THashSet<VirtualFile>();
64 processIncludes(file, new Processor<FileIncludeInfo>() {
66 public boolean process(FileIncludeInfo info) {
67 if (compileTimeOnly != info.runtimeOnly) {
68 PsiFileSystemItem item = resolveFileInclude(info, file);
70 ContainerUtil.addIfNotNull(files, item.getVirtualFile());
77 return VfsUtilCore.toVirtualFileArray(files);
80 private final Map<String, FileIncludeProvider> myProviderMap;
82 public void processIncludes(PsiFile file, Processor<FileIncludeInfo> processor) {
83 GlobalSearchScope scope = GlobalSearchScope.allScope(myProject);
84 List<FileIncludeInfoImpl> infoList = FileIncludeIndex.getIncludes(file.getVirtualFile(), scope);
85 for (FileIncludeInfoImpl info : infoList) {
86 if (!processor.process(info)) {
92 private final IncludeCacheHolder myIncludingHolder = new IncludeCacheHolder("compile time contexts", "runtime contexts") {
94 protected VirtualFile[] computeFiles(PsiFile context, boolean compileTimeOnly) {
95 final Set<VirtualFile> files = new THashSet<VirtualFile>();
96 processIncludingFiles(context, new Processor<Pair<VirtualFile, FileIncludeInfo>>() {
98 public boolean process(Pair<VirtualFile, FileIncludeInfo> virtualFileFileIncludeInfoPair) {
99 files.add(virtualFileFileIncludeInfoPair.first);
103 return VfsUtilCore.toVirtualFileArray(files);
108 public void processIncludingFiles(PsiFile context, Processor<Pair<VirtualFile, FileIncludeInfo>> processor) {
109 context = context.getOriginalFile();
110 VirtualFile contextFile = context.getVirtualFile();
111 if (contextFile == null) return;
112 MultiMap<VirtualFile,FileIncludeInfoImpl> infoList = FileIncludeIndex.getIncludingFileCandidates(context.getName(), GlobalSearchScope.allScope(myProject));
113 for (VirtualFile candidate : infoList.keySet()) {
114 PsiFile psiFile = myPsiManager.findFile(candidate);
115 if (psiFile == null || context.equals(psiFile)) continue;
116 for (FileIncludeInfo info : infoList.get(candidate)) {
117 PsiFileSystemItem item = resolveFileInclude(info, psiFile);
118 if (item != null && contextFile.equals(item.getVirtualFile())) {
119 if (!processor.process(Pair.create(candidate, info))) {
127 public FileIncludeManagerImpl(Project project, PsiManager psiManager, PsiFileFactory psiFileFactory,
128 CachedValuesManager cachedValuesManager) {
130 myPsiManager = psiManager;
131 myPsiFileFactory = psiFileFactory;
133 FileIncludeProvider[] providers = Extensions.getExtensions(FileIncludeProvider.EP_NAME);
134 myProviderMap = new HashMap<String, FileIncludeProvider>(providers.length);
135 for (FileIncludeProvider provider : providers) {
136 FileIncludeProvider old = myProviderMap.put(provider.getId(), provider);
139 myCachedValuesManager = cachedValuesManager;
143 public VirtualFile[] getIncludedFiles(@NotNull VirtualFile file, boolean compileTimeOnly) {
144 return getIncludedFiles(file, compileTimeOnly, false);
148 public VirtualFile[] getIncludedFiles(@NotNull VirtualFile file, boolean compileTimeOnly, boolean recursively) {
149 if (file instanceof VirtualFileWithId) {
150 return myIncludedHolder.getAllFiles(file, compileTimeOnly, recursively);
153 return VirtualFile.EMPTY_ARRAY;
158 public VirtualFile[] getIncludingFiles(@NotNull VirtualFile file, boolean compileTimeOnly) {
159 return myIncludingHolder.getAllFiles(file, compileTimeOnly, false);
163 public PsiFileSystemItem resolveFileInclude(@NotNull final FileIncludeInfo info, @NotNull final PsiFile context) {
164 return doResolve(info, context);
168 private PsiFileSystemItem doResolve(@NotNull final FileIncludeInfo info, @NotNull final PsiFile context) {
169 if (info instanceof FileIncludeInfoImpl) {
170 String id = ((FileIncludeInfoImpl)info).providerId;
171 FileIncludeProvider provider = id == null ? null : myProviderMap.get(id);
172 final PsiFileSystemItem resolvedByProvider = provider == null ? null : provider.resolveIncludedFile(info, context);
173 if (resolvedByProvider != null) {
174 return resolvedByProvider;
178 PsiFileImpl psiFile = (PsiFileImpl)myPsiFileFactory.createFileFromText("dummy.txt", FileTypes.PLAIN_TEXT, info.path);
179 psiFile.setOriginalFile(context);
180 return new FileReferenceSet(psiFile) {
182 protected boolean useIncludingFileAsContext() {
188 public Collection<PsiFileSystemItem> computeDefaultContexts() {
189 Collection<PsiFileSystemItem> contexts = super.computeDefaultContexts();
190 if (!isAbsolutePathReference()) {
191 return addFileDirectoryToContexts(contexts, context);
199 private static Collection<PsiFileSystemItem> addFileDirectoryToContexts(@NotNull Collection<PsiFileSystemItem> contexts,
200 @NotNull PsiFile context) {
201 VirtualFile file = context.getOriginalFile().getVirtualFile();
202 VirtualFile dir = file == null ? null : file.getParent();
204 PsiFileSystemItem item = FileReferenceHelper.getPsiFileSystemItem(context.getManager(), dir);
205 if (item != null && !contexts.contains(item)) {
206 List<PsiFileSystemItem> result = ContainerUtil.newArrayList(contexts);
214 private abstract class IncludeCacheHolder {
216 private final Key<ParameterizedCachedValue<VirtualFile[], PsiFile>> COMPILE_TIME_KEY;
217 private final Key<ParameterizedCachedValue<VirtualFile[], PsiFile>> RUNTIME_KEY;
219 private final ParameterizedCachedValueProvider<VirtualFile[], PsiFile> COMPILE_TIME_PROVIDER = new IncludedFilesProvider(true) {
221 protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) {
222 return IncludeCacheHolder.this.computeFiles(file, compileTimeOnly);
226 private final ParameterizedCachedValueProvider<VirtualFile[], PsiFile> RUNTIME_PROVIDER = new IncludedFilesProvider(false) {
228 protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) {
229 return IncludeCacheHolder.this.computeFiles(file, compileTimeOnly);
233 private IncludeCacheHolder(String compileTimeKey, String runtimeKey) {
234 COMPILE_TIME_KEY = Key.create(compileTimeKey);
235 RUNTIME_KEY = Key.create(runtimeKey);
239 private VirtualFile[] getAllFiles(@NotNull VirtualFile file, boolean compileTimeOnly, boolean recursively) {
241 Set<VirtualFile> result = new HashSet<VirtualFile>();
242 getAllFilesRecursively(file, compileTimeOnly, result);
243 return VfsUtilCore.toVirtualFileArray(result);
245 return getFiles(file, compileTimeOnly);
248 private void getAllFilesRecursively(@NotNull VirtualFile file, boolean compileTimeOnly, Set<VirtualFile> result) {
249 if (!result.add(file)) return;
250 VirtualFile[] includes = getFiles(file, compileTimeOnly);
251 if (includes.length != 0) {
252 for (VirtualFile include : includes) {
253 getAllFilesRecursively(include, compileTimeOnly, result);
258 private VirtualFile[] getFiles(@NotNull VirtualFile file, boolean compileTimeOnly) {
259 PsiFile psiFile = myPsiManager.findFile(file);
260 if (psiFile == null) {
261 return VirtualFile.EMPTY_ARRAY;
263 if (compileTimeOnly) {
264 return myCachedValuesManager.getParameterizedCachedValue(psiFile, COMPILE_TIME_KEY, COMPILE_TIME_PROVIDER, false, psiFile);
266 return myCachedValuesManager.getParameterizedCachedValue(psiFile, RUNTIME_KEY, RUNTIME_PROVIDER, false, psiFile);
269 protected abstract VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly);
273 private abstract static class IncludedFilesProvider implements ParameterizedCachedValueProvider<VirtualFile[], PsiFile> {
274 private final boolean myRuntimeOnly;
276 public IncludedFilesProvider(boolean runtimeOnly) {
277 myRuntimeOnly = runtimeOnly;
280 protected abstract VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly);
283 public CachedValueProvider.Result<VirtualFile[]> compute(PsiFile psiFile) {
284 VirtualFile[] value = computeFiles(psiFile, myRuntimeOnly);
285 // todo: we need "url modification tracker" for VirtualFile
286 List<Object> deps = new ArrayList<Object>(Arrays.asList(value));
288 deps.add(VirtualFileManager.getInstance());
290 return CachedValueProvider.Result.create(value, deps);