IDEADEV-41031

This commit is contained in:
Alexey Kudravtsev
2009-10-27 17:23:31 +03:00
parent 08206aacb2
commit fee45016e6
3 changed files with 155 additions and 76 deletions
@@ -413,7 +413,7 @@ public class JavaPsiFacadeImpl extends JavaPsiFacadeEx implements Disposable {
@NotNull
public PsiClass[] getClasses(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
ArrayList<PsiClass> list = new ArrayList<PsiClass>();
List<PsiClass> list = new ArrayList<PsiClass>();
final PsiDirectory[] dirs = psiPackage.getDirectories(scope);
for (PsiDirectory dir : dirs) {
PsiClass[] classes = JavaDirectoryService.getInstance().getClasses(dir);
@@ -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<String> classesToUseSingle = findSingleImports(file, resultList, classesOrPackagesToImportOnDemand);
Set<String> toReimport = new THashSet<String>();
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<String> implicitlyImportedPackages = new THashSet<String>(Arrays.asList(file.getImplicitlyImportedPackages()));
final PsiManager manager = file.getManager();
JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
List<PsiElement> onDemandElements = new ArrayList<PsiElement>(onDemandImports.size());
List<String> onDemandImportsList = new ArrayList<String>(onDemandImports);
for (String onDemandName : onDemandImportsList) {
PsiElement aClass = facade.findClass(onDemandName, resolveScope);
onDemandElements.add(aClass);
}
for (Pair<String, Boolean> 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<String> onDemandImportsList,
GlobalSearchScope resolveScope, final Set<String> namesToUseSingle) {
List<String> onDemands = new ArrayList<String>(Arrays.asList(file.getImplicitlyImportedPackages()));
onDemands.addAll(onDemandImportsList);
if (onDemands.size() < 2) return;
Map<String, Set<String>> classNames = new THashMap<String, Set<String>>();
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<String> set = new THashSet<String>(psiClasses.length);
for (PsiClass psiClass : psiClasses) {
set.add(psiClass.getName());
}
classNames.put(onDemand, set);
}
final Set<String> conflicts = new THashSet<String>();
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<String> inter = new THashSet<String>(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<Pair<String, Boolean>> names,
@NotNull final Set<String> 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<PsiElement> classesToReimport = new ArrayList<PsiElement>();
List<PsiClass> classesToReimport = new ArrayList<PsiClass>();
List<PsiJavaCodeReferenceElement> 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<String> refTexts = new ArrayList<String>(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<PsiClass> classesToReimport,
Collection<String> 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<PsiJavaCodeReferenceElement> getImportsFromPackage(@NotNull PsiJavaFile file, @NotNull String packageName){
PsiClass[] refs = file.getSingleClassImports(true);
@@ -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<ClassCandidateInfo>();
}
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<ClassCandidateInfo>();
}
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> T getHint(Key<T> hintKey) {
if (hintKey == ElementClassHint.KEY || hintKey == NameHint.KEY) {