blob: 403465efab0392c97aeced4d4c6f712f421f0873 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NotNullLazyKey;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.JavaPsiImplementationHelper;
import com.intellij.psi.impl.PsiFileEx;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
import com.intellij.psi.impl.source.resolve.ClassResolverProcessor;
import com.intellij.psi.impl.source.resolve.SymbolCollectingProcessor;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.scope.ElementClassHint;
import com.intellij.psi.scope.JavaScopeProcessorEvent;
import com.intellij.psi.scope.NameHint;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.NotNullFunction;
import com.intellij.util.Processor;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.MostlySingularMultiMap;
import com.intellij.util.indexing.IndexingDataKeys;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJavaFile {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiJavaFileBaseImpl");
@NonNls private static final String[] IMPLICIT_IMPORTS = { CommonClassNames.DEFAULT_PACKAGE };
private final CachedValue<MostlySingularMultiMap<String, SymbolCollectingProcessor.ResultWithContext>> myResolveCache;
private volatile String myPackageName;
protected PsiJavaFileBaseImpl(IElementType elementType, IElementType contentElementType, FileViewProvider viewProvider) {
super(elementType, contentElementType, viewProvider);
myResolveCache = CachedValuesManager.getManager(myManager.getProject()).createCachedValue(new MyCacheBuilder(this), false);
}
@Override
@SuppressWarnings({"CloneDoesntDeclareCloneNotSupportedException"})
protected PsiJavaFileBaseImpl clone() {
PsiFileImpl clone = super.clone();
if (!(clone instanceof PsiJavaFileBaseImpl)) {
throw new AssertionError("Java file cloned as text: " + getTextLength() + "; " + getViewProvider());
}
clone.clearCaches();
return (PsiJavaFileBaseImpl)clone;
}
@Override
public void subtreeChanged() {
super.subtreeChanged();
myPackageName = null;
}
@Override
@NotNull
public PsiClass[] getClasses() {
final StubElement<?> stub = getStub();
if (stub != null) {
return stub.getChildrenByType(JavaStubElementTypes.CLASS, PsiClass.ARRAY_FACTORY);
}
return calcTreeElement().getChildrenAsPsiElements(Constants.CLASS_BIT_SET, PsiClass.ARRAY_FACTORY);
}
@Override
public PsiPackageStatement getPackageStatement() {
ASTNode node = calcTreeElement().findChildByType(JavaElementType.PACKAGE_STATEMENT);
return node != null ? (PsiPackageStatement)node.getPsi() : null;
}
@Override
@NotNull
public String getPackageName() {
PsiJavaFileStub stub = (PsiJavaFileStub)getStub();
if (stub != null) {
return stub.getPackageName();
}
String name = myPackageName;
if (name == null) {
PsiPackageStatement statement = getPackageStatement();
myPackageName = name = statement == null ? "" : statement.getPackageName();
}
return name;
}
@Override
public void setPackageName(final String packageName) throws IncorrectOperationException {
final PsiPackageStatement packageStatement = getPackageStatement();
final PsiElementFactory factory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
if (packageStatement != null) {
if (!packageName.isEmpty()) {
final PsiJavaCodeReferenceElement reference = packageStatement.getPackageReference();
reference.replace(factory.createReferenceFromText(packageName, packageStatement));
}
else {
packageStatement.delete();
}
}
else {
if (!packageName.isEmpty()) {
addBefore(factory.createPackageStatement(packageName), getFirstChild());
}
}
}
@Override
@NotNull
public PsiImportList getImportList() {
StubElement<?> stub = getStub();
if (stub != null) {
PsiImportList[] nodes = stub.getChildrenByType(JavaStubElementTypes.IMPORT_LIST, PsiImportList.ARRAY_FACTORY);
if (nodes.length != 1) {
reportStubAstMismatch(stub + "; " + stub.getChildrenStubs(), getStubTree(), PsiDocumentManager.getInstance(getProject()).getCachedDocument(this));
}
return nodes[0];
}
ASTNode node = calcTreeElement().findChildByType(JavaElementType.IMPORT_LIST);
assert node != null : getFileType() + ", " + getName();
return SourceTreeToPsiMap.treeToPsiNotNull(node);
}
@Override
@NotNull
public PsiElement[] getOnDemandImports(boolean includeImplicit, boolean checkIncludes) {
List<PsiElement> array = new ArrayList<PsiElement>();
PsiImportList importList = getImportList();
PsiImportStatement[] statements = importList.getImportStatements();
for (PsiImportStatement statement : statements) {
if (statement.isOnDemand()) {
PsiElement resolved = statement.resolve();
if (resolved != null) {
array.add(resolved);
}
}
}
if (includeImplicit){
PsiJavaCodeReferenceElement[] implicitRefs = getImplicitlyImportedPackageReferences();
for (PsiJavaCodeReferenceElement implicitRef : implicitRefs) {
final PsiElement resolved = implicitRef.resolve();
if (resolved != null) {
array.add(resolved);
}
}
}
return PsiUtilCore.toPsiElementArray(array);
}
@Override
@NotNull
public PsiClass[] getSingleClassImports(boolean checkIncludes) {
List<PsiClass> array = new ArrayList<PsiClass>();
PsiImportList importList = getImportList();
PsiImportStatement[] statements = importList.getImportStatements();
for (PsiImportStatement statement : statements) {
if (!statement.isOnDemand()) {
PsiElement ref = statement.resolve();
if (ref instanceof PsiClass) {
array.add((PsiClass)ref);
}
}
}
return array.toArray(new PsiClass[array.size()]);
}
@Override
public PsiJavaCodeReferenceElement findImportReferenceTo(PsiClass aClass) {
PsiImportList importList = getImportList();
PsiImportStatement[] statements = importList.getImportStatements();
for (PsiImportStatement statement : statements) {
if (!statement.isOnDemand()) {
PsiElement ref = statement.resolve();
if (ref != null && getManager().areElementsEquivalent(ref, aClass)) {
return statement.getImportReference();
}
}
}
return null;
}
@Override
@NotNull
public String[] getImplicitlyImportedPackages() {
return IMPLICIT_IMPORTS;
}
@Override
@NotNull
public PsiJavaCodeReferenceElement[] getImplicitlyImportedPackageReferences() {
return PsiImplUtil.namesToPackageReferences(myManager, IMPLICIT_IMPORTS);
}
private static class StaticImportFilteringProcessor implements PsiScopeProcessor {
private final PsiScopeProcessor myDelegate;
private boolean myIsProcessingOnDemand;
private final Collection<String> myHiddenNames = new HashSet<String>();
private final Collection<PsiElement> myCollectedElements = new HashSet<PsiElement>();
public StaticImportFilteringProcessor(final PsiScopeProcessor delegate) {
myDelegate = delegate;
}
@Override
public <T> T getHint(@NotNull final Key<T> hintKey) {
return myDelegate.getHint(hintKey);
}
@Override
public void handleEvent(@NotNull final Event event, final Object associated) {
if (JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT.equals(event) && associated instanceof PsiImportStaticStatement) {
final PsiImportStaticStatement importStaticStatement = (PsiImportStaticStatement)associated;
myIsProcessingOnDemand = importStaticStatement.isOnDemand();
if (!myIsProcessingOnDemand) {
myHiddenNames.add(importStaticStatement.getReferenceName());
}
}
myDelegate.handleEvent(event, associated);
}
@Override
public boolean execute(@NotNull final PsiElement element, @NotNull final ResolveState state) {
if (element instanceof PsiModifierListOwner && ((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) {
if (element instanceof PsiNamedElement && myIsProcessingOnDemand) {
final String name = ((PsiNamedElement)element).getName();
if (myHiddenNames.contains(name)) return true;
}
if (myCollectedElements.add(element)) {
return myDelegate.execute(element, state);
}
}
return true;
}
}
@Override
public boolean processDeclarations(@NotNull final PsiScopeProcessor processor,
@NotNull final ResolveState state,
PsiElement lastParent,
@NotNull PsiElement place) {
assert isValid();
if (processor instanceof ClassResolverProcessor &&
isPhysical() &&
(getUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING) == Boolean.TRUE || myResolveCache.hasUpToDateValue())) {
final ClassResolverProcessor hint = (ClassResolverProcessor)processor;
String name = hint.getName(state);
MostlySingularMultiMap<String, SymbolCollectingProcessor.ResultWithContext> cache = myResolveCache.getValue();
MyResolveCacheProcessor cacheProcessor = new MyResolveCacheProcessor(processor, state);
return name != null ? cache.processForKey(name, cacheProcessor) : cache.processAllValues(cacheProcessor);
}
return processDeclarationsNoGuess(processor, state, lastParent, place);
}
private boolean processDeclarationsNoGuess(PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, PsiElement place) {
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this);
final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
final NameHint nameHint = processor.getHint(NameHint.KEY);
final String name = nameHint != null ? nameHint.getName(state) : null;
final PsiImportList importList = getImportList();
if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
final PsiClass[] classes = getClasses();
for (PsiClass aClass : classes) {
if (!processor.execute(aClass, state)) return false;
}
final PsiImportStatement[] importStatements = importList.getImportStatements();
// single-type processing
for (PsiImportStatement statement : importStatements) {
if (!statement.isOnDemand()) {
if (name != null) {
final String refText = statement.getQualifiedName();
if (refText == null || !refText.endsWith(name)) continue;
}
final PsiElement resolved = statement.resolve();
if (resolved instanceof PsiClass) {
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, statement);
if (!processor.execute(resolved, state)) return false;
}
}
}
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, null);
// check in current package
final PsiPackage aPackage = JavaPsiFacade.getInstance(myManager.getProject()).findPackage(getPackageName());
if (aPackage != null) {
if (!aPackage.processDeclarations(processor, state, null, place)) {
return false;
}
}
// on-demand processing
for (PsiImportStatement statement : importStatements) {
if (statement.isOnDemand()) {
final PsiElement resolved = statement.resolve();
if (resolved != null) {
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, statement);
processOnDemandTarget(resolved, processor, state, place);
}
}
}
}
final PsiImportStaticStatement[] importStaticStatements = importList.getImportStaticStatements();
if (importStaticStatements.length > 0) {
final StaticImportFilteringProcessor staticImportProcessor = new StaticImportFilteringProcessor(processor);
// single member processing
for (PsiImportStaticStatement importStaticStatement : importStaticStatements) {
if (importStaticStatement.isOnDemand()) continue;
final PsiJavaCodeReferenceElement reference = importStaticStatement.getImportReference();
if (reference != null) {
final JavaResolveResult[] results = reference.multiResolve(false);
if (results.length > 0) {
staticImportProcessor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, importStaticStatement);
for (JavaResolveResult result : results) {
if (!staticImportProcessor.execute(result.getElement(), state)) return false;
}
}
}
}
// on-demand processing
for (PsiImportStaticStatement importStaticStatement : importStaticStatements) {
if (!importStaticStatement.isOnDemand()) continue;
final PsiClass targetElement = importStaticStatement.resolveTargetClass();
if (targetElement != null) {
staticImportProcessor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, importStaticStatement);
if (!targetElement.processDeclarations(staticImportProcessor, state, lastParent, place)) return false;
}
}
}
if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, null);
final PsiJavaCodeReferenceElement[] implicitlyImported = getImplicitlyImportedPackageReferences();
for (PsiJavaCodeReferenceElement aImplicitlyImported : implicitlyImported) {
final PsiElement resolved = aImplicitlyImported.resolve();
if (resolved != null) {
if (!processOnDemandTarget(resolved, processor, state, place)) return false;
}
}
}
return true;
}
private static boolean processOnDemandTarget(PsiElement target, PsiScopeProcessor processor, ResolveState substitutor, PsiElement place) {
if (target instanceof PsiPackage) {
if (!target.processDeclarations(processor, substitutor, null, place)) {
return false;
}
}
else if (target instanceof PsiClass) {
PsiClass[] inners = ((PsiClass)target).getInnerClasses();
for (PsiClass inner : inners) {
if (!processor.execute(inner, substitutor)) return false;
}
}
else {
LOG.assertTrue(false);
}
return true;
}
@Override
public void accept(@NotNull PsiElementVisitor visitor){
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitJavaFile(this);
}
else {
visitor.visitFile(this);
}
}
@Override
@NotNull
public Language getLanguage() {
return JavaLanguage.INSTANCE;
}
@Override
public boolean importClass(PsiClass aClass) {
return JavaCodeStyleManager.getInstance(getProject()).addImport(this, aClass);
}
private static final NotNullLazyKey<LanguageLevel, PsiJavaFileBaseImpl> LANGUAGE_LEVEL_KEY = NotNullLazyKey.create("LANGUAGE_LEVEL", new NotNullFunction<PsiJavaFileBaseImpl, LanguageLevel>() {
@Override
@NotNull
public LanguageLevel fun(PsiJavaFileBaseImpl file) {
return file.getLanguageLevelInner();
}
});
@Override
@NotNull
public LanguageLevel getLanguageLevel() {
return LANGUAGE_LEVEL_KEY.getValue(this);
}
@Override
public void clearCaches() {
super.clearCaches();
putUserData(LANGUAGE_LEVEL_KEY, null);
}
private LanguageLevel getLanguageLevelInner() {
if (myOriginalFile instanceof PsiJavaFile) {
return ((PsiJavaFile)myOriginalFile).getLanguageLevel();
}
LanguageLevel forcedLanguageLevel = getUserData(PsiUtil.FILE_LANGUAGE_LEVEL_KEY);
if (forcedLanguageLevel != null) return forcedLanguageLevel;
VirtualFile virtualFile = getVirtualFile();
if (virtualFile == null) virtualFile = getUserData(IndexingDataKeys.VIRTUAL_FILE);
final Project project = getProject();
if (virtualFile == null) {
final PsiFile originalFile = getOriginalFile();
if (originalFile instanceof PsiJavaFile && originalFile != this) {
return ((PsiJavaFile)originalFile).getLanguageLevel();
}
return LanguageLevelProjectExtension.getInstance(project).getLanguageLevel();
}
return JavaPsiImplementationHelper.getInstance(project).getEffectiveLanguageLevel(virtualFile);
}
private static class MyCacheBuilder implements CachedValueProvider<MostlySingularMultiMap<String, SymbolCollectingProcessor.ResultWithContext>> {
private final PsiJavaFileBaseImpl myFile;
public MyCacheBuilder(PsiJavaFileBaseImpl file) {
myFile = file;
}
@Override
public Result<MostlySingularMultiMap<String, SymbolCollectingProcessor.ResultWithContext>> compute() {
SymbolCollectingProcessor p = new SymbolCollectingProcessor();
myFile.processDeclarationsNoGuess(p, ResolveState.initial(), myFile, myFile);
MostlySingularMultiMap<String, SymbolCollectingProcessor.ResultWithContext> results = p.getResults();
return Result.create(results, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
}
private static class MyResolveCacheProcessor implements Processor<SymbolCollectingProcessor.ResultWithContext> {
private final PsiScopeProcessor myProcessor;
private final ResolveState myState;
public MyResolveCacheProcessor(PsiScopeProcessor processor, ResolveState state) {
myProcessor = processor;
myState = state;
}
@Override
public boolean process(SymbolCollectingProcessor.ResultWithContext result) {
myProcessor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, result.getFileContext());
return myProcessor.execute(result.getElement(), myState);
}
}
}