update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / psi / impl / file / impl / JavaFileManagerImpl.java
blob3f9fd2780ecfd05651b4c4302bcab12fba3b954d
1 /*
2 * Copyright 2000-2009 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.
18 * @author max
20 package com.intellij.psi.impl.file.impl;
22 import com.intellij.AppTopics;
23 import com.intellij.ProjectTopics;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
26 import com.intellij.openapi.fileTypes.StdFileTypes;
27 import com.intellij.openapi.roots.*;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.openapi.vfs.VirtualFileManager;
30 import com.intellij.openapi.vfs.newvfs.BulkFileListener;
31 import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
32 import com.intellij.psi.*;
33 import com.intellij.psi.impl.PsiManagerEx;
34 import com.intellij.psi.impl.file.PsiPackageImpl;
35 import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex;
36 import com.intellij.psi.search.GlobalSearchScope;
37 import com.intellij.psi.util.PsiUtil;
38 import com.intellij.util.Query;
39 import com.intellij.util.containers.ConcurrentHashMap;
40 import com.intellij.util.containers.ContainerUtil;
41 import com.intellij.util.indexing.FileBasedIndex;
42 import com.intellij.util.messages.MessageBus;
43 import com.intellij.util.messages.MessageBusConnection;
44 import org.jetbrains.annotations.NonNls;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
48 import java.util.*;
50 public class JavaFileManagerImpl implements JavaFileManager {
51 private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.file.impl.JavaFileManagerImpl");
52 private final ConcurrentHashMap<GlobalSearchScope, PsiClass> myCachedObjectClassMap = new ConcurrentHashMap<GlobalSearchScope, PsiClass>();
53 private final Map<String,PsiClass> myNameToClassMap = new ConcurrentHashMap<String, PsiClass>(); // used only in mode without repository
54 @NonNls private static final String JAVA_EXTENSION = ".java";
55 @NonNls private static final String CLASS_EXTENSION = ".class";
56 private final PsiManagerEx myManager;
57 private final ProjectRootManager myProjectRootManager;
58 private final FileManager myFileManager;
59 private final boolean myUseRepository;
60 private Set<String> myNontrivialPackagePrefixes = null;
61 private boolean myInitialized = false;
62 private boolean myDisposed = false;
63 private final PackageIndex myPackageIndex;
66 public JavaFileManagerImpl(final PsiManagerEx manager, final ProjectRootManager projectRootManager, FileManager fileManager, MessageBus bus) {
67 myManager = manager;
68 myProjectRootManager = projectRootManager;
69 myFileManager = fileManager;
71 myUseRepository = true;
73 myManager.registerRunnableToRunOnChange(new Runnable() {
74 public void run() {
75 myCachedObjectClassMap.clear();
77 });
79 final MessageBusConnection connection = bus.connect();
80 connection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
81 public void beforeRootsChange(final ModuleRootEvent event) {
84 public void rootsChanged(final ModuleRootEvent event) {
85 myNontrivialPackagePrefixes = null;
86 clearNonRepositoryMaps();
88 });
90 connection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() {
91 public void before(final List<? extends VFileEvent> events) {
92 clearNonRepositoryMaps();
95 public void after(final List<? extends VFileEvent> events) {
96 clearNonRepositoryMaps();
98 });
100 connection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() {
101 public void fileWithNoDocumentChanged(final VirtualFile file) {
102 clearNonRepositoryMaps();
106 myPackageIndex = PackageIndex.getInstance(myManager.getProject());
109 public void initialize() {
110 myInitialized = true;
113 public void dispose() {
114 myDisposed = true;
115 myCachedObjectClassMap.clear();
118 private void clearNonRepositoryMaps() {
119 if (!myUseRepository) {
120 myNameToClassMap.clear();
124 @Nullable
125 public PsiPackage findPackage(@NotNull String packageName) {
126 Query<VirtualFile> dirs = myPackageIndex.getDirsByPackageName(packageName, false);
127 if (dirs.findFirst() == null) return null;
128 return new PsiPackageImpl(myManager, packageName);
131 public PsiClass[] findClasses(@NotNull String qName, @NotNull final GlobalSearchScope scope) {
132 final Collection<? extends PsiElement> classes = JavaFullClassNameIndex.getInstance().get(qName.hashCode(), myManager.getProject(), scope);
133 if (classes.isEmpty()) return PsiClass.EMPTY_ARRAY;
134 List<PsiClass> result = new ArrayList<PsiClass>(classes.size());
135 int count = 0;
136 PsiClass aClass = null;
137 for (PsiElement found : classes) {
138 if (notClass(found)) continue;
140 aClass = (PsiClass)found;
141 final String qualifiedName = aClass.getQualifiedName();
142 if (qualifiedName == null || !qualifiedName.equals(qName)) continue;
144 VirtualFile vFile = aClass.getContainingFile().getVirtualFile();
145 if (!fileIsInScope(scope, vFile)) continue;
147 result.add(aClass);
148 count++;
151 if (count == 0) return PsiClass.EMPTY_ARRAY;
152 if (count == 1) return new PsiClass[] {aClass};
154 ContainerUtil.quickSort(result, new Comparator<PsiClass>() {
155 public int compare(PsiClass o1, PsiClass o2) {
156 return scope.compare(o2.getContainingFile().getVirtualFile(), o1.getContainingFile().getVirtualFile());
160 return result.toArray(new PsiClass[count]);
163 private static boolean notClass(final PsiElement found) {
164 if (found instanceof PsiClass) return false;
166 VirtualFile faultyContainer = PsiUtil.getVirtualFile(found);
167 LOG.error("Non class in class list: " + faultyContainer);
168 if (faultyContainer != null && faultyContainer.isValid()) {
169 FileBasedIndex.getInstance().requestReindex(faultyContainer);
172 return true;
175 @Nullable
176 public PsiClass findClass(@NotNull String qName, @NotNull GlobalSearchScope scope) {
177 if (!myUseRepository) {
178 return findClassWithoutRepository(qName);
181 if (!myInitialized) {
182 LOG.error("Access to psi files should be performed only after startup activity");
183 return null;
185 LOG.assertTrue(!myDisposed);
187 if ("java.lang.Object".equals(qName)) { // optimization
188 PsiClass cached = myCachedObjectClassMap.get(scope);
189 if (cached == null) {
190 cached = findClassInIndex(qName, scope);
191 if (cached != null) {
192 cached = myCachedObjectClassMap.cacheOrGet(scope, cached);
196 return cached;
199 return findClassInIndex(qName, scope);
202 @Nullable
203 private PsiClass findClassWithoutRepository(String qName) {
204 PsiClass aClass = myNameToClassMap.get(qName);
205 if (aClass != null) {
206 return aClass;
209 aClass = _findClassWithoutRepository(qName);
210 myNameToClassMap.put(qName, aClass);
211 return aClass;
214 @Nullable
215 private PsiClass _findClassWithoutRepository(String qName) {
216 VirtualFile[] sourcePath = myProjectRootManager.getFilesFromAllModules(OrderRootType.SOURCES);
217 VirtualFile[] classPath = myProjectRootManager.getFilesFromAllModules(OrderRootType.CLASSES);
219 int index = 0;
220 while (index < qName.length()) {
221 int index1 = qName.indexOf('.', index);
222 if (index1 < 0) {
223 index1 = qName.length();
225 String name = qName.substring(index, index1);
227 final int sourceType = 0;
228 //final int compiledType = 1;
230 for (int type = 0; type < 2; type++) {
231 VirtualFile[] vDirs = type == sourceType ? sourcePath : classPath;
232 for (VirtualFile vDir : vDirs) {
233 if (vDir != null) {
234 VirtualFile vChild = type == sourceType
235 ? vDir.findChild(name + JAVA_EXTENSION)
236 : vDir.findChild(name + CLASS_EXTENSION);
237 if (vChild != null) {
238 PsiFile file = myFileManager.findFile(vChild);
239 if (file instanceof PsiJavaFile) {
240 PsiClass aClass = findClassByName((PsiJavaFile)file, name);
241 if (aClass != null) {
242 index = index1 + 1;
243 while (index < qName.length()) {
244 index1 = qName.indexOf('.', index);
245 if (index1 < 0) {
246 index1 = qName.length();
248 name = qName.substring(index, index1);
249 aClass = findClassByName(aClass, name);
250 if (aClass == null) return null;
251 index = index1 + 1;
253 return aClass;
261 boolean existsDir = false;
262 for (int type = 0; type < 2; type++) {
263 VirtualFile[] vDirs = type == sourceType ? sourcePath : classPath;
264 for (int i = 0; i < vDirs.length; i++) {
265 if (vDirs[i] != null) {
266 VirtualFile vDirChild = vDirs[i].findChild(name);
267 if (vDirChild != null) {
268 PsiDirectory dir = myFileManager.findDirectory(vDirChild);
269 if (dir != null) {
270 vDirs[i] = vDirChild;
271 existsDir = true;
272 continue;
275 vDirs[i] = null;
279 if (!existsDir) return null;
280 index = index1 + 1;
282 return null;
285 @Nullable
286 private static PsiClass findClassByName(PsiJavaFile scope, String name) {
287 PsiClass[] classes = scope.getClasses();
288 for (PsiClass aClass : classes) {
289 if (name.equals(aClass.getName())) {
290 return aClass;
293 return null;
296 @Nullable
297 private static PsiClass findClassByName(PsiClass scope, String name) {
298 PsiClass[] classes = scope.getInnerClasses();
299 for (PsiClass aClass : classes) {
300 if (name.equals(aClass.getName())) {
301 return aClass;
304 return null;
307 @Nullable
308 private PsiClass findClassInIndex(String qName, GlobalSearchScope scope) {
309 VirtualFile bestFile = null;
310 PsiClass bestClass = null;
312 final Collection<? extends PsiElement> classes = JavaFullClassNameIndex.getInstance().get(qName.hashCode(), myManager.getProject(), scope);
314 for (PsiElement found : classes) {
315 if (notClass(found)) continue;
317 PsiClass aClass = (PsiClass)found;
318 final boolean valid = aClass.isValid();
319 if (!valid) {
320 LOG.error("Invalid class "+aClass+"; "+aClass.getContainingFile());
321 continue;
324 final String qualifiedName = aClass.getQualifiedName();
325 if (qualifiedName == null || !qualifiedName.equals(qName)) continue;
327 PsiFile file = aClass.getContainingFile();
328 if (file == null) {
329 LOG.error("aClass=" + aClass);
330 continue;
333 VirtualFile vFile = file.getVirtualFile();
334 if (!fileIsInScope(scope, vFile)) continue;
335 if (bestFile == null || scope.compare(vFile, bestFile) > 0) {
336 bestFile = vFile;
337 bestClass = aClass;
341 return bestClass;
345 private boolean fileIsInScope(final GlobalSearchScope scope, final VirtualFile vFile) {
346 if (!scope.contains(vFile)) return false;
348 if (vFile.getFileType() == StdFileTypes.CLASS) {
349 // See IDEADEV-5626
350 final VirtualFile root = ProjectRootManager.getInstance(myManager.getProject()).getFileIndex().getClassRootForFile(vFile);
351 VirtualFile parent = vFile.getParent();
352 final PsiNameHelper nameHelper = JavaPsiFacade.getInstance(myManager.getProject()).getNameHelper();
353 while (parent != null && parent != root) {
354 if (!nameHelper.isIdentifier(parent.getName())) return false;
355 parent = parent.getParent();
359 return true;
362 public Collection<String> getNonTrivialPackagePrefixes() {
363 if (myNontrivialPackagePrefixes == null) {
364 Set<String> names = new HashSet<String>();
365 final ProjectRootManager rootManager = myProjectRootManager;
366 final VirtualFile[] sourceRoots = rootManager.getContentSourceRoots();
367 final ProjectFileIndex fileIndex = rootManager.getFileIndex();
368 for (final VirtualFile sourceRoot : sourceRoots) {
369 final String packageName = fileIndex.getPackageNameByDirectory(sourceRoot);
370 if (packageName != null && packageName.length() > 0) {
371 names.add(packageName);
374 myNontrivialPackagePrefixes = names;
376 return myNontrivialPackagePrefixes;