support inline parent styles

This commit is contained in:
Eugene Kudelevsky
2012-08-14 16:42:05 +04:00
parent d1cc2cc87e
commit 9db752387b
18 changed files with 472 additions and 146 deletions
@@ -0,0 +1,70 @@
package org.jetbrains.android.refactoring;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class AndroidAttributeInfo {
private final String myName;
private final String myPackage;
public AndroidAttributeInfo(@NotNull String name, @Nullable String aPackage) {
myName = name;
myPackage = aPackage;
}
public boolean isSystem() {
return AndroidUtils.SYSTEM_RESOURCE_PACKAGE.equals(myPackage);
}
@NotNull
public String getName() {
return myName;
}
@Nullable
public String getPackage() {
return myPackage;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AndroidAttributeInfo info = (AndroidAttributeInfo)o;
if (!myName.equals(info.myName)) {
return false;
}
if (myPackage != null ? !myPackage.equals(info.myPackage) : info.myPackage != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = myName.hashCode();
result = 31 * result + (myPackage != null ? myPackage.hashCode() : 0);
return result;
}
@NotNull
public String getAttributeId() {
return myPackage != null ? myPackage + ":" + myName : myName;
}
@Override
public String toString() {
return getAttributeId();
}
}
@@ -4,7 +4,6 @@ import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
@@ -22,10 +21,6 @@ import com.intellij.usageView.UsageViewBundle;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.OrderedSet;
import com.intellij.util.xml.GenericAttributeValue;
import com.intellij.util.xml.XmlName;
import org.jetbrains.android.dom.converters.AndroidResourceReference;
import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
@@ -40,16 +35,16 @@ import java.util.*;
class AndroidInlineAllStyleUsagesProcessor extends BaseRefactoringProcessor {
private final PsiElement myStyleElement;
private final String myStyleName;
private final Map<XmlName, String> myAttributeValues;
private final AndroidInlineUtil.MyStyleRefData myParentStyleRef;
private final Map<AndroidAttributeInfo, String> myAttributeValues;
private final StyleRefData myParentStyleRef;
private final XmlTag myStyleTag;
protected AndroidInlineAllStyleUsagesProcessor(@NotNull Project project,
@NotNull PsiElement styleElement,
@NotNull XmlTag styleTag,
@NotNull String styleName,
@NotNull Map<XmlName, String> attributeValues,
@Nullable AndroidInlineUtil.MyStyleRefData parentStyleRef) {
@NotNull Map<AndroidAttributeInfo, String> attributeValues,
@Nullable StyleRefData parentStyleRef) {
super(project);
myStyleElement = styleElement;
myStyleTag = styleTag;
@@ -101,7 +96,7 @@ class AndroidInlineAllStyleUsagesProcessor extends BaseRefactoringProcessor {
@Override
protected void performRefactoring(UsageInfo[] usages) {
final List<AndroidInlineUtil.MyStyleUsageData> inlineInfos = new ArrayList<AndroidInlineUtil.MyStyleUsageData>();
final List<StyleUsageData> inlineInfos = new ArrayList<StyleUsageData>();
final List<PsiElement> nonXmlUsages = new ArrayList<PsiElement>();
final List<PsiElement> unsupportedUsages = new ArrayList<PsiElement>();
final List<PsiElement> unambiguousUsages = new ArrayList<PsiElement>();
@@ -115,19 +110,18 @@ class AndroidInlineAllStyleUsagesProcessor extends BaseRefactoringProcessor {
continue;
}
final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class);
final Pair<AndroidResourceReference, GenericAttributeValue<ResourceValue>> pair =
tag != null ? AndroidInlineUtil.findStyleReference(tag) : null;
final StyleUsageData usageData = tag != null ? AndroidInlineUtil.getUsageData(tag) : null;
if (pair == null) {
if (usageData == null) {
unsupportedUsages.add(element);
continue;
}
if (pair.getFirst().computeTargetElements().length > 1) {
if (usageData.getReference().computeTargetElements().length > 1) {
unambiguousUsages.add(element);
continue;
}
inlineInfos.add(new AndroidInlineUtil.MyStyleUsageData(tag, pair.getSecond()));
inlineInfos.add(usageData);
}
if (nonXmlUsages.size() > 0 || unambiguousUsages.size() > 0 || unsupportedUsages.size() > 0) {
@@ -136,8 +130,8 @@ class AndroidInlineAllStyleUsagesProcessor extends BaseRefactoringProcessor {
return;
}
for (AndroidInlineUtil.MyStyleUsageData info : inlineInfos) {
AndroidInlineUtil.inlineStyleUsage(info, myAttributeValues, myParentStyleRef);
for (StyleUsageData info : inlineInfos) {
info.inline(myAttributeValues, myParentStyleRef);
}
myStyleTag.delete();
}
@@ -4,7 +4,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlTag;
import com.intellij.refactoring.inline.InlineOptionsDialog;
import com.intellij.util.xml.XmlName;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -17,8 +16,8 @@ import java.util.Map;
class AndroidInlineStyleDialog extends InlineOptionsDialog {
private final String myStyleName;
private final boolean myInlineThisByDefault;
private final Map<XmlName, String> myAttributeValues;
private final AndroidInlineUtil.MyStyleRefData myParentStyleRef;
private final Map<AndroidAttributeInfo, String> myAttributeValues;
private final StyleRefData myParentStyleRef;
private final XmlTag myStyleTag;
@@ -26,8 +25,8 @@ class AndroidInlineStyleDialog extends InlineOptionsDialog {
@NotNull PsiElement styleElement,
@NotNull XmlTag styleTag,
@NotNull String styleName,
@NotNull Map<XmlName, String> attributeValues,
@Nullable AndroidInlineUtil.MyStyleRefData parentStyleRef,
@NotNull Map<AndroidAttributeInfo, String> attributeValues,
@Nullable StyleRefData parentStyleRef,
boolean inlineThisByDefault,
boolean invokedOnReference) {
super(project, true, styleElement);
@@ -7,13 +7,9 @@ import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomManager;
import org.jetbrains.android.dom.layout.LayoutViewElement;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.util.ErrorReporter;
import org.jetbrains.android.util.HintBasedErrorReporter;
@@ -42,45 +38,33 @@ public class AndroidInlineStyleHandler extends InlineActionHandler {
public boolean canInlineElement(PsiElement element) {
return element != null &&
AndroidFacet.getInstance(element) != null &&
element.getManager().isInProject(element) &&
AndroidInlineUtil.getStyleDataFromContext(element) != null;
AndroidInlineUtil.getInlinableStyleDataFromContext(element) != null;
}
@Override
public void inlineElement(Project project, final Editor editor, PsiElement element) {
final AndroidInlineUtil.MyStyleData data = AndroidInlineUtil.getStyleDataFromContext(element);
final AndroidInlineUtil.MyStyleData data = AndroidInlineUtil.getInlinableStyleDataFromContext(element);
if (data != null) {
final PsiReference reference = editor != null
? TargetElementUtilBase.findReference(editor)
: null;
final PsiElement usageElement = reference != null ? reference.getElement() : null;
final AndroidInlineUtil.MyStyleUsageData usageData = usageElement != null
? getStyleUsageDataFromContext(project, usageElement)
: null;
final ErrorReporter reporter = editor != null
? new HintBasedErrorReporter(editor)
: new ProjectBasedErrorReporter(project);
final StyleUsageData usageData = editor != null ? getUsageDataFromEditor(editor) : null;
final ErrorReporter reporter = editor != null ? new HintBasedErrorReporter(editor) : new ProjectBasedErrorReporter(project);
AndroidInlineUtil.doInlineStyleDeclaration(project, data, usageData, reporter, ourTestConfig);
}
}
@Nullable
private static AndroidInlineUtil.MyStyleUsageData getStyleUsageDataFromContext(@NotNull Project project, @NotNull PsiElement context) {
final XmlTag tag = PsiTreeUtil.getParentOfType(context, XmlTag.class, false);
private static StyleUsageData getUsageDataFromEditor(@NotNull Editor editor) {
final PsiReference reference = TargetElementUtilBase.findReference(editor);
if (tag == null) {
if (reference == null) {
return null;
}
final DomElement element = DomManager.getDomManager(project).getDomElement(tag);
final PsiElement usageElement = reference.getElement();
if (!(element instanceof LayoutViewElement)) {
if (usageElement == null) {
return null;
}
final PsiFile file = tag.getContainingFile();
if (file == null) {
return null;
}
return new AndroidInlineUtil.MyStyleUsageData(tag, ((LayoutViewElement)element).getStyle());
final XmlTag tag = PsiTreeUtil.getParentOfType(usageElement, XmlTag.class, false);
return tag != null ? AndroidInlineUtil.getUsageData(tag) : null;
}
}
@@ -1,17 +1,18 @@
package org.jetbrains.android.refactoring;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomFileDescription;
import com.intellij.util.xml.DomManager;
import com.intellij.util.xml.GenericAttributeValue;
import org.jetbrains.android.dom.converters.AndroidResourceReference;
import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.android.dom.layout.LayoutDomFileDescription;
import org.jetbrains.android.dom.resources.ResourcesDomFileDescription;
import org.jetbrains.android.dom.resources.Style;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidUtils;
@@ -22,7 +23,7 @@ import org.jetbrains.annotations.TestOnly;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidInlineStyleReferenceAction extends AndroidBaseLayoutRefactoringAction {
public class AndroidInlineStyleReferenceAction extends AndroidBaseXmlRefactoringAction {
public static final String ACTION_ID = "AndroidInlineStyleReferenceAction";
private final AndroidInlineTestConfig myTestConfig;
@@ -43,18 +44,12 @@ public class AndroidInlineStyleReferenceAction extends AndroidBaseLayoutRefactor
if (file == null) {
return;
}
final Pair<AndroidResourceReference, GenericAttributeValue<ResourceValue>> pair = AndroidInlineUtil
.findStyleReference(tag);
final StyleUsageData usageData = AndroidInlineUtil.getUsageData(tag);
if (pair == null) {
return;
}
final AndroidResourceReference reference = pair.getFirst();
final GenericAttributeValue<ResourceValue> styleAttribute = pair.getSecond();
if (reference == null || styleAttribute == null) {
if (usageData == null) {
return;
}
final AndroidResourceReference reference = usageData.getReference();
final String title = AndroidBundle.message("android.inline.style.title");
final PsiElement[] styleElements = reference.computeTargetElements();
@@ -83,13 +78,20 @@ public class AndroidInlineStyleReferenceAction extends AndroidBaseLayoutRefactor
return;
}
AndroidInlineUtil.doInlineStyleDeclaration(project, new AndroidInlineUtil.MyStyleData(styleName, style, styleElement),
new AndroidInlineUtil.MyStyleUsageData(tag, styleAttribute),
usageData,
new ProjectBasedErrorReporter(project), myTestConfig);
}
@Override
protected boolean isEnabled(@NotNull XmlTag tag) {
return super.isEnabled(tag) &&
AndroidInlineUtil.findStyleReference(tag) != null;
return AndroidInlineUtil.getUsageData(tag) != null;
}
@Override
protected boolean isMyFile(PsiFile file) {
final DomFileDescription<?> description = DomManager.getDomManager(file.getProject()).getDomFileDescription((XmlFile)file);
return description instanceof LayoutDomFileDescription ||
description instanceof ResourcesDomFileDescription;
}
}
@@ -1,13 +1,10 @@
package org.jetbrains.android.refactoring;
import com.android.resources.ResourceType;
import com.android.sdklib.SdkConstants;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
@@ -19,7 +16,6 @@ import com.intellij.util.containers.HashMap;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomManager;
import com.intellij.util.xml.GenericAttributeValue;
import com.intellij.util.xml.XmlName;
import org.jetbrains.android.dom.converters.AndroidResourceReference;
import org.jetbrains.android.dom.layout.LayoutViewElement;
import org.jetbrains.android.dom.resources.ResourceValue;
@@ -27,6 +23,7 @@ import org.jetbrains.android.dom.resources.Style;
import org.jetbrains.android.dom.resources.StyleItem;
import org.jetbrains.android.dom.wrappers.LazyValueResourceElementWrapper;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.android.util.ErrorReporter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -62,57 +59,54 @@ class AndroidInlineUtil {
}
@Nullable
static Pair<AndroidResourceReference, GenericAttributeValue<ResourceValue>> findStyleReference(@NotNull XmlTag tag) {
final LayoutViewElement viewElement = AndroidBaseLayoutRefactoringAction.getLayoutViewElement(tag);
if (viewElement == null) {
static StyleUsageData getUsageData(@NotNull XmlTag tag) {
final DomElement domElement = DomManager.getDomManager(tag.getProject()).getDomElement(tag);
if (domElement instanceof LayoutViewElement) {
final GenericAttributeValue<ResourceValue> styleAttribute = ((LayoutViewElement)domElement).getStyle();
final AndroidResourceReference reference = getAndroidResourceReference(styleAttribute);
if (reference != null) {
return new ViewStyleUsageData(tag, styleAttribute, reference);
}
}
else if (domElement instanceof Style) {
final AndroidResourceReference reference = getAndroidResourceReference(((Style)domElement).getParentStyle());
if (reference != null) {
return new ParentStyleUsageData((Style)domElement, reference);
}
}
return null;
}
@Nullable
private static AndroidResourceReference getAndroidResourceReference(@Nullable GenericAttributeValue<ResourceValue> attribute) {
if (attribute == null) {
return null;
}
final GenericAttributeValue<ResourceValue> style = viewElement.getStyle();
if (style == null) {
return null;
}
final ResourceValue styleValue = style.getValue();
final ResourceValue styleValue = attribute.getValue();
if (styleValue == null || styleValue.getPackage() != null) {
return null;
}
final XmlAttributeValue styleAttributeValue = style.getXmlAttributeValue();
final XmlAttributeValue styleAttributeValue = attribute.getXmlAttributeValue();
if (styleAttributeValue == null) {
return null;
}
for (PsiReference reference : styleAttributeValue.getReferences()) {
if (reference instanceof AndroidResourceReference) {
return Pair.create((AndroidResourceReference)reference, style);
return (AndroidResourceReference)reference;
}
}
return null;
}
static void inlineStyleUsage(MyStyleUsageData usageData,
Map<XmlName, String> attributeValues,
MyStyleRefData parentStyleRef) {
final XmlTag tag = usageData.myTag;
final GenericAttributeValue<ResourceValue> styleAttribute = usageData.myStyleAttribute;
for (Map.Entry<XmlName, String> entry : attributeValues.entrySet()) {
final XmlName name = entry.getKey();
if (tag.getAttribute(name.getLocalName(), name.getNamespaceKey()) == null) {
tag.setAttribute(name.getLocalName(), name.getNamespaceKey(), entry.getValue());
}
}
styleAttribute.setValue(parentStyleRef != null
? ResourceValue.referenceTo('@', parentStyleRef.myStylePackage, ResourceType.STYLE.getName(),
parentStyleRef.myStyleName)
: null);
}
@Nullable
static Map<XmlName, String> computeAttributeMap(@NotNull Style style, @NotNull ErrorReporter errorReporter) {
final Map<XmlName, String> attributeValues = new HashMap<XmlName, String>();
static Map<AndroidAttributeInfo, String> computeAttributeMap(@NotNull Style style, @NotNull ErrorReporter errorReporter) {
final Map<AndroidAttributeInfo, String> attributeValues = new HashMap<AndroidAttributeInfo, String>();
for (StyleItem item : style.getItems()) {
final String attributeName = item.getName().getStringValue();
@@ -124,13 +118,9 @@ class AndroidInlineUtil {
final int idx = attributeName.indexOf(':');
final String localName = idx >= 0 ? attributeName.substring(idx + 1) : attributeName;
final String nsPrefix = idx >= 0 ? attributeName.substring(0, idx) : null;
String namespace;
if (nsPrefix != null) {
if ("android".equals(nsPrefix)) {
namespace = SdkConstants.NS_RESOURCES;
}
else {
if (!AndroidUtils.SYSTEM_RESOURCE_PACKAGE.equals(nsPrefix)) {
errorReporter.report(RefactoringBundle.getCannotRefactorMessage("Unknown XML attribute prefix '" + nsPrefix + ":'"),
AndroidBundle.message("android.inline.style.title"));
return null;
@@ -142,22 +132,22 @@ class AndroidInlineUtil {
AndroidBundle.message("android.inline.style.title"));
return null;
}
attributeValues.put(new XmlName(localName, namespace), attributeValue);
attributeValues.put(new AndroidAttributeInfo(localName, nsPrefix), attributeValue);
}
return attributeValues;
}
static void doInlineStyleDeclaration(@NotNull Project project,
@NotNull MyStyleData data,
@Nullable final MyStyleUsageData usageData,
@Nullable final StyleUsageData usageData,
@NotNull ErrorReporter errorReporter,
@Nullable AndroidInlineTestConfig testConfig) {
final Style style = data.myStyleElement;
final Map<XmlName, String> attributeValues = computeAttributeMap(style, errorReporter);
final Map<AndroidAttributeInfo, String> attributeValues = computeAttributeMap(style, errorReporter);
if (attributeValues == null) {
return;
}
final MyStyleRefData parentStyleRef = getParentStyle(style);
final StyleRefData parentStyleRef = getParentStyle(style);
boolean inlineThisOnly;
if (testConfig != null) {
@@ -178,7 +168,7 @@ class AndroidInlineUtil {
if (inlineThisOnly) {
assert usageData != null;
final PsiFile file = usageData.myTag.getContainingFile();
final PsiFile file = usageData.getFile();
if (file == null) {
return;
@@ -186,7 +176,7 @@ class AndroidInlineUtil {
new WriteCommandAction(project, AndroidBundle.message("android.inline.style.command.name", data.myStyleName), file) {
@Override
protected void run(final Result result) throws Throwable {
inlineStyleUsage(usageData, attributeValues, parentStyleRef);
usageData.inline(attributeValues, parentStyleRef);
}
@Override
@@ -204,14 +194,14 @@ class AndroidInlineUtil {
}
@Nullable
static MyStyleRefData getParentStyle(@NotNull Style style) {
static StyleRefData getParentStyle(@NotNull Style style) {
final ResourceValue parentStyleRefValue = style.getParentStyle().getValue();
if (parentStyleRefValue != null) {
final String parentStyleName = parentStyleRefValue.getResourceName();
if (parentStyleName != null) {
return new MyStyleRefData(parentStyleName, parentStyleRefValue.getPackage());
return new StyleRefData(parentStyleName, parentStyleRefValue.getPackage());
}
}
else {
@@ -221,7 +211,7 @@ class AndroidInlineUtil {
final int idx = styleName.lastIndexOf('.');
if (idx > 0) {
return new MyStyleRefData(styleName.substring(0, idx), null);
return new StyleRefData(styleName.substring(0, idx), null);
}
}
}
@@ -229,11 +219,11 @@ class AndroidInlineUtil {
}
@Nullable
static MyStyleData getStyleDataFromContext(@Nullable PsiElement context) {
static MyStyleData getInlinableStyleDataFromContext(@Nullable PsiElement context) {
if (context instanceof LazyValueResourceElementWrapper) {
context = ((LazyValueResourceElementWrapper)context).computeElement();
}
if (context == null) {
if (context == null || !context.getManager().isInProject(context)) {
return null;
}
final XmlAttributeValue attrValue = PsiTreeUtil.getParentOfType(context, XmlAttributeValue.class, false);
@@ -258,24 +248,4 @@ class AndroidInlineUtil {
myReferredElement = referredElement;
}
}
static class MyStyleRefData {
private final String myStyleName;
private final String myStylePackage;
MyStyleRefData(@NotNull String styleName, @Nullable String stylePackage) {
myStyleName = styleName;
myStylePackage = stylePackage;
}
}
static class MyStyleUsageData {
private final XmlTag myTag;
private final GenericAttributeValue<ResourceValue> myStyleAttribute;
MyStyleUsageData(XmlTag tag, GenericAttributeValue<ResourceValue> styleAttribute) {
myTag = tag;
myStyleAttribute = styleAttribute;
}
}
}
@@ -0,0 +1,71 @@
package org.jetbrains.android.refactoring;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.containers.HashMap;
import org.jetbrains.android.dom.converters.AndroidResourceReference;
import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.android.dom.resources.Style;
import org.jetbrains.android.dom.resources.StyleItem;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author Eugene.Kudelevsky
*/
class ParentStyleUsageData implements StyleUsageData {
private final AndroidResourceReference myReference;
private final Style myStyle;
public ParentStyleUsageData(@NotNull Style style,
@NotNull AndroidResourceReference reference) {
myReference = reference;
myStyle = style;
}
@Override
public PsiFile getFile() {
final XmlTag tag = myStyle.getXmlTag();
return tag != null ? tag.getContainingFile() : null;
}
@Override
public void inline(@NotNull Map<AndroidAttributeInfo, String> attributeValues, @Nullable StyleRefData parentStyleRef) {
final Map<String, String> id2Value = toId2ValueMap(attributeValues);
for (StyleItem item : myStyle.getItems()) {
final String name = item.getName().getStringValue();
if (name != null) {
id2Value.remove(name);
}
}
for (Map.Entry<String, String> entry : id2Value.entrySet()) {
final StyleItem newItem = myStyle.addItem();
newItem.getName().setStringValue(entry.getKey());
newItem.setStringValue(entry.getValue());
}
myStyle.getParentStyle().setValue(parentStyleRef != null
? ResourceValue.referenceTo((char)0, parentStyleRef.getStylePackage(), null,
parentStyleRef.getStyleName())
: null);
}
private static Map<String, String> toId2ValueMap(Map<AndroidAttributeInfo, String> info2ValueMap) {
final Map<String, String> result = new HashMap<String, String>(info2ValueMap.size());
for (Map.Entry<AndroidAttributeInfo, String> entry : info2ValueMap.entrySet()) {
result.put(entry.getKey().getAttributeId(), entry.getValue());
}
return result;
}
@NotNull
@Override
public AndroidResourceReference getReference() {
return myReference;
}
}
@@ -0,0 +1,27 @@
package org.jetbrains.android.refactoring;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class StyleRefData {
private final String myStyleName;
private final String myStylePackage;
StyleRefData(@NotNull String styleName, @Nullable String stylePackage) {
myStyleName = styleName;
myStylePackage = stylePackage;
}
@NotNull
public String getStyleName() {
return myStyleName;
}
@Nullable
public String getStylePackage() {
return myStylePackage;
}
}
@@ -0,0 +1,21 @@
package org.jetbrains.android.refactoring;
import com.intellij.psi.PsiFile;
import org.jetbrains.android.dom.converters.AndroidResourceReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author Eugene.Kudelevsky
*/
interface StyleUsageData {
@Nullable
PsiFile getFile();
void inline(@NotNull Map<AndroidAttributeInfo, String> attributeValues, @Nullable StyleRefData parentStyleRef);
@NotNull
AndroidResourceReference getReference();
}
@@ -0,0 +1,56 @@
package org.jetbrains.android.refactoring;
import com.android.resources.ResourceType;
import com.android.sdklib.SdkConstants;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.xml.GenericAttributeValue;
import org.jetbrains.android.dom.converters.AndroidResourceReference;
import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author Eugene.Kudelevsky
*/
class ViewStyleUsageData implements StyleUsageData {
private final XmlTag myTag;
private final GenericAttributeValue<ResourceValue> myStyleAttribute;
private final AndroidResourceReference myReference;
ViewStyleUsageData(@NotNull XmlTag tag,
@NotNull GenericAttributeValue<ResourceValue> styleAttribute,
@NotNull AndroidResourceReference reference) {
myTag = tag;
myStyleAttribute = styleAttribute;
myReference = reference;
}
@Nullable
public PsiFile getFile() {
return myTag.getContainingFile();
}
public void inline(@NotNull Map<AndroidAttributeInfo, String> attributeValues, @Nullable StyleRefData parentStyleRef) {
for (Map.Entry<AndroidAttributeInfo, String> entry : attributeValues.entrySet()) {
final AndroidAttributeInfo info = entry.getKey();
final String localName = info.getName();
final String namespace = info.isSystem() ? SdkConstants.NS_RESOURCES : null;
if (myTag.getAttribute(localName, namespace) == null) {
myTag.setAttribute(localName, namespace, entry.getValue());
}
}
myStyleAttribute.setValue(parentStyleRef != null
? ResourceValue.referenceTo('@', parentStyleRef.getStylePackage(), ResourceType.STYLE.getName(),
parentStyleRef.getStyleName())
: null);
}
@NotNull
public AndroidResourceReference getReference() {
return myReference;
}
}
@@ -0,0 +1,15 @@
<resources>
<style name="parentStyle">
<item name="android:textColor">@android:color/black</item>
<item name="android:background">@android:color/white</item>
<item name="android:color">@android:color/red</item>
</style>
<style name="myStyle" parent="par<caret>entStyle">
<item name="android:textColor">@android:color/green</item>
</style>
<style name="myStyle1" parent="parentStyle">
<item name="android:textColor">@android:color/green</item>
</style>
</resources>
@@ -0,0 +1,17 @@
<resources>
<style name="parentStyle">
<item name="android:textColor">@android:color/black</item>
<item name="android:background">@android:color/white</item>
<item name="android:color">@android:color/red</item>
</style>
<style name="myStyle">
<item name="android:textColor">@android:color/green</item>
<item name="android:background">@android:color/white</item>
<item name="android:color">@android:color/red</item>
</style>
<style name="myStyle1" parent="parentStyle">
<item name="android:textColor">@android:color/green</item>
</style>
</resources>
@@ -0,0 +1,15 @@
<resources>
<style name="parentStyle">
<item name="android:textColor">@android:color/black</item>
<item name="android:background">@android:color/white</item>
<item name="android:color">@android:color/red</item>
</style>
<sty<caret>le name="myStyle" parent="parentStyle">
<item name="android:textColor">@android:color/green</item>
</style>
<style name="myStyle1" parent="parentStyle">
<item name="android:textColor">@android:color/green</item>
</style>
</resources>
@@ -0,0 +1,17 @@
<resources>
<style name="parentStyle">
<item name="android:textColor">@android:color/black</item>
<item name="android:background">@android:color/white</item>
<item name="android:color">@android:color/red</item>
</style>
<style name="myStyle">
<item name="android:textColor">@android:color/green</item>
<item name="android:background">@android:color/white</item>
<item name="android:color">@android:color/red</item>
</style>
<style name="myStyle1" parent="parentStyle">
<item name="android:textColor">@android:color/green</item>
</style>
</resources>
@@ -0,0 +1,15 @@
<resources>
<style name="parentStyle" parent="android:Widget.Button">
<item name="android:textColor">@android:color/black</item>
<item name="android:background">@android:color/white</item>
<item name="android:color">@android:color/red</item>
</style>
<sty<caret>le name="myStyle" parent="parentStyle">
<item name="android:textColor">@android:color/green</item>
</style>
<style name="myStyle1" parent="parentStyle">
<item name="android:textColor">@android:color/green</item>
</style>
</resources>
@@ -0,0 +1,14 @@
<resources>
<style name="myStyle" parent="android:Widget.Button">
<item name="android:textColor">@android:color/green</item>
<item name="android:background">@android:color/white</item>
<item name="android:color">@android:color/red</item>
</style>
<style name="myStyle1" parent="android:Widget.Button">
<item name="android:textColor">@android:color/green</item>
<item name="android:background">@android:color/white</item>
<item name="android:color">@android:color/red</item>
</style>
</resources>
@@ -0,0 +1,7 @@
<resources>
<style name="myStyle" parent="android:Widget.But<caret>ton">
<item name="android:textColor">@android:color/black</item>
<item name="android:background">@android:color/white</item>
<item name="android:color">@android:color/red</item>
</style>
</resources>
@@ -111,10 +111,7 @@ public class AndroidInlineStyleTest extends AndroidTestCase {
final VirtualFile f = myFixture
.copyFileToProject(BASE_PATH + testName + ".xml", libModuleDir + "/res/layout/test.xml");
myFixture.configureFromExistingVirtualFile(f);
final Presentation p =
myFixture.testAction(new AndroidInlineStyleReferenceAction(new AndroidInlineTestConfig(false)));
assertTrue(p.isEnabled());
assertTrue(p.isVisible());
doInlineStyleReferenceAction(false, true);
myFixture.checkResultByFile(BASE_PATH + testName + "_after.xml");
myFixture.checkResultByFile(appLayoutPath, BASE_PATH + getTestName(true) + "_1_after.xml", true);
@@ -232,6 +229,37 @@ public class AndroidInlineStyleTest extends AndroidTestCase {
myFixture.checkResultByFile("res/values/styles.xml", BASE_PATH + testName + "_styles_after.xml", true);
}
public void test24() {
final String testName = getTestName(true);
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + testName + ".xml", "res/values/test.xml");
myFixture.configureFromExistingVirtualFile(f);
doCommonInlineAction(true);
myFixture.checkResultByFile(BASE_PATH + testName + "_after.xml");
}
public void test25() {
final String testName = getTestName(true);
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + testName + ".xml", "res/values/test.xml");
myFixture.configureFromExistingVirtualFile(f);
doInlineStyleReferenceAction(true, true);
myFixture.checkResultByFile(BASE_PATH + testName + "_after.xml");
}
public void test26() {
final String testName = getTestName(true);
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + testName + ".xml", "res/values/test.xml");
myFixture.configureFromExistingVirtualFile(f);
doInlineStyleReferenceAction(false, true);
myFixture.checkResultByFile(BASE_PATH + testName + "_after.xml");
}
public void test27() {
final String testName = getTestName(true);
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + testName + ".xml", "res/values/test.xml");
myFixture.configureFromExistingVirtualFile(f);
doInlineStyleReferenceAction(false, false);
}
private void doTest(boolean inlineThisOnly) {
doTest(inlineThisOnly, "res/layout");
}
@@ -242,15 +270,19 @@ public class AndroidInlineStyleTest extends AndroidTestCase {
final VirtualFile f = myFixture
.copyFileToProject(BASE_PATH + testName + ".xml", dirToCopy + "/test" + testName + "_" + Boolean.toString(inlineThisOnly) + ".xml");
myFixture.configureFromExistingVirtualFile(f);
final Presentation p =
myFixture.testAction(new AndroidInlineStyleReferenceAction(new AndroidInlineTestConfig(inlineThisOnly)));
assertTrue(p.isEnabled());
assertTrue(p.isVisible());
doInlineStyleReferenceAction(inlineThisOnly, true);
myFixture.checkResultByFile(BASE_PATH + testName + "_after.xml");
myFixture.checkResultByFile("res/values/styles.xml", BASE_PATH + testName + (
inlineThisOnly ? "_styles.xml" : "_styles_after.xml"), true);
}
private void doInlineStyleReferenceAction(boolean inlineThisOnly, boolean enabled) {
final Presentation p =
myFixture.testAction(new AndroidInlineStyleReferenceAction(new AndroidInlineTestConfig(inlineThisOnly)));
assertEquals(enabled, p.isEnabled());
assertTrue(p.isVisible());
}
private void doTestErrorMessageShown(boolean testInlineThis, boolean testInlineAll, boolean copyStyles, String dirToCopy) {
if (copyStyles) {
myFixture.copyFileToProject(BASE_PATH + getTestName(true) + "_styles.xml",