blob: 9521885ec0d6ca4b0b48426a7d5c59362db31b63 [file] [log] [blame]
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;
}
}