package org.jetbrains.android.refactoring; import com.android.resources.ResourceType; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomManager; import com.intellij.util.xml.GenericAttributeValue; import org.jetbrains.android.dom.resources.ResourcesDomFileDescription; import org.jetbrains.android.dom.resources.Style; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.resourceManagers.ResourceManager; import org.jetbrains.android.resourceManagers.ValueResourceInfoImpl; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.android.util.ErrorReporter; import org.jetbrains.android.util.ProjectBasedErrorReporter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; /** * @author Eugene.Kudelevsky */ public class AndroidFindStyleApplicationsAction extends AndroidBaseXmlRefactoringAction { private final MyTestConfig myTestConfig; public AndroidFindStyleApplicationsAction() { this(null); } public AndroidFindStyleApplicationsAction(@Nullable MyTestConfig testConfig) { myTestConfig = testConfig; } @Override protected boolean isEnabledForTags(@NotNull XmlTag[] tags) { if (tags.length != 1) { return false; } final MyStyleData data = getStyleData(tags[0]); return data != null && data.getStyle().getItems().size() > 0; } @Override protected boolean isMyFile(PsiFile file) { return DomManager.getDomManager(file.getProject()).getDomFileDescription( (XmlFile)file) instanceof ResourcesDomFileDescription; } @Override protected void doRefactorForTags(@NotNull Project project, @NotNull XmlTag[] tags) { assert tags.length == 1; final XmlTag tag = tags[0]; final MyStyleData styleData = getStyleData(tag); assert styleData != null; doRefactoringForTag(tag, styleData, null, myTestConfig); } static void doRefactoringForTag(XmlTag tag, MyStyleData styleData, @Nullable PsiFile context, MyTestConfig testConfig) { final AndroidFindStyleApplicationsProcessor processor = createFindStyleApplicationsProcessor(tag, styleData, context); if (processor == null) return; final Module module = styleData.getFacet().getModule(); final VirtualFile contextVFile = context != null ? context.getVirtualFile() : null; if (testConfig != null) { processor.configureScope(testConfig.getScope(), contextVFile); processor.run(); return; } processor.setPreviewUsages(true); final boolean showModuleRadio = AndroidFindStyleApplicationsProcessor.getAllModulesToScan(module).size() > 1; if (showModuleRadio || contextVFile != null) { final AndroidFindStyleApplicationsDialog dialog = new AndroidFindStyleApplicationsDialog( contextVFile, processor, showModuleRadio); dialog.show(); } else { processor.run(); } } public static AndroidFindStyleApplicationsProcessor createFindStyleApplicationsProcessor(XmlTag tag, MyStyleData styleData, PsiFile context) { final ErrorReporter errorReporter = new ProjectBasedErrorReporter(tag.getProject()); final Style style = styleData.getStyle(); final Map<AndroidAttributeInfo, String> attrMap = AndroidRefactoringUtil.computeAttributeMap(style, new ProjectBasedErrorReporter(tag.getProject()), AndroidBundle.message("android.find.style.applications.title")); if (attrMap == null || attrMap.size() == 0) { return null; } final AndroidFacet facet = styleData.getFacet(); final StyleRefData parentStyleRef = AndroidRefactoringUtil.getParentStyle(style); PsiElement parentStyleAttrName = null; if (parentStyleRef != null) { parentStyleAttrName = resolveStyleRef(parentStyleRef, facet); if (parentStyleAttrName == null) { errorReporter.report("Cannot resolve parent style '" + parentStyleRef.getStyleName() + "'", AndroidBundle.message("android.find.style.applications.title")); return null; } } return new AndroidFindStyleApplicationsProcessor(styleData.getFacet().getModule(), attrMap, styleData.getName(), tag, styleData.getNameAttrValue(), parentStyleAttrName, context); } private static PsiElement resolveStyleRef(StyleRefData styleRef, AndroidFacet facet) { final ResourceManager resourceManager = facet.getResourceManager(styleRef.getStylePackage()); if (resourceManager == null) { return null; } final List<ValueResourceInfoImpl> infos = resourceManager.findValueResourceInfos( ResourceType.STYLE.getName(), styleRef.getStyleName(), true, false); return infos.size() == 1 ? infos.get(0).computeXmlElement() : null; } @Nullable static MyStyleData getStyleData(@NotNull XmlTag tag) { final DomElement element = DomManager.getDomManager(tag.getProject()).getDomElement(tag); if (!(element instanceof Style)) { return null; } final Style style = (Style)element; final GenericAttributeValue<String> styleNameDomAttr = style.getName(); final String styleName = styleNameDomAttr.getStringValue(); final XmlAttributeValue styleNameAttrValue = styleNameDomAttr.getXmlAttributeValue(); if (styleName == null || styleName.length() == 0 || styleNameAttrValue == null) { return null; } final AndroidFacet facet = AndroidFacet.getInstance(tag); if (facet == null) { return null; } return new MyStyleData(style, styleName, facet, styleNameAttrValue); } static class MyStyleData { private final Style myStyle; private final String myName; private final XmlAttributeValue myNameAttrValue; private final AndroidFacet myFacet; private MyStyleData(@NotNull Style style, @NotNull String name, @NotNull AndroidFacet facet, @NotNull XmlAttributeValue nameAttrValue) { myStyle = style; myName = name; myFacet = facet; myNameAttrValue = nameAttrValue; } @NotNull public Style getStyle() { return myStyle; } @NotNull public String getName() { return myName; } @NotNull public AndroidFacet getFacet() { return myFacet; } @NotNull public XmlAttributeValue getNameAttrValue() { return myNameAttrValue; } } static class MyTestConfig { private final AndroidFindStyleApplicationsProcessor.MyScope myScope; MyTestConfig(@NotNull AndroidFindStyleApplicationsProcessor.MyScope scope) { myScope = scope; } @NotNull public AndroidFindStyleApplicationsProcessor.MyScope getScope() { return myScope; } } }