Files
openide/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java
Mikhail Pyltsin ecff6e161b IJ-CR-138822 [java-highlighting] IDEA-355777 Support JEP 477: implicit imports
- cache implicit static references
- extract ImplicitlyImportedStaticMember into a separate file

GitOrigin-RevId: 105a69ce72b4722f0d32d1d858c426e96b73f9c5
2024-07-05 13:55:46 +00:00

653 lines
28 KiB
Java

// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 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.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NotNullLazyKey;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.text.Strings;
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.PsiClassImplUtil;
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.resolve.SymbolCollectingProcessor.ResultWithContext;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.scope.*;
import com.intellij.psi.scope.processor.MethodsProcessor;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.psi.util.CachedValueProvider.Result;
import com.intellij.util.FileContentUtilCore;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.containers.MostlySingularMultiMap;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.execution.ParametersListUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJavaFile {
private static final Logger LOG = Logger.getInstance(PsiJavaFileBaseImpl.class);
private static final String[] IMPLICIT_IMPORTS = { CommonClassNames.DEFAULT_PACKAGE };
private final CachedValue<MostlySingularMultiMap<String, ResultWithContext>> myResolveCache;
private final CachedValue<Map<String, Iterable<ResultWithContext>>> myCachedDeclarations;
private final CachedValue<List<PsiImportStaticStatement>> myCachedImplicitImportStaticStatements;
private volatile String myPackageName;
protected PsiJavaFileBaseImpl(@NotNull IElementType elementType, @NotNull IElementType contentElementType, @NotNull FileViewProvider viewProvider) {
super(elementType, contentElementType, viewProvider);
CachedValuesManager cachedValuesManager = CachedValuesManager.getManager(myManager.getProject());
myResolveCache = cachedValuesManager.createCachedValue(new MyCacheBuilder(this), false);
myCachedDeclarations = cachedValuesManager.createCachedValue(() -> {
Map<String, Iterable<ResultWithContext>> declarations = findEnumeratedDeclarations();
if (!this.isPhysical()) {
return Result.create(declarations, this.getContainingFile(), PsiModificationTracker.MODIFICATION_COUNT);
}
return Result.create(declarations, PsiModificationTracker.MODIFICATION_COUNT);
}, false);
myCachedImplicitImportStaticStatements = cachedValuesManager.createCachedValue(() -> {
List<PsiImportStaticStatement> statements = createImplicitImportStaticStatements();
if (!this.isPhysical()) {
return Result.create(statements, this.getContainingFile(), PsiModificationTracker.MODIFICATION_COUNT);
}
return Result.create(statements, PsiModificationTracker.MODIFICATION_COUNT);
}, false);
}
@Override
public void subtreeChanged() {
super.subtreeChanged();
myPackageName = null;
}
@Override
public PsiClass @NotNull [] getClasses() {
return withGreenStubOrAst(
stub -> stub.getChildrenByType(Constants.CLASS_BIT_SET, PsiClass.ARRAY_FACTORY),
ast -> ast.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
public @NotNull String getPackageName() {
return withGreenStubOrAst(
PsiJavaFileStub.class,
stub -> stub.getPackageName(),
ast -> {
String name = myPackageName;
if (name == null) {
PsiPackageStatement statement = getPackageStatement();
myPackageName = name = statement == null ? "" : statement.getPackageName();
}
return name;
}
);
}
@Override
public void setPackageName(@NotNull String packageName) throws IncorrectOperationException {
if (PsiUtil.isModuleFile(this)) {
throw new IncorrectOperationException("Cannot set package name for module declarations");
}
PsiPackageStatement packageStatement = getPackageStatement();
PsiElementFactory factory = JavaPsiFacade.getElementFactory(getProject());
if (packageStatement != null) {
if (!packageName.isEmpty()) {
PsiJavaCodeReferenceElement reference = packageStatement.getPackageReference();
reference.replace(factory.createPackageStatement(packageName).getPackageReference());
}
else {
packageStatement.delete();
}
}
else if (!packageName.isEmpty()) {
cleanupBrokenPackageKeyword();
PsiElement anchor = getFirstChild();
if (PsiPackage.PACKAGE_INFO_FILE.equals(getName())) {
// If javadoc is already present in a package-info.java file, try to position the new package statement after it,
// so the package becomes documented.
anchor = getImportList();
assert anchor != null; // import list always available inside package-info.java
PsiElement prev = anchor.getPrevSibling();
if (prev instanceof PsiComment) {
String text = prev.getText().trim();
if (text.startsWith("/*") && !text.endsWith("*/")) {
// close any open javadoc/comments before the import list
prev.replace(factory.createCommentFromText(text + (StringUtil.containsLineBreak(text) ? "\n*/" : " */"), prev));
}
}
}
addBefore(factory.createPackageStatement(packageName), anchor);
}
}
private void cleanupBrokenPackageKeyword() {
PsiElement child = getFirstChild();
while (child instanceof PsiWhiteSpace || child instanceof PsiComment || child instanceof PsiErrorElement) {
if (child instanceof PsiErrorElement && child.getFirstChild() != null && child.getFirstChild().textMatches(PsiKeyword.PACKAGE)) {
child.delete();
break;
}
child = child.getNextSibling();
}
}
@Override
public PsiImportList getImportList() {
return withGreenStubOrAst(
stub -> {
PsiImportList[] nodes = stub.getChildrenByType(JavaStubElementTypes.IMPORT_LIST, PsiImportList.ARRAY_FACTORY);
if (nodes.length == 1) return nodes[0];
assert nodes.length == 0;
return null;
},
ast -> {
ASTNode node = ast.findChildByType(JavaElementType.IMPORT_LIST);
return (PsiImportList)SourceTreeToPsiMap.treeElementToPsi(node);
}
);
}
@Override
public PsiElement @NotNull [] getOnDemandImports(boolean includeImplicit, boolean checkIncludes) {
PsiImportList importList = getImportList();
if (importList == null) return EMPTY_ARRAY;
List<PsiElement> array = new ArrayList<>();
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) {
PsiElement resolved = implicitRef.resolve();
if (resolved != null) {
array.add(resolved);
}
}
}
return PsiUtilCore.toPsiElementArray(array);
}
@Override
public PsiClass @NotNull [] getSingleClassImports(boolean checkIncludes) {
PsiImportList importList = getImportList();
if (importList == null) return PsiClass.EMPTY_ARRAY;
List<PsiClass> array = new ArrayList<>();
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(PsiClass.EMPTY_ARRAY);
}
@Override
public PsiJavaCodeReferenceElement findImportReferenceTo(@NotNull PsiClass aClass) {
PsiImportList importList = getImportList();
if (importList != null) {
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
public String @NotNull [] getImplicitlyImportedPackages() {
return IMPLICIT_IMPORTS;
}
@Override
public PsiJavaCodeReferenceElement @NotNull [] getImplicitlyImportedPackageReferences() {
return PsiImplUtil.namesToPackageReferences(myManager, IMPLICIT_IMPORTS);
}
private static class StaticImportFilteringProcessor extends DelegatingScopeProcessor {
private final Map<String, Iterable<ResultWithContext>> myExplicitlyEnumerated;
private final Collection<PsiElement> myCollectedElements = new HashSet<>();
StaticImportFilteringProcessor(@NotNull Map<String, Iterable<ResultWithContext>> explicitlyEnumerated, @NotNull PsiScopeProcessor delegate) {
super(delegate);
myExplicitlyEnumerated = explicitlyEnumerated;
}
@Override
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
if (element instanceof PsiModifierListOwner && ((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) {
PsiScopeProcessor delegate = getDelegate();
if (element instanceof PsiNamedElement) {
String name = ((PsiNamedElement)element).getName();
Iterable<ResultWithContext> shadowing = myExplicitlyEnumerated.get(name);
if (shadowing != null && ContainerUtil.exists(shadowing, rwc -> hasSameDeclarationKind(element, rwc.getElement()))) return true;
if (delegate instanceof MethodsProcessor && element instanceof PsiMethod) {
PsiClass containingClass = ((PsiMethod)element).getContainingClass();
if (containingClass != null && containingClass.isInterface()) {
PsiElement currentFileContext = ((MethodsProcessor)delegate).getCurrentFileContext();
if (currentFileContext instanceof PsiImportStaticStatement &&
((PsiImportStaticStatement)currentFileContext).isOnDemand() &&
!containingClass.isEquivalentTo(((PsiImportStaticStatement)currentFileContext).resolveTargetClass())) {
return true;
}
}
}
}
if (myCollectedElements.add(element)) {
return delegate.execute(element, state);
}
}
return true;
}
private static boolean hasSameDeclarationKind(PsiElement e1, PsiElement e2) {
return e1 instanceof PsiClass ? e2 instanceof PsiClass : e1 instanceof PsiMethod ? e2 instanceof PsiMethod : e2 instanceof PsiField;
}
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
PsiElement lastParent,
@NotNull PsiElement place) {
NameHint nameHint = processor.getHint(NameHint.KEY);
String name = nameHint != null ? nameHint.getName(state) : null;
Map<String, Iterable<ResultWithContext>> explicitlyEnumerated = getEnumeratedDeclarations();
//noinspection unchecked
Iterable<ResultWithContext> iterable = name != null ? explicitlyEnumerated.get(name)
: ContainerUtil.concat(explicitlyEnumerated.values().toArray(new Iterable[0]));
if (iterable != null && !ContainerUtil.process(iterable, new MyResolveCacheProcessor(state, processor))) return false;
if (processor instanceof ClassResolverProcessor &&
(getUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING) == Boolean.TRUE || myResolveCache.hasUpToDateValue()) &&
!PsiUtil.isInsideJavadocComment(place)) {
MostlySingularMultiMap<String, ResultWithContext> cache = myResolveCache.getValue();
MyResolveCacheProcessor cacheProcessor = new MyResolveCacheProcessor(state, processor);
return name != null ? cache.processForKey(name, cacheProcessor) : cache.processAllValues(cacheProcessor);
}
return processOnDemandPackages(state, place, processor);
}
private @NotNull Map<String, Iterable<ResultWithContext>> getEnumeratedDeclarations() {
return myCachedDeclarations.getValue();
}
private @NotNull Map<String, Iterable<ResultWithContext>> findEnumeratedDeclarations() {
MultiMap<String, PsiClass> ownClasses = MultiMap.create();
MultiMap<String, PsiImportStatement> typeImports = MultiMap.create();
MultiMap<String, PsiImportStaticStatement> staticImports = MultiMap.create();
for (PsiClass psiClass : getClasses()) {
if (psiClass instanceof PsiImplicitClass) continue;
String name = psiClass.getName();
if (name != null) {
ownClasses.putValue(name, psiClass);
}
}
for (PsiImportStatement anImport : getImportStatements()) {
if (!anImport.isOnDemand()) {
String qName = anImport.getQualifiedName();
if (qName != null) {
typeImports.putValue(StringUtil.getShortName(qName), anImport);
}
}
}
for (PsiImportStaticStatement staticImport : ContainerUtil.append(getImplicitImportStaticStatements(), getImportStaticStatements())) {
String name = staticImport.getReferenceName();
if (name != null) {
staticImports.putValue(name, staticImport);
}
}
Map<String, Iterable<ResultWithContext>> result = new LinkedHashMap<>();
for (String name : ContainerUtil.newLinkedHashSet(
ContainerUtil.concat(ownClasses.keySet(), typeImports.keySet(), staticImports.keySet()))) {
NotNullLazyValue<Iterable<ResultWithContext>> lazy =
NotNullLazyValue.volatileLazy(() -> findExplicitDeclarations(name, ownClasses, typeImports, staticImports));
result.put(name, () -> lazy.getValue().iterator());
}
return result;
}
private static @NotNull Iterable<ResultWithContext> findExplicitDeclarations(@NotNull String name,
@NotNull MultiMap<String, PsiClass> ownClasses,
@NotNull MultiMap<String, PsiImportStatement> typeImports,
@NotNull MultiMap<String, PsiImportStaticStatement> staticImports) {
List<ResultWithContext> result = new ArrayList<>();
for (PsiClass psiClass : ownClasses.get(name)) {
result.add(new ResultWithContext(psiClass, null));
}
for (PsiImportStatement statement : typeImports.get(name)) {
PsiElement target = statement.resolve();
if (target == null || target instanceof PsiClass) {
result.add(new ResultWithContext((PsiNamedElement)target, statement));
}
}
for (PsiImportStaticStatement statement : staticImports.get(name)) {
PsiJavaCodeReferenceElement reference = statement.getImportReference();
if (reference != null) {
JavaResolveResult[] targets = reference.multiResolve(false);
if (targets.length == 0) {
result.add(new ResultWithContext(null, statement));
} else {
for (JavaResolveResult target : targets) {
PsiElement element = target.getElement();
if (element instanceof PsiNamedElement) {
result.add(new ResultWithContext((PsiNamedElement)element, statement));
}
}
}
}
}
return JBIterable.from(result).unique(ResultWithContext::getElement);
}
private boolean processOnDemandPackages(@NotNull ResolveState state, @NotNull PsiElement place, @NotNull PsiScopeProcessor processor) {
ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
boolean shouldProcessClasses = classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS);
if (shouldProcessClasses && !processCurrentPackage(state, place, processor)) return false;
if (!processOnDemandStaticImports(state, new StaticImportFilteringProcessor(getEnumeratedDeclarations(), processor))) {
return false;
}
if (shouldProcessClasses && !processOnDemandTypeImports(state, place, processor)) return false;
return !shouldProcessClasses || processImplicitImports(state, place, processor);
}
private PsiImportStaticStatement @NotNull [] getImportStaticStatements() {
return getImportList() != null ? getImportList().getImportStaticStatements() : PsiImportStaticStatement.EMPTY_ARRAY;
}
private PsiImportStatement @NotNull [] getImportStatements() {
return getImportList() != null ? getImportList().getImportStatements() : PsiImportStatement.EMPTY_ARRAY;
}
private boolean processCurrentPackage(@NotNull ResolveState state, @NotNull PsiElement place, @NotNull PsiScopeProcessor processor) {
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, null);
PsiPackage aPackage = JavaPsiFacade.getInstance(myManager.getProject()).findPackage(getPackageName());
return aPackage == null || processPackageDeclarations(state, place, aPackage, processor);
}
private boolean processOnDemandTypeImports(@NotNull ResolveState state, @NotNull PsiElement place, @NotNull PsiScopeProcessor processor) {
for (PsiImportStatement statement : getImportStatements()) {
if (statement.isOnDemand()) {
PsiElement resolved = statement.resolve();
if (resolved != null) {
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, statement);
if (!processOnDemandTarget(resolved, state, place, processor)) return false;
}
}
}
return true;
}
private boolean processOnDemandStaticImports(@NotNull ResolveState state, @NotNull StaticImportFilteringProcessor processor) {
for (PsiImportStaticStatement importStaticStatement : ContainerUtil.append(getImplicitImportStaticStatements(), getImportStaticStatements())) {
if (!importStaticStatement.isOnDemand()) continue;
PsiClass targetElement = importStaticStatement.resolveTargetClass();
if (targetElement != null) {
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, importStaticStatement);
if (!PsiClassImplUtil.processAllMembersWithoutSubstitutors(targetElement, processor, state)) return false;
}
}
return true;
}
private boolean processImplicitImports(@NotNull ResolveState state, @NotNull PsiElement place, @NotNull PsiScopeProcessor processor) {
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, null);
for (PsiJavaCodeReferenceElement aImplicitlyImported : getImplicitlyImportedPackageReferences()) {
PsiElement resolved = aImplicitlyImported.resolve();
if (resolved != null) {
if (!processOnDemandTarget(resolved, state, place, processor)) return false;
}
}
return true;
}
private static boolean processPackageDeclarations(@NotNull ResolveState state,
@NotNull PsiElement place,
@NotNull PsiPackage aPackage,
@NotNull PsiScopeProcessor processor) {
if (!aPackage.getQualifiedName().isEmpty()) {
processor = new DelegatingScopeProcessor(processor) {
@Override
public @Nullable <T> T getHint(@NotNull Key<T> hintKey) {
if (hintKey == ElementClassHint.KEY) {
//noinspection unchecked
return (T)(ElementClassHint)kind -> kind == ElementClassHint.DeclarationKind.CLASS;
}
return super.getHint(hintKey);
}
};
}
return aPackage.processDeclarations(processor, state, null, place);
}
private static @NotNull PsiSubstitutor createRawSubstitutor(@NotNull PsiClass containingClass) {
return JavaPsiFacade.getElementFactory(containingClass.getProject()).createRawSubstitutor(containingClass);
}
private static boolean processOnDemandTarget(@NotNull PsiElement target, @NotNull ResolveState substitutor, @NotNull PsiElement place, @NotNull PsiScopeProcessor processor) {
if (target instanceof PsiPackage) {
return processPackageDeclarations(substitutor, place, (PsiPackage)target, processor);
}
if (target instanceof PsiClass) {
PsiClass[] inners = ((PsiClass)target).getInnerClasses();
if (((PsiClass)target).hasTypeParameters()) {
substitutor = substitutor.put(PsiSubstitutor.KEY, createRawSubstitutor((PsiClass)target));
}
for (PsiClass inner : inners) {
if (!processor.execute(inner, substitutor)) return false;
}
}
else {
LOG.error("Unexpected target type: " + target);
}
return true;
}
@Override
public void accept(@NotNull PsiElementVisitor visitor){
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitJavaFile(this);
}
else {
visitor.visitFile(this);
}
}
@Override
public @NotNull Language getLanguage() {
return JavaLanguage.INSTANCE;
}
@Override
public boolean importClass(@NotNull PsiClass aClass) {
return JavaCodeStyleManager.getInstance(getProject()).addImport(this, aClass);
}
private static final NotNullLazyKey<LanguageLevel, PsiJavaFileBaseImpl> LANGUAGE_LEVEL_KEY = NotNullLazyKey.createLazyKey("LANGUAGE_LEVEL",
file -> file.getLanguageLevelInner());
@Override
public @NotNull LanguageLevel getLanguageLevel() {
return LANGUAGE_LEVEL_KEY.getValue(this);
}
@Override
public @Nullable PsiJavaModule getModuleDeclaration() {
return null;
}
@Override
public @NotNull ImplicitlyImportedStaticMember @NotNull [] getImplicitlyImportedStaticMembers() {
return PsiImplUtil.getImplicitStaticImports(this);
}
@Override
public void clearCaches() {
super.clearCaches();
putUserData(LANGUAGE_LEVEL_KEY, null);
}
@Override
public void setOriginalFile(@NotNull PsiFile originalFile) {
super.setOriginalFile(originalFile);
clearCaches();
}
@NotNull
private List<PsiImportStaticStatement> getImplicitImportStaticStatements() {
return myCachedImplicitImportStaticStatements.getValue();
}
@NotNull
private List<PsiImportStaticStatement> createImplicitImportStaticStatements() {
PsiElementFactory factory = PsiElementFactory.getInstance(getProject());
return ContainerUtil.map(getImplicitlyImportedStaticMembers(),
member -> factory.createImportStaticStatementFromText(member.getContainingClass(), member.getMemberName()));
}
private static final Key<String> SHEBANG_SOURCE_LEVEL = Key.create("SHEBANG_SOURCE_LEVEL");
private @NotNull 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 = getViewProvider().getVirtualFile();
String sourceLevel = null;
try {
CharSequence contents = getViewProvider().getContents();
int lineBound = Strings.indexOf(contents, "\n");
CharSequence line = lineBound > 0 ? contents.subSequence(0, lineBound) : contents;
if (Strings.startsWith(line, 0,"#!")) {
List<String> params = ParametersListUtil.parse(line.toString());
int srcIdx = params.indexOf("--source");
if (srcIdx > 0 && srcIdx + 1 < params.size()) {
sourceLevel = params.get(srcIdx + 1);
LanguageLevel sheBangLevel = LanguageLevel.parse(sourceLevel);
if (sheBangLevel != null) {
return sheBangLevel;
}
}
}
}
catch (Throwable ignored) {
}
finally {
if (!Objects.equals(sourceLevel, virtualFile.getUserData(SHEBANG_SOURCE_LEVEL)) && virtualFile.isInLocalFileSystem()) {
virtualFile.putUserData(SHEBANG_SOURCE_LEVEL, sourceLevel);
VirtualFile file = virtualFile;
ApplicationManager.getApplication().invokeLater(() -> FileContentUtilCore.reparseFiles(file),
ModalityState.nonModal(),
ApplicationManager.getApplication().getDisposed());
}
}
return JavaPsiImplementationHelper.getInstance(getProject()).getEffectiveLanguageLevel(virtualFile);
}
private static class MyCacheBuilder implements CachedValueProvider<MostlySingularMultiMap<String, ResultWithContext>> {
private final @NotNull PsiJavaFileBaseImpl myFile;
MyCacheBuilder(@NotNull PsiJavaFileBaseImpl file) {
myFile = file;
}
@Override
public @NotNull Result<MostlySingularMultiMap<String, ResultWithContext>> compute() {
SymbolCollectingProcessor p = new SymbolCollectingProcessor();
myFile.processOnDemandPackages(ResolveState.initial(), myFile, p);
MostlySingularMultiMap<String, ResultWithContext> results = p.getResults();
return Result.create(results, PsiModificationTracker.MODIFICATION_COUNT, myFile);
}
}
private static class MyResolveCacheProcessor implements Processor<ResultWithContext> {
private final PsiScopeProcessor myProcessor;
private final ResolveState myState;
MyResolveCacheProcessor(@NotNull ResolveState state, @NotNull PsiScopeProcessor processor) {
myProcessor = processor;
myState = state;
}
@Override
public boolean process(@NotNull ResultWithContext result) {
PsiElement context = result.getFileContext();
myProcessor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, context);
PsiNamedElement element = result.getElement();
if (element == null) {
return myProcessor.executeForUnresolved();
}
if (element instanceof PsiClass && context instanceof PsiImportStatement) {
PsiClass containingClass = ((PsiClass)element).getContainingClass();
if (containingClass != null && containingClass.hasTypeParameters()) {
return myProcessor.execute(element, myState.put(PsiSubstitutor.KEY, createRawSubstitutor(containingClass)));
}
}
return myProcessor.execute(element, myState);
}
}
}