From fee45016e6c47a3f13dd82ccab3baaa6ce4b4169 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 27 Oct 2009 14:34:16 +0300 Subject: [PATCH] IDEADEV-41031 --- .../intellij/psi/impl/JavaPsiFacadeImpl.java | 2 +- .../impl/source/codeStyle/ImportHelper.java | 156 +++++++++++++----- .../resolve/ClassResolverProcessor.java | 73 ++++---- 3 files changed, 155 insertions(+), 76 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java b/java/java-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java index e1bab9877917..7900a496594f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java @@ -413,7 +413,7 @@ public class JavaPsiFacadeImpl extends JavaPsiFacadeEx implements Disposable { @NotNull public PsiClass[] getClasses(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { - ArrayList list = new ArrayList(); + List list = new ArrayList(); final PsiDirectory[] dirs = psiPackage.getDirectories(scope); for (PsiDirectory dir : dirs) { PsiClass[] classes = JavaDirectoryService.getInstance().getClasses(dir); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java index 07ac0a68ce1f..4e6d243b4867 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java @@ -38,6 +38,7 @@ import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; +import gnu.trove.THashMap; import gnu.trove.THashSet; import gnu.trove.TObjectIntHashMap; import gnu.trove.TObjectIntProcedure; @@ -123,6 +124,9 @@ public class ImportHelper{ packageToCountMap.forEachEntry(new MyVisitorProcedure(true)); Set classesToUseSingle = findSingleImports(file, resultList, classesOrPackagesToImportOnDemand); + Set toReimport = new THashSet(); + calcClassesConflictingViaOnDemandImports(file, classesOrPackagesToImportOnDemand, file.getResolveScope(), toReimport); + classesToUseSingle.addAll(toReimport); try { StringBuilder text = buildImportListText(resultList, classesOrPackagesToImportOnDemand, classesToUseSingle); @@ -153,6 +157,13 @@ public class ImportHelper{ final String thisPackageName = file.getPackageName(); final Set implicitlyImportedPackages = new THashSet(Arrays.asList(file.getImplicitlyImportedPackages())); final PsiManager manager = file.getManager(); + JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject()); + List onDemandElements = new ArrayList(onDemandImports.size()); + List onDemandImportsList = new ArrayList(onDemandImports); + for (String onDemandName : onDemandImportsList) { + PsiElement aClass = facade.findClass(onDemandName, resolveScope); + onDemandElements.add(aClass); + } for (Pair pair : names) { String name = pair.getFirst(); Boolean isStatic = pair.getSecond(); @@ -169,15 +180,17 @@ public class ImportHelper{ } if (!isImplicitlyImported) { String langPackageClass = JAVA_LANG_PACKAGE + "." + shortName; //TODO : JSP! - if (JavaPsiFacade.getInstance(manager.getProject()).findClass(langPackageClass, resolveScope) != null) { + if (facade.findClass(langPackageClass, resolveScope) != null) { namesToUseSingle.add(name); continue; } } - for (String onDemandName : onDemandImports) { + for (int i = 0; i < onDemandImportsList.size(); i++) { + String onDemandName = onDemandImportsList.get(i); if (prefix.equals(onDemandName)) continue; if (isStatic) { - PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(onDemandName, resolveScope); + PsiElement element = onDemandElements.get(i); + PsiClass aClass = (PsiClass)element; if (aClass != null) { PsiField field = aClass.findFieldByName(shortName, true); if (field != null && field.hasModifierProperty(PsiModifier.STATIC)) { @@ -200,16 +213,62 @@ public class ImportHelper{ } } else { - PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(onDemandName + "." + shortName, resolveScope); + PsiClass aClass = facade.findClass(onDemandName + "." + shortName, resolveScope); if (aClass != null) { namesToUseSingle.add(name); } } } } + return namesToUseSingle; } + private static void calcClassesConflictingViaOnDemandImports(PsiJavaFile file, Collection onDemandImportsList, + GlobalSearchScope resolveScope, final Set namesToUseSingle) { + List onDemands = new ArrayList(Arrays.asList(file.getImplicitlyImportedPackages())); + onDemands.addAll(onDemandImportsList); + if (onDemands.size() < 2) return; + + Map> classNames = new THashMap>(); + JavaPsiFacade facade = JavaPsiFacade.getInstance(file.getProject()); + for (String onDemand : onDemands) { + PsiPackage aPackage = facade.findPackage(onDemand); + if (aPackage == null) continue; + PsiClass[] psiClasses = aPackage.getClasses(resolveScope); + Set set = new THashSet(psiClasses.length); + for (PsiClass psiClass : psiClasses) { + set.add(psiClass.getName()); + } + classNames.put(onDemand, set); + } + + final Set conflicts = new THashSet(); + for (int i = 0; i < onDemands.size(); i++) { + String on1 = onDemands.get(i); + for (int j = i+1; j < onDemands.size(); j++) { + String on2 = onDemands.get(j); + Set inter = new THashSet(classNames.get(on1)); + inter.retainAll(classNames.get(on2)); + + conflicts.addAll(inter); + } + } + if (!conflicts.isEmpty()) { + file.accept(new JavaRecursiveElementVisitor() { + @Override + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { + if (reference.getQualifier() != null) return; + PsiElement element = reference.resolve(); + if (element instanceof PsiClass && conflicts.contains(((PsiClass)element).getName())) { + String fqn = ((PsiClass)element).getQualifiedName(); + namesToUseSingle.add(fqn); + } + } + }); + } + } + @NotNull private static StringBuilder buildImportListText(@NotNull List> names, @NotNull final Set packagesOrClassesToImportOnDemand, @@ -222,7 +281,7 @@ public class ImportHelper{ String packageOrClassName = getPackageOrClassName(name); final boolean implicitlyImported = JAVA_LANG_PACKAGE.equals(packageOrClassName); boolean useOnDemand = implicitlyImported || packagesOrClassesToImportOnDemand.contains(packageOrClassName); - if (useOnDemand && namesToUseSingle.contains(name)) { + if (useOnDemand && namesToUseSingle.remove(name)) { useOnDemand = false; } if (useOnDemand && (importedPackagesOrClasses.contains(packageOrClassName) || implicitlyImported)) continue; @@ -239,6 +298,12 @@ public class ImportHelper{ buffer.append(";\n"); } + for (String remainingSingle : namesToUseSingle) { + buffer.append("import "); + buffer.append(remainingSingle); + buffer.append(";\n"); + } + return buffer; } @@ -276,7 +341,7 @@ public class ImportHelper{ useOnDemand = false; } - List classesToReimport = new ArrayList(); + List classesToReimport = new ArrayList(); List importRefs = getImportsFromPackage(file, packageName); if (useOnDemand){ @@ -292,60 +357,30 @@ public class ImportHelper{ // check conflicts if (useOnDemand){ PsiElement[] onDemandRefs = file.getOnDemandImports(false, true); - if (onDemandRefs.length > 0){ - PsiPackage aPackage = facade.findPackage(packageName); - if (aPackage != null){ - PsiDirectory[] dirs = aPackage.getDirectories(); - for (PsiDirectory dir : dirs) { - PsiFile[] files = dir.getFiles(); // do not iterate classes - too slow when not loaded - for (PsiFile aFile : files) { - if (aFile instanceof PsiJavaFile) { - String name = aFile.getVirtualFile().getNameWithoutExtension(); - for (PsiElement ref : onDemandRefs) { - String refName = ref instanceof PsiClass ? ((PsiClass)ref).getQualifiedName() : ((PsiPackage)ref).getQualifiedName(); - String conflictClassName = refName + "." + name; - GlobalSearchScope resolveScope = file.getResolveScope(); - PsiClass conflictClass = facade.findClass(conflictClassName, resolveScope); - if (conflictClass != null && helper.isAccessible(conflictClass, file, null)) { - String conflictClassName2 = aPackage.getQualifiedName() + "." + name; - PsiClass conflictClass2 = facade.findClass(conflictClassName2, resolveScope); - if (conflictClass2 != null && helper.isAccessible(conflictClass2, file, null)) { - if (ReferencesSearch.search(conflictClass, new LocalSearchScope(file), false).findFirst() != null) { - classesToReimport.add(conflictClass); - } - } - } - } - } - } - } - } + List refTexts = new ArrayList(onDemandRefs.length); + for (PsiElement ref : onDemandRefs) { + String refName = ref instanceof PsiClass ? ((PsiClass)ref).getQualifiedName() : ((PsiPackage)ref).getQualifiedName(); + refTexts.add(refName); } + calcClassesToReimport(file, facade, helper, packageName, classesToReimport, refTexts); } } - try{ + try { PsiImportList importList = file.getImportList(); - PsiImportStatement statement; - if (useOnDemand) { - statement = factory.createImportStatementOnDemand(packageName); - } - else { - statement = factory.createImportStatement(refClass); - } + PsiImportStatement statement = useOnDemand ? factory.createImportStatementOnDemand(packageName) : factory.createImportStatement(refClass); importList.add(statement); if (useOnDemand) { for (PsiJavaCodeReferenceElement ref : importRefs) { LOG.assertTrue(ref.getParent() instanceof PsiImportStatement); if (!ref.isValid()) continue; // todo[dsl] Q? - classesToReimport.add(ref.resolve()); + classesToReimport.add((PsiClass)ref.resolve()); PsiImportStatement importStatement = (PsiImportStatement) ref.getParent(); importStatement.delete(); } } - for (PsiElement aClassesToReimport : classesToReimport) { - PsiClass aClass = (PsiClass)aClassesToReimport; + for (PsiClass aClass : classesToReimport) { if (aClass != null) { addImport(file, aClass); } @@ -357,6 +392,37 @@ public class ImportHelper{ return true; } + private static void calcClassesToReimport(PsiJavaFile file, JavaPsiFacade facade, PsiResolveHelper helper, String packageName, List classesToReimport, + Collection onDemandRefs) { + if (onDemandRefs.isEmpty()) { + return; + } + PsiPackage aPackage = facade.findPackage(packageName); + if (aPackage != null) { + PsiDirectory[] dirs = aPackage.getDirectories(); + GlobalSearchScope resolveScope = file.getResolveScope(); + for (PsiDirectory dir : dirs) { + PsiFile[] files = dir.getFiles(); // do not iterate classes - too slow when not loaded + for (PsiFile aFile : files) { + if (!(aFile instanceof PsiJavaFile)) continue; + String name = aFile.getVirtualFile().getNameWithoutExtension(); + for (String refName : onDemandRefs) { + String conflictClassName = refName + "." + name; + PsiClass conflictClass = facade.findClass(conflictClassName, resolveScope); + if (conflictClass == null || !helper.isAccessible(conflictClass, file, null)) continue; + String conflictClassName2 = packageName + "." + name; + PsiClass conflictClass2 = facade.findClass(conflictClassName2, resolveScope); + if (conflictClass2 != null && + helper.isAccessible(conflictClass2, file, null) && + ReferencesSearch.search(conflictClass, new LocalSearchScope(file), false).findFirst() != null) { + classesToReimport.add(conflictClass); + } + } + } + } + } + } + @NotNull private static List getImportsFromPackage(@NotNull PsiJavaFile file, @NotNull String packageName){ PsiClass[] refs = file.getSingleClassImports(true); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/ClassResolverProcessor.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/ClassResolverProcessor.java index 52494621920a..d68013cbca2c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/ClassResolverProcessor.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/ClassResolverProcessor.java @@ -85,7 +85,8 @@ public class ClassResolverProcessor extends BaseScopeProcessor implements NameHi public void handleEvent(Event event, Object associated) { if (event == JavaScopeProcessorEvent.START_STATIC) { myStaticContext = true; - } else if (event == JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT) { + } + else if (event == JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT) { myCurrentFileContext = (PsiElement)associated; } } @@ -94,36 +95,52 @@ public class ClassResolverProcessor extends BaseScopeProcessor implements NameHi if (!(element instanceof PsiClass)) return true; final PsiClass aClass = (PsiClass)element; final String name = aClass.getName(); - if (myClassName.equals(name)) { - if (myCandidates == null) { - myCandidates = new SmartList(); - } - else { - String fqName = aClass.getQualifiedName(); - if (fqName != null) { - for (ClassCandidateInfo info : myCandidates) { - final PsiClass otherClass = info.getElement(); - assert otherClass != null; - if (fqName.equals(otherClass.getQualifiedName())) { - return true; - } - final PsiClass containingclass1 = aClass.getContainingClass(); - final PsiClass containingclass2 = otherClass.getContainingClass(); - if (containingclass1 != null && containingclass2 != null && containingclass2.isInheritor(containingclass1, true)) { - //shadowing - return true; + if (!myClassName.equals(name)) { + return true; + } + if (myCandidates == null) { + myCandidates = new SmartList(); + } + else { + String fqName = aClass.getQualifiedName(); + if (fqName != null) { + for (int i = myCandidates.size()-1; i>=0; i--) { + ClassCandidateInfo info = myCandidates.get(i); + final PsiClass otherClass = info.getElement(); + assert otherClass != null; + if (fqName.equals(otherClass.getQualifiedName())) { + return true; + } + final PsiClass containingclass1 = aClass.getContainingClass(); + final PsiClass containingclass2 = otherClass.getContainingClass(); + if (containingclass1 != null && containingclass2 != null && containingclass2.isInheritor(containingclass1, true)) { + //shadowing + return true; + } + + // single import wins over on-demand + if (myCurrentFileContext instanceof PsiImportStatementBase && + info.getCurrentFileResolveScope() instanceof PsiImportStatementBase) { + PsiImportStatementBase myImport = (PsiImportStatementBase)myCurrentFileContext; + PsiImportStatementBase otherImport = (PsiImportStatementBase)info.getCurrentFileResolveScope(); + if (myImport.isOnDemand() && !otherImport.isOnDemand()) return true; + if (!myImport.isOnDemand() && otherImport.isOnDemand()) { + myCandidates.remove(i); } } } } - - boolean accessible = myPlace == null || checkAccessibility(aClass); - myHasAccessibleCandidate |= accessible; - myHasInaccessibleCandidate |= !accessible; - myCandidates.add(new ClassCandidateInfo(aClass, state.get(PsiSubstitutor.KEY), !accessible, myCurrentFileContext)); - myResult = null; - //return !accessible; } + + boolean accessible = myPlace == null || checkAccessibility(aClass); + myHasAccessibleCandidate |= accessible; + myHasInaccessibleCandidate |= !accessible; + myCandidates.add(new ClassCandidateInfo(aClass, state.get(PsiSubstitutor.KEY), !accessible, myCurrentFileContext)); + myResult = null; + if (!accessible) return true; + if (!(myCurrentFileContext instanceof PsiImportStatementBase)) return false; + + return true; } @@ -183,10 +200,6 @@ public class ClassResolverProcessor extends BaseScopeProcessor implements NameHi return accessible; } - public void forceResult(JavaResolveResult[] result) { - myResult = result; - } - @Override public T getHint(Key hintKey) { if (hintKey == ElementClassHint.KEY || hintKey == NameHint.KEY) {