diff --git a/plugins/android/src/org/jetbrains/android/refactoring/AndroidAttributeInfo.java b/plugins/android/src/org/jetbrains/android/refactoring/AndroidAttributeInfo.java new file mode 100644 index 000000000000..ad649f54877d --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/refactoring/AndroidAttributeInfo.java @@ -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(); + } +} diff --git a/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineAllStyleUsagesProcessor.java b/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineAllStyleUsagesProcessor.java index 430ff441e870..a7f8133fa6e1 100644 --- a/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineAllStyleUsagesProcessor.java +++ b/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineAllStyleUsagesProcessor.java @@ -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 myAttributeValues; - private final AndroidInlineUtil.MyStyleRefData myParentStyleRef; + private final Map 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 attributeValues, - @Nullable AndroidInlineUtil.MyStyleRefData parentStyleRef) { + @NotNull Map 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 inlineInfos = new ArrayList(); + final List inlineInfos = new ArrayList(); final List nonXmlUsages = new ArrayList(); final List unsupportedUsages = new ArrayList(); final List unambiguousUsages = new ArrayList(); @@ -115,19 +110,18 @@ class AndroidInlineAllStyleUsagesProcessor extends BaseRefactoringProcessor { continue; } final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); - final Pair> 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(); } diff --git a/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleDialog.java b/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleDialog.java index 1602098f6ed9..857af7e1f1d7 100644 --- a/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleDialog.java +++ b/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleDialog.java @@ -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 myAttributeValues; - private final AndroidInlineUtil.MyStyleRefData myParentStyleRef; + private final Map 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 attributeValues, - @Nullable AndroidInlineUtil.MyStyleRefData parentStyleRef, + @NotNull Map attributeValues, + @Nullable StyleRefData parentStyleRef, boolean inlineThisByDefault, boolean invokedOnReference) { super(project, true, styleElement); diff --git a/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleHandler.java b/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleHandler.java index c746dbaef1cd..0b59ce9d2d0f 100644 --- a/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleHandler.java +++ b/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleHandler.java @@ -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; } } diff --git a/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleReferenceAction.java b/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleReferenceAction.java index 37c209499164..77f0b91912f3 100644 --- a/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleReferenceAction.java +++ b/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineStyleReferenceAction.java @@ -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> pair = AndroidInlineUtil - .findStyleReference(tag); + final StyleUsageData usageData = AndroidInlineUtil.getUsageData(tag); - if (pair == null) { - return; - } - final AndroidResourceReference reference = pair.getFirst(); - final GenericAttributeValue 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; } } diff --git a/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineUtil.java b/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineUtil.java index dd84de00b825..dda845fda65b 100644 --- a/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineUtil.java +++ b/plugins/android/src/org/jetbrains/android/refactoring/AndroidInlineUtil.java @@ -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> 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 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 attribute) { + if (attribute == null) { return null; } - final GenericAttributeValue 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 attributeValues, - MyStyleRefData parentStyleRef) { - final XmlTag tag = usageData.myTag; - final GenericAttributeValue styleAttribute = usageData.myStyleAttribute; - - for (Map.Entry 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 computeAttributeMap(@NotNull Style style, @NotNull ErrorReporter errorReporter) { - final Map attributeValues = new HashMap(); + static Map computeAttributeMap(@NotNull Style style, @NotNull ErrorReporter errorReporter) { + final Map attributeValues = new HashMap(); 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 attributeValues = computeAttributeMap(style, errorReporter); + final Map 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 myStyleAttribute; - - MyStyleUsageData(XmlTag tag, GenericAttributeValue styleAttribute) { - myTag = tag; - myStyleAttribute = styleAttribute; - } - } } diff --git a/plugins/android/src/org/jetbrains/android/refactoring/ParentStyleUsageData.java b/plugins/android/src/org/jetbrains/android/refactoring/ParentStyleUsageData.java new file mode 100644 index 000000000000..b1df21faaed6 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/refactoring/ParentStyleUsageData.java @@ -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 attributeValues, @Nullable StyleRefData parentStyleRef) { + final Map id2Value = toId2ValueMap(attributeValues); + + for (StyleItem item : myStyle.getItems()) { + final String name = item.getName().getStringValue(); + + if (name != null) { + id2Value.remove(name); + } + } + + for (Map.Entry 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 toId2ValueMap(Map info2ValueMap) { + final Map result = new HashMap(info2ValueMap.size()); + + for (Map.Entry entry : info2ValueMap.entrySet()) { + result.put(entry.getKey().getAttributeId(), entry.getValue()); + } + return result; + } + + @NotNull + @Override + public AndroidResourceReference getReference() { + return myReference; + } +} diff --git a/plugins/android/src/org/jetbrains/android/refactoring/StyleRefData.java b/plugins/android/src/org/jetbrains/android/refactoring/StyleRefData.java new file mode 100644 index 000000000000..c286653836d2 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/refactoring/StyleRefData.java @@ -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; + } +} diff --git a/plugins/android/src/org/jetbrains/android/refactoring/StyleUsageData.java b/plugins/android/src/org/jetbrains/android/refactoring/StyleUsageData.java new file mode 100644 index 000000000000..ca2f6d7b4c91 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/refactoring/StyleUsageData.java @@ -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 attributeValues, @Nullable StyleRefData parentStyleRef); + + @NotNull + AndroidResourceReference getReference(); +} diff --git a/plugins/android/src/org/jetbrains/android/refactoring/ViewStyleUsageData.java b/plugins/android/src/org/jetbrains/android/refactoring/ViewStyleUsageData.java new file mode 100644 index 000000000000..048464fec60e --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/refactoring/ViewStyleUsageData.java @@ -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 myStyleAttribute; + private final AndroidResourceReference myReference; + + ViewStyleUsageData(@NotNull XmlTag tag, + @NotNull GenericAttributeValue styleAttribute, + @NotNull AndroidResourceReference reference) { + myTag = tag; + myStyleAttribute = styleAttribute; + myReference = reference; + } + + @Nullable + public PsiFile getFile() { + return myTag.getContainingFile(); + } + + public void inline(@NotNull Map attributeValues, @Nullable StyleRefData parentStyleRef) { + for (Map.Entry 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; + } +} diff --git a/plugins/android/testData/refactoring/inlineStyle/24.xml b/plugins/android/testData/refactoring/inlineStyle/24.xml new file mode 100644 index 000000000000..63dca10218be --- /dev/null +++ b/plugins/android/testData/refactoring/inlineStyle/24.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/refactoring/inlineStyle/24_after.xml b/plugins/android/testData/refactoring/inlineStyle/24_after.xml new file mode 100644 index 000000000000..a38f47d294e6 --- /dev/null +++ b/plugins/android/testData/refactoring/inlineStyle/24_after.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/refactoring/inlineStyle/25.xml b/plugins/android/testData/refactoring/inlineStyle/25.xml new file mode 100644 index 000000000000..106b6e6e80ca --- /dev/null +++ b/plugins/android/testData/refactoring/inlineStyle/25.xml @@ -0,0 +1,15 @@ + + + + le name="myStyle" parent="parentStyle"> + @android:color/green + + + + \ No newline at end of file diff --git a/plugins/android/testData/refactoring/inlineStyle/25_after.xml b/plugins/android/testData/refactoring/inlineStyle/25_after.xml new file mode 100644 index 000000000000..a38f47d294e6 --- /dev/null +++ b/plugins/android/testData/refactoring/inlineStyle/25_after.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/refactoring/inlineStyle/26.xml b/plugins/android/testData/refactoring/inlineStyle/26.xml new file mode 100644 index 000000000000..aca0b75605ab --- /dev/null +++ b/plugins/android/testData/refactoring/inlineStyle/26.xml @@ -0,0 +1,15 @@ + + + + le name="myStyle" parent="parentStyle"> + @android:color/green + + + + \ No newline at end of file diff --git a/plugins/android/testData/refactoring/inlineStyle/26_after.xml b/plugins/android/testData/refactoring/inlineStyle/26_after.xml new file mode 100644 index 000000000000..f2a1f289b685 --- /dev/null +++ b/plugins/android/testData/refactoring/inlineStyle/26_after.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/refactoring/inlineStyle/27.xml b/plugins/android/testData/refactoring/inlineStyle/27.xml new file mode 100644 index 000000000000..5ed41b5406b7 --- /dev/null +++ b/plugins/android/testData/refactoring/inlineStyle/27.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/plugins/android/testSrc/org/jetbrains/android/refactoring/AndroidInlineStyleTest.java b/plugins/android/testSrc/org/jetbrains/android/refactoring/AndroidInlineStyleTest.java index a3d8866573db..ba096199154b 100644 --- a/plugins/android/testSrc/org/jetbrains/android/refactoring/AndroidInlineStyleTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/refactoring/AndroidInlineStyleTest.java @@ -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",