blob: 89aa23f77fe8709dc9ba1fd9876a676f02dbde67 [file] [log] [blame]
package org.jetbrains.android.augment;
import com.intellij.facet.ProjectFacetManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.reference.SoftReference;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidPsiElementFinder extends PsiElementFinder {
public static final String INTERNAL_PACKAGE_QNAME = "com.android.internal";
public static final String INTERNAL_R_CLASS_QNAME = INTERNAL_PACKAGE_QNAME + ".R";
private final Object myLock = new Object();
private final Map<Sdk, SoftReference<PsiClass>> myInternalRClasses = new HashMap<Sdk, SoftReference<PsiClass>>();
public AndroidPsiElementFinder(@NotNull Project project) {
ApplicationManager.getApplication().getMessageBus().connect(project).subscribe(
ProjectJdkTable.JDK_TABLE_TOPIC, new ProjectJdkTable.Adapter() {
@Override
public void jdkRemoved(final Sdk sdk) {
synchronized (myLock) {
myInternalRClasses.remove(sdk);
}
}
});
}
private boolean processInternalRClasses(@NotNull Project project, @NotNull GlobalSearchScope scope, Processor<PsiClass> processor) {
for (Module module : ModuleManager.getInstance(project).getModules()) {
Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
AndroidPlatform platform = sdk == null ? null : AndroidPlatform.getInstance(sdk);
PsiClass internalRClass = platform == null ? null : getOrCreateInternalRClass(project, sdk, platform);
if (internalRClass != null && scope.contains(internalRClass.getContainingFile().getViewProvider().getVirtualFile())) {
if (!processor.process(internalRClass)) {
return false;
}
}
}
return true;
}
@Nullable
@Override
public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
PsiClass[] classes = findClasses(qualifiedName, scope);
return classes.length == 0 ? null : classes[0];
}
private PsiClass getOrCreateInternalRClass(Project project, Sdk sdk, AndroidPlatform platform) {
synchronized (myLock) {
PsiClass internalRClass = SoftReference.dereference(myInternalRClasses.get(sdk));
if (internalRClass == null) {
internalRClass = new AndroidInternalRClass(PsiManager.getInstance(project), platform, sdk);
myInternalRClasses.put(sdk, new SoftReference<PsiClass>(internalRClass));
}
return internalRClass;
}
}
@NotNull
@Override
public PsiClass[] findClasses(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
Project project = scope.getProject();
if (project == null || !ProjectFacetManager.getInstance(project).hasFacets(AndroidFacet.ID)) {
return PsiClass.EMPTY_ARRAY;
}
if (INTERNAL_R_CLASS_QNAME.equals(qualifiedName)) {
CommonProcessors.CollectUniquesProcessor<PsiClass> processor = new CommonProcessors.CollectUniquesProcessor<PsiClass>();
processInternalRClasses(project, scope, processor);
Collection<PsiClass> results = processor.getResults();
return results.isEmpty() ? PsiClass.EMPTY_ARRAY : results.toArray(new PsiClass[results.size()]);
}
final int lastDot = qualifiedName.lastIndexOf('.');
if (lastDot < 0) {
return PsiClass.EMPTY_ARRAY;
}
final String shortName = qualifiedName.substring(lastDot + 1);
final String parentName = qualifiedName.substring(0, lastDot);
if (shortName.length() == 0 || !parentName.endsWith(".R")) {
return PsiClass.EMPTY_ARRAY;
}
List<PsiClass> result = new SmartList<PsiClass>();
for (PsiClass parentClass : JavaPsiFacade.getInstance(project).findClasses(parentName, scope)) {
ContainerUtil.addIfNotNull(result, parentClass.findInnerClassByName(shortName, false));
}
return result.isEmpty() ? PsiClass.EMPTY_ARRAY : result.toArray(new PsiClass[result.size()]);
}
@NotNull
@Override
public Set<String> getClassNames(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
if (INTERNAL_PACKAGE_QNAME.equals(psiPackage.getQualifiedName())) {
return Collections.singleton("R");
}
return Collections.emptySet();
}
}