| package org.jetbrains.android.dom.resources; |
| |
| import com.intellij.codeInsight.completion.JavaLookupElementBuilder; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.source.resolve.ResolveCache; |
| import com.intellij.psi.search.PsiShortNamesCache; |
| import com.intellij.psi.search.searches.ClassInheritorsSearch; |
| import com.intellij.util.Processor; |
| import com.intellij.util.containers.HashSet; |
| import com.intellij.util.xml.*; |
| import org.jetbrains.android.facet.AndroidFacet; |
| import org.jetbrains.android.util.AndroidUtils; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.Set; |
| |
| /** |
| * @author Eugene.Kudelevsky |
| */ |
| public class DeclareStyleableNameConverter extends Converter<String> implements CustomReferenceConverter<String> { |
| @Override |
| public String fromString(@Nullable @NonNls String s, ConvertContext context) { |
| return s; |
| } |
| |
| @Override |
| public String toString(@Nullable String s, ConvertContext context) { |
| return s; |
| } |
| |
| @NotNull |
| @Override |
| public PsiReference[] createReferences(GenericDomValue<String> value, PsiElement element, ConvertContext context) { |
| final Module module = context.getModule(); |
| if (module != null) { |
| final AndroidFacet facet = AndroidFacet.getInstance(module); |
| if (facet != null) { |
| return new PsiReference[]{new MyReference(facet, value)}; |
| } |
| } |
| return PsiReference.EMPTY_ARRAY; |
| } |
| |
| private static class MyReference extends PsiPolyVariantReferenceBase<PsiElement> { |
| private final GenericDomValue<String> myValue; |
| private final AndroidFacet myFacet; |
| |
| public MyReference(@NotNull AndroidFacet facet, @NotNull GenericDomValue<String> value) { |
| super(DomUtil.getValueElement(value), true); |
| myFacet = facet; |
| myValue = value; |
| } |
| |
| @NotNull |
| @Override |
| public ResolveResult[] multiResolve(boolean incompleteCode) { |
| return ResolveCache.getInstance(myElement.getProject()) |
| .resolveWithCaching(this, new ResolveCache.PolyVariantResolver<MyReference>() { |
| @NotNull |
| @Override |
| public ResolveResult[] resolve(@NotNull MyReference reference, boolean incompleteCode) { |
| return resolveInner(); |
| } |
| }, false, incompleteCode); |
| } |
| |
| private ResolveResult[] resolveInner() { |
| final String value = myValue.getStringValue(); |
| |
| if (value == null || value.length() <= 0) { |
| return ResolveResult.EMPTY_ARRAY; |
| } |
| |
| // Search for custom views with the same name as the declare styleable, such that |
| // you can navigate from the XML styleable declaration to the corresponding custom view |
| final PsiClass[] classes = PsiShortNamesCache.getInstance(myElement.getProject()) |
| .getClassesByName(value, myFacet.getModule().getModuleWithDependenciesAndLibrariesScope(false)); |
| if (classes.length == 0) { |
| return ResolveResult.EMPTY_ARRAY; |
| } |
| final ResolveResult[] result = new ResolveResult[classes.length]; |
| for (int i = 0; i < result.length; i++) { |
| result[i] = new PsiElementResolveResult(classes[i]); |
| } |
| return result; |
| } |
| |
| @NotNull |
| @Override |
| public Object[] getVariants() { |
| final PsiClass viewClass = JavaPsiFacade.getInstance(myElement.getProject()) |
| .findClass(AndroidUtils.VIEW_CLASS_NAME, myFacet.getModule().getModuleWithDependenciesAndLibrariesScope(false)); |
| if (viewClass == null) { |
| return EMPTY_ARRAY; |
| } |
| final Set<Object> shortNames = new HashSet<Object>(); |
| |
| ClassInheritorsSearch.search(viewClass, myFacet.getModule().getModuleWithDependenciesScope(), true). |
| forEach(new Processor<PsiClass>() { |
| @Override |
| public boolean process(PsiClass aClass) { |
| final String name = aClass.getName(); |
| |
| if (name != null) { |
| shortNames.add(JavaLookupElementBuilder.forClass(aClass, name, true)); |
| } |
| return true; |
| } |
| }); |
| return shortNames.toArray(); |
| } |
| } |
| } |