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.
16 package com
.intellij
.psi
.impl
.source
;
18 import com
.intellij
.lang
.ASTNode
;
19 import com
.intellij
.lang
.Language
;
20 import com
.intellij
.lang
.StdLanguages
;
21 import com
.intellij
.openapi
.diagnostic
.Logger
;
22 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
23 import com
.intellij
.openapi
.project
.Project
;
24 import com
.intellij
.openapi
.roots
.*;
25 import com
.intellij
.openapi
.roots
.impl
.DirectoryIndex
;
26 import com
.intellij
.openapi
.util
.Key
;
27 import com
.intellij
.openapi
.vfs
.VfsUtil
;
28 import com
.intellij
.openapi
.vfs
.VirtualFile
;
29 import com
.intellij
.pom
.java
.LanguageLevel
;
30 import com
.intellij
.psi
.*;
31 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
32 import com
.intellij
.psi
.impl
.PsiImplUtil
;
33 import com
.intellij
.psi
.impl
.java
.stubs
.JavaStubElementTypes
;
34 import com
.intellij
.psi
.impl
.java
.stubs
.PsiJavaFileStub
;
35 import com
.intellij
.psi
.impl
.source
.resolve
.ClassCollectingProcessor
;
36 import com
.intellij
.psi
.impl
.source
.resolve
.ClassResolverProcessor
;
37 import com
.intellij
.psi
.impl
.source
.resolve
.ResolveCache
;
38 import com
.intellij
.psi
.impl
.source
.tree
.JavaElementType
;
39 import com
.intellij
.psi
.scope
.ElementClassHint
;
40 import com
.intellij
.psi
.scope
.JavaScopeProcessorEvent
;
41 import com
.intellij
.psi
.scope
.NameHint
;
42 import com
.intellij
.psi
.scope
.PsiScopeProcessor
;
43 import com
.intellij
.psi
.stubs
.StubElement
;
44 import com
.intellij
.psi
.tree
.IElementType
;
45 import com
.intellij
.psi
.util
.*;
46 import com
.intellij
.reference
.SoftReference
;
47 import com
.intellij
.util
.Processor
;
48 import com
.intellij
.util
.containers
.HashSet
;
49 import com
.intellij
.util
.containers
.MostlySingularMultiMap
;
50 import com
.intellij
.util
.indexing
.FileBasedIndex
;
51 import org
.jetbrains
.annotations
.NonNls
;
52 import org
.jetbrains
.annotations
.NotNull
;
54 import java
.util
.ArrayList
;
55 import java
.util
.Collection
;
56 import java
.util
.List
;
57 import java
.util
.concurrent
.ConcurrentMap
;
59 public abstract class PsiJavaFileBaseImpl
extends PsiFileImpl
implements PsiJavaFile
{
60 private static final Logger LOG
= Logger
.getInstance("#com.intellij.psi.impl.source.PsiJavaFileBaseImpl");
62 private final CachedValue
<MostlySingularMultiMap
<String
, ClassCollectingProcessor
.ResultWithContext
>> myResolveCache
;
64 @NonNls private static final String
[] IMPLICIT_IMPORTS
= { "java.lang" };
65 private static final ParameterizedCachedValueProvider
<LanguageLevel
,PsiJavaFileBaseImpl
> LANGUAGE_LEVEL_PROVIDER
= new ParameterizedCachedValueProvider
<LanguageLevel
, PsiJavaFileBaseImpl
>() {
66 public CachedValueProvider
.Result
<LanguageLevel
> compute(PsiJavaFileBaseImpl file
) {
67 LanguageLevel level
= file
.getLanguageLevelInner();
68 return CachedValueProvider
.Result
.create(level
, ProjectRootManager
.getInstance(file
.getProject()));
72 private static final Key
<ResolveCache
.MapPair
<PsiJavaFile
,ConcurrentMap
<String
, SoftReference
<JavaResolveResult
[]>>>> CACHED_CLASSES_MAP_KEY
= Key
.create("CACHED_CLASSES_MAP_KEY");
73 protected PsiJavaFileBaseImpl(IElementType elementType
, IElementType contentElementType
, FileViewProvider viewProvider
) {
74 super(elementType
, contentElementType
, viewProvider
);
75 myResolveCache
= myManager
.getCachedValuesManager().createCachedValue(new MyCacheBuilder(), false);
78 @SuppressWarnings({"CloneDoesntDeclareCloneNotSupportedException"})
79 protected PsiJavaFileBaseImpl
clone() {
80 PsiJavaFileBaseImpl clone
= (PsiJavaFileBaseImpl
)super.clone();
86 public PsiClass
[] getClasses() {
87 final StubElement
<?
> stub
= getStub();
89 return stub
.getChildrenByType(JavaStubElementTypes
.CLASS
, PsiClass
.ARRAY_FACTORY
);
92 return calcTreeElement().getChildrenAsPsiElements(Constants
.CLASS_BIT_SET
, Constants
.PSI_CLASS_ARRAY_CONSTRUCTOR
);
95 public PsiPackageStatement
getPackageStatement() {
96 ASTNode node
= calcTreeElement().findChildByType(JavaElementType
.PACKAGE_STATEMENT
);
97 return node
!= null ?
(PsiPackageStatement
)node
.getPsi() : null;
101 public String
getPackageName() {
102 PsiJavaFileStub stub
= (PsiJavaFileStub
)getStub();
104 return stub
.getPackageName();
107 PsiPackageStatement statement
= getPackageStatement();
108 return statement
== null ?
"" : statement
.getPackageName();
112 public PsiImportList
getImportList() {
113 final StubElement
<?
> stub
= getStub();
115 return stub
.getChildrenByType(JavaStubElementTypes
.IMPORT_LIST
, PsiImportList
.ARRAY_FACTORY
)[0];
118 ASTNode node
= calcTreeElement().findChildByType(JavaElementType
.IMPORT_LIST
);
121 return (PsiImportList
)node
.getPsi();
125 public PsiElement
[] getOnDemandImports(boolean includeImplicit
, boolean checkIncludes
) {
126 List
<PsiElement
> array
= new ArrayList
<PsiElement
>();
128 PsiImportList importList
= getImportList();
129 PsiImportStatement
[] statements
= importList
.getImportStatements();
130 for (PsiImportStatement statement
: statements
) {
131 if (statement
.isOnDemand()) {
132 PsiElement resolved
= statement
.resolve();
133 if (resolved
!= null) {
139 if (includeImplicit
){
140 PsiJavaCodeReferenceElement
[] implicitRefs
= getImplicitlyImportedPackageReferences();
141 for (PsiJavaCodeReferenceElement implicitRef
: implicitRefs
) {
142 final PsiElement resolved
= implicitRef
.resolve();
143 if (resolved
!= null) {
149 return array
.toArray(new PsiElement
[array
.size()]);
153 public PsiClass
[] getSingleClassImports(boolean checkIncludes
) {
154 List
<PsiClass
> array
= new ArrayList
<PsiClass
>();
155 PsiImportList importList
= getImportList();
156 PsiImportStatement
[] statements
= importList
.getImportStatements();
157 for (PsiImportStatement statement
: statements
) {
158 if (!statement
.isOnDemand()) {
159 PsiElement ref
= statement
.resolve();
160 if (ref
instanceof PsiClass
) {
161 array
.add((PsiClass
)ref
);
165 return array
.toArray(new PsiClass
[array
.size()]);
168 public PsiJavaCodeReferenceElement
findImportReferenceTo(PsiClass aClass
) {
169 PsiImportList importList
= getImportList();
170 PsiImportStatement
[] statements
= importList
.getImportStatements();
171 for (PsiImportStatement statement
: statements
) {
172 if (!statement
.isOnDemand()) {
173 PsiElement ref
= statement
.resolve();
174 if (ref
!= null && getManager().areElementsEquivalent(ref
, aClass
)) {
175 return statement
.getImportReference();
183 public String
[] getImplicitlyImportedPackages() {
184 return IMPLICIT_IMPORTS
;
188 public PsiJavaCodeReferenceElement
[] getImplicitlyImportedPackageReferences() {
189 return PsiImplUtil
.namesToPackageReferences(myManager
, IMPLICIT_IMPORTS
);
192 private static class StaticImportFilteringProcessor
implements PsiScopeProcessor
{
193 private final PsiScopeProcessor myDelegate
;
194 private String myNameToFilter
;
195 private boolean myIsProcessingOnDemand
;
196 private final Collection
<String
> myHiddenNames
= new HashSet
<String
>();
198 private StaticImportFilteringProcessor(PsiScopeProcessor delegate
, String nameToFilter
) {
199 myDelegate
= delegate
;
200 myNameToFilter
= nameToFilter
;
203 public void setNameToFilter(String nameToFilter
) {
204 myNameToFilter
= nameToFilter
;
207 public <T
> T
getHint(Key
<T
> hintKey
) {
208 return myDelegate
.getHint(hintKey
);
211 public void handleEvent(Event event
, Object associated
) {
212 if (JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
.equals(event
)) {
213 if (associated
instanceof PsiImportStaticStatement
) {
214 final PsiImportStaticStatement importStaticStatement
= (PsiImportStaticStatement
)associated
;
215 if (importStaticStatement
.isOnDemand()) {
216 myIsProcessingOnDemand
= true;
219 myIsProcessingOnDemand
= false;
220 myHiddenNames
.add(importStaticStatement
.getReferenceName());
224 myIsProcessingOnDemand
= false;
228 myDelegate
.handleEvent(event
, associated
);
231 public boolean execute(PsiElement element
, ResolveState state
) {
232 if (element
instanceof PsiModifierListOwner
&& ((PsiModifierListOwner
)element
).hasModifierProperty(PsiModifier
.STATIC
)) {
233 if (myNameToFilter
!= null &&
234 (!(element
instanceof PsiNamedElement
) || !myNameToFilter
.equals(((PsiNamedElement
)element
).getName()))) {
237 if (element
instanceof PsiNamedElement
&& myIsProcessingOnDemand
) {
238 final String name
= ((PsiNamedElement
)element
).getName();
239 if (myHiddenNames
.contains(name
)) return true;
241 return myDelegate
.execute(element
, state
);
249 public boolean processDeclarations(@NotNull final PsiScopeProcessor processor
, @NotNull final ResolveState state
, PsiElement lastParent
, @NotNull PsiElement place
){
250 if (processor
instanceof ClassResolverProcessor
&& isPhysical() &&
251 (getUserData(BATCH_REFERENCE_PROCESSING
) == Boolean
.TRUE
|| myResolveCache
.hasUpToDateValue())) {
252 final ClassResolverProcessor hint
= (ClassResolverProcessor
)processor
;
253 String name
= hint
.getName(state
);
254 MostlySingularMultiMap
<String
, ClassCollectingProcessor
.ResultWithContext
> cache
= myResolveCache
.getValue();
255 MyResolveCacheProcessor cacheProcessor
= new MyResolveCacheProcessor(processor
, state
);
256 return name
!= null ? cache
.processForKey(name
, cacheProcessor
) : cache
.processAllValues(cacheProcessor
);
259 return processDeclarationsNoGuess(processor
, state
, lastParent
, place
);
262 private boolean processDeclarationsNoGuess(PsiScopeProcessor processor
, ResolveState state
, PsiElement lastParent
, PsiElement place
){
263 processor
.handleEvent(PsiScopeProcessor
.Event
.SET_DECLARATION_HOLDER
, this);
264 final ElementClassHint classHint
= processor
.getHint(ElementClassHint
.KEY
);
265 final NameHint nameHint
= processor
.getHint(NameHint
.KEY
);
266 final String name
= nameHint
!= null ? nameHint
.getName(state
) : null;
267 if (classHint
== null || classHint
.shouldProcess(ElementClassHint
.DeclaractionKind
.CLASS
)){
268 final PsiClass
[] classes
= getClasses();
269 for (PsiClass aClass
: classes
) {
270 if (!processor
.execute(aClass
, state
)) return false;
273 PsiImportList importList
= getImportList();
274 PsiImportStatement
[] importStatements
= importList
.getImportStatements();
276 //Single-type processing
277 for (PsiImportStatement statement
: importStatements
) {
278 if (!statement
.isOnDemand()) {
280 String refText
= statement
.getQualifiedName();
281 if (refText
== null || !refText
.endsWith(name
)) continue;
284 PsiElement resolved
= statement
.resolve();
285 if (resolved
instanceof PsiClass
) {
286 processor
.handleEvent(JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
, statement
);
287 if (!processor
.execute(resolved
, state
)) return false;
291 processor
.handleEvent(JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
, null);
293 // check in current package
294 String packageName
= getPackageName();
295 PsiPackage aPackage
= JavaPsiFacade
.getInstance(myManager
.getProject()).findPackage(packageName
);
296 if (aPackage
!= null) {
297 if (!aPackage
.processDeclarations(processor
, state
, null, place
)) {
302 //On demand processing
303 for (PsiImportStatement statement
: importStatements
) {
304 if (statement
.isOnDemand()) {
305 PsiElement resolved
= statement
.resolve();
306 if (resolved
!= null) {
307 processor
.handleEvent(JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
, statement
);
308 processOnDemandTarget(resolved
, processor
, state
, place
);
314 if(classHint
== null || classHint
.shouldProcess(ElementClassHint
.DeclaractionKind
.PACKAGE
)){
315 final PsiPackage rootPackage
= JavaPsiFacade
.getInstance(getProject()).findPackage("");
316 processor
.handleEvent(JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
, rootPackage
);
317 if(rootPackage
!= null) rootPackage
.processDeclarations(processor
, state
, null, place
);
320 // todo[dsl] class processing
321 final PsiImportList importList
= getImportList();
322 final PsiImportStaticStatement
[] importStaticStatements
= importList
.getImportStaticStatements();
323 if (importStaticStatements
.length
> 0) {
324 final StaticImportFilteringProcessor staticImportProcessor
= new StaticImportFilteringProcessor(processor
, null);
326 // single member processing
327 for (PsiImportStaticStatement importStaticStatement
: importStaticStatements
) {
328 if (!importStaticStatement
.isOnDemand()) {
329 final String referenceName
= importStaticStatement
.getReferenceName();
330 final PsiClass targetElement
= importStaticStatement
.resolveTargetClass();
331 if (targetElement
!= null) {
332 staticImportProcessor
.setNameToFilter(referenceName
);
333 staticImportProcessor
.handleEvent(JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
, importStaticStatement
);
334 final boolean result
= targetElement
.processDeclarations(staticImportProcessor
, state
, lastParent
, place
);
335 if (!result
) return false;
340 // on-demand processing
341 for (PsiImportStaticStatement importStaticStatement
: importStaticStatements
) {
342 if (importStaticStatement
.isOnDemand()) {
343 final PsiClass targetElement
= importStaticStatement
.resolveTargetClass();
344 if (targetElement
!= null) {
345 staticImportProcessor
.setNameToFilter(null);
346 staticImportProcessor
.handleEvent(JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
, importStaticStatement
);
347 final boolean result
= targetElement
.processDeclarations(staticImportProcessor
, state
, lastParent
, place
);
348 if (!result
) return false;
353 staticImportProcessor
.handleEvent(JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
, null);
356 if (classHint
== null || classHint
.shouldProcess(ElementClassHint
.DeclaractionKind
.CLASS
)){
357 processor
.handleEvent(JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
, null);
359 PsiJavaCodeReferenceElement
[] implicitlyImported
= getImplicitlyImportedPackageReferences();
360 for (PsiJavaCodeReferenceElement aImplicitlyImported
: implicitlyImported
) {
361 PsiElement resolved
= aImplicitlyImported
.resolve();
362 if (resolved
!= null) {
363 if (!processOnDemandTarget(resolved
, processor
, state
, place
)) return false;
371 private static boolean processOnDemandTarget (PsiElement target
, PsiScopeProcessor processor
, ResolveState substitutor
, PsiElement place
) {
372 if (target
instanceof PsiPackage
) {
373 if (!target
.processDeclarations(processor
, substitutor
, null, place
)) {
377 else if (target
instanceof PsiClass
) {
378 PsiClass
[] inners
= ((PsiClass
)target
).getInnerClasses();
379 for (PsiClass inner
: inners
) {
380 if (!processor
.execute(inner
, substitutor
)) return false;
384 LOG
.assertTrue(false);
389 public void accept(@NotNull PsiElementVisitor visitor
){
390 if (visitor
instanceof JavaElementVisitor
) {
391 ((JavaElementVisitor
)visitor
).visitJavaFile(this);
394 visitor
.visitFile(this);
399 public Language
getLanguage() {
400 return StdLanguages
.JAVA
;
403 public boolean importClass(PsiClass aClass
) {
404 return JavaCodeStyleManager
.getInstance(getProject()).addImport(this, aClass
);
407 private static final Key
<ParameterizedCachedValue
<LanguageLevel
, PsiJavaFileBaseImpl
>> LANGUAGE_LEVEL_KEY
= Key
.create("LANGUAGE_LEVEL");
409 public LanguageLevel
getLanguageLevel() {
410 return getManager().getCachedValuesManager().getParameterizedCachedValue(this, LANGUAGE_LEVEL_KEY
, LANGUAGE_LEVEL_PROVIDER
, false, this);
413 private LanguageLevel
getLanguageLevelInner() {
414 if (myOriginalFile
instanceof PsiJavaFile
) return ((PsiJavaFile
)myOriginalFile
).getLanguageLevel();
415 final LanguageLevel forcedLanguageLevel
= getUserData(PsiUtil
.FILE_LANGUAGE_LEVEL_KEY
);
416 if (forcedLanguageLevel
!= null) return forcedLanguageLevel
;
417 VirtualFile virtualFile
= getVirtualFile();
419 if (virtualFile
== null) {
420 virtualFile
= getUserData(FileBasedIndex
.VIRTUAL_FILE
);
423 if (virtualFile
== null) {
424 final PsiFile originalFile
= getOriginalFile();
425 if (originalFile
instanceof PsiJavaFile
&& originalFile
!= this) return ((PsiJavaFile
)originalFile
).getLanguageLevel();
426 return LanguageLevelProjectExtension
.getInstance(getProject()).getLanguageLevel();
429 final VirtualFile folder
= virtualFile
.getParent();
430 if (folder
!= null) {
431 final LanguageLevel level
= folder
.getUserData(LanguageLevel
.KEY
);
432 if (level
!= null) return level
;
435 final Project project
= getProject();
436 final ProjectFileIndex index
= ProjectRootManager
.getInstance(project
).getFileIndex();
437 final VirtualFile sourceRoot
= index
.getSourceRootForFile(virtualFile
);
438 if (sourceRoot
!= null) {
439 String relativePath
= VfsUtil
.getRelativePath(folder
, sourceRoot
, '/');
440 LOG
.assertTrue(relativePath
!= null);
441 List
<OrderEntry
> orderEntries
= index
.getOrderEntriesForFile(virtualFile
);
442 if (orderEntries
.isEmpty()) {
443 LOG
.error("Inconsistent: " + DirectoryIndex
.getInstance(project
).getInfoForDirectory(folder
).toString());
445 final VirtualFile
[] files
= orderEntries
.get(0).getFiles(OrderRootType
.CLASSES
);
446 for (VirtualFile rootFile
: files
) {
447 final VirtualFile classFile
= rootFile
.findFileByRelativePath(relativePath
);
448 if (classFile
!= null) {
449 return getLanguageLevel(classFile
);
454 return LanguageLevelProjectExtension
.getInstance(project
).getLanguageLevel();
457 private LanguageLevel
getLanguageLevel(final VirtualFile dirFile
) {
458 final VirtualFile
[] children
= dirFile
.getChildren();
459 final LanguageLevel defaultLanguageLevel
= LanguageLevelProjectExtension
.getInstance(getProject()).getLanguageLevel();
460 for (VirtualFile child
: children
) {
461 if (StdFileTypes
.CLASS
.equals(child
.getFileType())) {
462 final PsiFile psiFile
= getManager().findFile(child
);
463 if (psiFile
instanceof PsiJavaFile
) return ((PsiJavaFile
)psiFile
).getLanguageLevel();
467 return defaultLanguageLevel
;
470 private class MyCacheBuilder
implements CachedValueProvider
<MostlySingularMultiMap
<String
, ClassCollectingProcessor
.ResultWithContext
>> {
471 public Result
<MostlySingularMultiMap
<String
, ClassCollectingProcessor
.ResultWithContext
>> compute() {
472 ClassCollectingProcessor p
= new ClassCollectingProcessor();
473 processDeclarationsNoGuess(p
, ResolveState
.initial(),
474 PsiJavaFileBaseImpl
.this,
475 PsiJavaFileBaseImpl
.this);
476 return new Result
<MostlySingularMultiMap
<String
, ClassCollectingProcessor
.ResultWithContext
>>(p
.getResults(), PsiModificationTracker
.JAVA_STRUCTURE_MODIFICATION_COUNT
);
480 private static class MyResolveCacheProcessor
implements Processor
<ClassCollectingProcessor
.ResultWithContext
> {
481 private final PsiScopeProcessor myProcessor
;
482 private final ResolveState myState
;
484 public MyResolveCacheProcessor(PsiScopeProcessor processor
, ResolveState state
) {
485 myProcessor
= processor
;
489 public boolean process(ClassCollectingProcessor
.ResultWithContext result
) {
490 myProcessor
.handleEvent(JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
, result
.getFileContext());
491 return myProcessor
.execute(result
.getElement(), myState
);