| package org.jetbrains.android.dom.resources; |
| |
| import com.android.resources.ResourceFolderType; |
| import com.android.resources.ResourceType; |
| import com.android.tools.idea.rendering.ResourceNameValidator; |
| import com.intellij.codeInspection.LocalQuickFix; |
| import com.intellij.codeInspection.LocalQuickFixProvider; |
| import com.intellij.lang.java.lexer.JavaLexer; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.pom.java.LanguageLevel; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiReference; |
| import com.intellij.util.xml.*; |
| import org.jetbrains.android.dom.converters.AndroidResourceReferenceBase; |
| import org.jetbrains.android.facet.AndroidFacet; |
| import org.jetbrains.android.inspections.CreateValueResourceQuickFix; |
| import org.jetbrains.android.resourceManagers.LocalResourceManager; |
| import org.jetbrains.android.resourceManagers.ValueResourceInfoImpl; |
| import org.jetbrains.android.util.AndroidBundle; |
| import org.jetbrains.android.util.AndroidResourceUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * @author Eugene.Kudelevsky |
| */ |
| public class ResourceNameConverter extends ResolvingConverter<String> implements CustomReferenceConverter<String> { |
| @Override |
| public String fromString(@Nullable @NonNls String s, ConvertContext context) { |
| if (s == null) { |
| return null; |
| } |
| String fieldName = AndroidResourceUtil.getFieldNameByResourceName(s); |
| return StringUtil.isJavaIdentifier(fieldName) && !JavaLexer.isKeyword(fieldName, LanguageLevel.JDK_1_5) ? s : null; |
| } |
| |
| @Override |
| public String toString(@Nullable String s, ConvertContext context) { |
| return s; |
| } |
| |
| @Override |
| public String getErrorMessage(@Nullable String s, ConvertContext context) { |
| if (s != null) { |
| String message = ResourceNameValidator.create(false, ResourceFolderType.VALUES).getErrorText(s); |
| if (message != null) { |
| return message; |
| } |
| } |
| |
| return AndroidBundle.message("invalid.resource.name.error", s); |
| } |
| |
| @NotNull |
| @Override |
| public Collection<? extends String> getVariants(ConvertContext context) { |
| final DomElement element = context.getInvocationElement(); |
| |
| if (!(element instanceof GenericAttributeValue)) { |
| return Collections.emptyList(); |
| } |
| if (element.getParent() instanceof Style) { |
| return getStyleNameVariants(context, (GenericAttributeValue)element); |
| } |
| return Collections.emptyList(); |
| } |
| |
| private static Collection<? extends String> getStyleNameVariants(ConvertContext context, GenericAttributeValue element) { |
| final Module module = context.getModule(); |
| |
| if (module == null) { |
| return Collections.emptyList(); |
| } |
| final LocalResourceManager manager = LocalResourceManager.getInstance(module); |
| |
| if (manager == null) { |
| return Collections.emptyList(); |
| } |
| final Collection<String> styleNames = manager.getResourceNames(ResourceType.STYLE.getName()); |
| final List<String> result = new ArrayList<String>(); |
| |
| final String currentValue = element.getStringValue(); |
| for (String name : styleNames) { |
| if (currentValue == null || !currentValue.startsWith(name)) { |
| result.add(name + '.'); |
| } |
| } |
| return result; |
| } |
| |
| @NotNull |
| @Override |
| public PsiReference[] createReferences(GenericDomValue<String> value, PsiElement element, ConvertContext context) { |
| final Module module = context.getModule(); |
| |
| if (module == null) { |
| return PsiReference.EMPTY_ARRAY; |
| } |
| AndroidFacet facet = AndroidFacet.getInstance(module); |
| |
| if (facet == null) { |
| return PsiReference.EMPTY_ARRAY; |
| } |
| final DomElement parent = value.getParent(); |
| |
| if (parent instanceof Style) { |
| return getReferencesInStyleName((Style)parent, value, facet); |
| } |
| return PsiReference.EMPTY_ARRAY; |
| } |
| |
| private static PsiReference[] getReferencesInStyleName(@NotNull Style style, |
| @NotNull GenericDomValue<String> value, |
| @NotNull AndroidFacet facet) { |
| final String s = value.getStringValue(); |
| |
| if (s == null) { |
| return PsiReference.EMPTY_ARRAY; |
| } |
| final String[] ids = s.split("\\."); |
| if (ids.length < 2 || |
| style.getParentStyle().getStringValue() != null) { |
| return PsiReference.EMPTY_ARRAY; |
| } |
| final List<PsiReference> result = new ArrayList<PsiReference>(ids.length - 1); |
| int offset = s.length(); |
| |
| for (int i = ids.length - 1; i >= 0; i--) { |
| if (i < ids.length - 1) { |
| final String parentStyleName = s.substring(0, offset); |
| final ResourceValue val = ResourceValue.referenceTo((char)0, null, ResourceType.STYLE.getName(), parentStyleName); |
| result.add(new MyParentStyleReference(value, new TextRange(1, 1 + offset), val, facet)); |
| |
| if (hasExplicitParent(facet, parentStyleName)) { |
| break; |
| } |
| } |
| offset = offset - ids[i].length() - 1; |
| } |
| return result.toArray(new PsiReference[result.size()]); |
| } |
| |
| public static boolean hasExplicitParent(@NotNull AndroidFacet facet, @NotNull String localStyleName) { |
| final List<ValueResourceInfoImpl> styles = facet.getLocalResourceManager(). |
| findValueResourceInfos(ResourceType.STYLE.getName(), localStyleName, true, false); |
| |
| if (styles.size() == 0) { |
| return false; |
| } |
| // all resolved styles have explicit parents |
| for (ValueResourceInfoImpl info : styles) { |
| final ResourceElement domElement = info.computeDomElement(); |
| |
| if (!(domElement instanceof Style) || ((Style)domElement).getParentStyle().getStringValue() == null) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public static class MyParentStyleReference extends AndroidResourceReferenceBase implements LocalQuickFixProvider { |
| |
| public MyParentStyleReference(@NotNull GenericDomValue value, |
| @Nullable TextRange range, |
| @NotNull ResourceValue resourceValue, |
| @NotNull AndroidFacet facet) { |
| super(value, range, resourceValue, facet); |
| } |
| |
| @Override |
| public LocalQuickFix[] getQuickFixes() { |
| final String resourceName = getValue(); |
| |
| if (resourceName.length() == 0) { |
| return LocalQuickFix.EMPTY_ARRAY; |
| } |
| final PsiFile psiFile = getElement().getContainingFile(); |
| if (psiFile == null) { |
| return LocalQuickFix.EMPTY_ARRAY; |
| } |
| return new LocalQuickFix[] {new CreateValueResourceQuickFix(myFacet, ResourceType.STYLE, resourceName, psiFile, false)}; |
| } |
| } |
| } |