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.
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
;
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
) {
68 myProjectRootManager
= projectRootManager
;
69 myFileManager
= fileManager
;
71 myUseRepository
= true;
73 myManager
.registerRunnableToRunOnChange(new Runnable() {
75 myCachedObjectClassMap
.clear();
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();
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();
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() {
115 myCachedObjectClassMap
.clear();
118 private void clearNonRepositoryMaps() {
119 if (!myUseRepository
) {
120 myNameToClassMap
.clear();
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());
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;
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
);
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");
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
);
199 return findClassInIndex(qName
, scope
);
203 private PsiClass
findClassWithoutRepository(String qName
) {
204 PsiClass aClass
= myNameToClassMap
.get(qName
);
205 if (aClass
!= null) {
209 aClass
= _findClassWithoutRepository(qName
);
210 myNameToClassMap
.put(qName
, aClass
);
215 private PsiClass
_findClassWithoutRepository(String qName
) {
216 VirtualFile
[] sourcePath
= myProjectRootManager
.getFilesFromAllModules(OrderRootType
.SOURCES
);
217 VirtualFile
[] classPath
= myProjectRootManager
.getFilesFromAllModules(OrderRootType
.CLASSES
);
220 while (index
< qName
.length()) {
221 int index1
= qName
.indexOf('.', index
);
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
) {
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) {
243 while (index
< qName
.length()) {
244 index1
= qName
.indexOf('.', index
);
246 index1
= qName
.length();
248 name
= qName
.substring(index
, index1
);
249 aClass
= findClassByName(aClass
, name
);
250 if (aClass
== null) return null;
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
);
270 vDirs
[i
] = vDirChild
;
279 if (!existsDir
) return null;
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())) {
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())) {
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();
320 LOG
.error("Invalid class "+aClass
+"; "+aClass
.getContainingFile());
324 final String qualifiedName
= aClass
.getQualifiedName();
325 if (qualifiedName
== null || !qualifiedName
.equals(qName
)) continue;
327 PsiFile file
= aClass
.getContainingFile();
329 LOG
.error("aClass=" + aClass
);
333 VirtualFile vFile
= file
.getVirtualFile();
334 if (!fileIsInScope(scope
, vFile
)) continue;
335 if (bestFile
== null || scope
.compare(vFile
, bestFile
) > 0) {
345 private boolean fileIsInScope(final GlobalSearchScope scope
, final VirtualFile vFile
) {
346 if (!scope
.contains(vFile
)) return false;
348 if (vFile
.getFileType() == StdFileTypes
.CLASS
) {
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();
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
;