| package org.jetbrains.android.refactoring; |
| |
| import com.intellij.lang.xml.XMLLanguage; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.command.UndoConfirmationPolicy; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiField; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.xml.XmlTag; |
| import com.intellij.refactoring.BaseRefactoringProcessor; |
| import com.intellij.refactoring.ui.UsageViewDescriptorAdapter; |
| import com.intellij.usageView.UsageInfo; |
| import com.intellij.usageView.UsageViewBundle; |
| import com.intellij.usageView.UsageViewDescriptor; |
| import com.intellij.util.containers.HashSet; |
| import com.intellij.util.containers.MultiMap; |
| import org.jetbrains.android.dom.resources.ResourceNameConverter; |
| import org.jetbrains.android.util.AndroidBundle; |
| import org.jetbrains.android.util.AndroidResourceUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * @author Eugene.Kudelevsky |
| */ |
| class AndroidInlineAllStyleUsagesProcessor extends BaseRefactoringProcessor { |
| private final PsiElement myStyleElement; |
| private final String myStyleName; |
| private final Map<AndroidAttributeInfo, String> myAttributeValues; |
| private final StyleRefData myParentStyleRef; |
| private final XmlTag myStyleTag; |
| private final AndroidInlineTestConfig myTestConfig; |
| |
| protected AndroidInlineAllStyleUsagesProcessor(@NotNull Project project, |
| @NotNull PsiElement styleElement, |
| @NotNull XmlTag styleTag, |
| @NotNull String styleName, |
| @NotNull Map<AndroidAttributeInfo, String> attributeValues, |
| @Nullable StyleRefData parentStyleRef, |
| @Nullable AndroidInlineTestConfig config) { |
| super(project); |
| myStyleElement = styleElement; |
| myStyleTag = styleTag; |
| myStyleName = styleName; |
| myAttributeValues = attributeValues; |
| myParentStyleRef = parentStyleRef; |
| myTestConfig = config; |
| } |
| |
| @NotNull |
| @Override |
| protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) { |
| return new UsageViewDescriptorAdapter() { |
| @NotNull |
| @Override |
| public PsiElement[] getElements() { |
| return new PsiElement[]{myStyleElement}; |
| } |
| |
| @Override |
| public String getCodeReferencesText(int usagesCount, int filesCount) { |
| return "References to be inlined" + UsageViewBundle.getReferencesString(usagesCount, filesCount); |
| } |
| |
| @Override |
| public String getProcessedElementsHeader() { |
| return "Style to inline"; |
| } |
| }; |
| } |
| |
| @NotNull |
| @Override |
| protected UsageInfo[] findUsages() { |
| final Set<UsageInfo> usages = new HashSet<UsageInfo>(); |
| AndroidInlineUtil.addReferences(myStyleElement, usages); |
| |
| for (PsiField field : AndroidResourceUtil.findResourceFieldsForValueResource(myStyleTag, false)) { |
| AndroidInlineUtil.addReferences(field, usages); |
| } |
| return usages.toArray(new UsageInfo[usages.size()]); |
| } |
| |
| |
| @Override |
| protected void performRefactoring(UsageInfo[] usages) { |
| final List<StyleUsageData> inlineInfos = new ArrayList<StyleUsageData>(); |
| |
| for (UsageInfo usage : usages) { |
| final PsiElement element = usage.getElement(); |
| if (element == null) continue; |
| |
| final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); |
| StyleUsageData usageData = tag != null ? AndroidInlineUtil.getStyleUsageData(tag) : null; |
| |
| if (usageData != null && usageData.getReference().computeTargetElements().length == 1) { |
| inlineInfos.add(usageData); |
| } |
| } |
| |
| for (StyleUsageData info : inlineInfos) { |
| info.inline(myAttributeValues, myParentStyleRef); |
| } |
| myStyleTag.delete(); |
| } |
| |
| @Override |
| protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) { |
| final UsageInfo[] usages = refUsages.get(); |
| final MultiMap<PsiElement, String> conflicts = detectConflicts(usages); |
| |
| if (ApplicationManager.getApplication().isUnitTestMode()) { |
| myTestConfig.setConflicts(conflicts); |
| return true; |
| } |
| return showConflicts(conflicts, usages); |
| } |
| |
| private static MultiMap<PsiElement, String> detectConflicts(UsageInfo[] usages) { |
| final List<PsiElement> nonXmlUsages = new ArrayList<PsiElement>(); |
| final List<PsiElement> unsupportedUsages = new ArrayList<PsiElement>(); |
| final List<PsiElement> unambiguousUsages = new ArrayList<PsiElement>(); |
| final List<PsiElement> implicitlyInherited = new ArrayList<PsiElement>(); |
| |
| for (UsageInfo usage : usages) { |
| final PsiElement element = usage.getElement(); |
| if (element == null) continue; |
| |
| if (element.getLanguage() != XMLLanguage.INSTANCE) { |
| nonXmlUsages.add(element); |
| continue; |
| } |
| final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); |
| StyleUsageData usageData = tag != null ? AndroidInlineUtil.getStyleUsageData(tag) : null; |
| |
| if (usageData == null) { |
| if (usage.getReference() instanceof ResourceNameConverter.MyParentStyleReference) { |
| implicitlyInherited.add(element); |
| } |
| else { |
| unsupportedUsages.add(element); |
| } |
| continue; |
| } |
| |
| if (usageData.getReference().computeTargetElements().length > 1) { |
| unambiguousUsages.add(element); |
| } |
| } |
| return AndroidInlineUtil.buildConflicts(nonXmlUsages, unambiguousUsages, unsupportedUsages, implicitlyInherited); |
| } |
| |
| @Override |
| protected String getCommandName() { |
| return AndroidBundle.message("android.inline.style.command.name", myStyleName); |
| } |
| |
| @Override |
| protected UndoConfirmationPolicy getUndoConfirmationPolicy() { |
| // do it because the refactoring can be invoked from UI designer |
| return UndoConfirmationPolicy.REQUEST_CONFIRMATION; |
| } |
| } |