update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / psi / impl / source / PsiJavaFileBaseImpl.java
blob1c6b3e728afaabe4c6970c50ef9709b7cf9b768d
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.
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();
81 clone.clearCaches();
82 return clone;
85 @NotNull
86 public PsiClass[] getClasses() {
87 final StubElement<?> stub = getStub();
88 if (stub != null) {
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;
100 @NotNull
101 public String getPackageName() {
102 PsiJavaFileStub stub = (PsiJavaFileStub)getStub();
103 if (stub != null) {
104 return stub.getPackageName();
107 PsiPackageStatement statement = getPackageStatement();
108 return statement == null ? "" : statement.getPackageName();
111 @NotNull
112 public PsiImportList getImportList() {
113 final StubElement<?> stub = getStub();
114 if (stub != null) {
115 return stub.getChildrenByType(JavaStubElementTypes.IMPORT_LIST, PsiImportList.ARRAY_FACTORY)[0];
118 ASTNode node = calcTreeElement().findChildByType(JavaElementType.IMPORT_LIST);
119 assert node != null;
121 return (PsiImportList)node.getPsi();
124 @NotNull
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) {
134 array.add(resolved);
139 if (includeImplicit){
140 PsiJavaCodeReferenceElement[] implicitRefs = getImplicitlyImportedPackageReferences();
141 for (PsiJavaCodeReferenceElement implicitRef : implicitRefs) {
142 final PsiElement resolved = implicitRef.resolve();
143 if (resolved != null) {
144 array.add(resolved);
149 return array.toArray(new PsiElement[array.size()]);
152 @NotNull
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();
179 return null;
182 @NotNull
183 public String[] getImplicitlyImportedPackages() {
184 return IMPLICIT_IMPORTS;
187 @NotNull
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;
218 else {
219 myIsProcessingOnDemand = false;
220 myHiddenNames.add(importStaticStatement.getReferenceName());
223 else {
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()))) {
235 return true;
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);
243 else {
244 return true;
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()) {
279 if (name != null) {
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)) {
298 return false;
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;
368 return true;
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)) {
374 return false;
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;
383 else {
384 LOG.assertTrue(false);
386 return true;
389 public void accept(@NotNull PsiElementVisitor visitor){
390 if (visitor instanceof JavaElementVisitor) {
391 ((JavaElementVisitor)visitor).visitJavaFile(this);
393 else {
394 visitor.visitFile(this);
398 @NotNull
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");
408 @NotNull
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;
486 myState = state;
489 public boolean process(ClassCollectingProcessor.ResultWithContext result) {
490 myProcessor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, result.getFileContext());
491 return myProcessor.execute(result.getElement(), myState);