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.Listener() { @Override public void jdkAdded(final Sdk sdk) { } @Override public void jdkRemoved(final Sdk sdk) { synchronized (myLock) { myInternalRClasses.remove(sdk); } } @Override public void jdkNameChanged(final Sdk sdk, final String previousName) { } }); } 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(); } }